Rework the setting of information about a file, in particular file resizing, file renaming and the returned error codes in case of error. This rework has implied a rework of the read, write, close and flush functions. To strickly separate what has been actually written to the media (flushed) from what has not been written when a file is open, an "Info" field has been added to the description of a file. The field is used to store the modifications done to the file by the means of SetInfo() like the change of the name or of the size. Such changes are written to the media only when a flush occurs. When a file is open, the information pointed to by the "Info" field is always up-to-date. This is not the case of the information stored in the "HwDescription" of the file description which from now is just a mirror of what is written on the media. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ronald Cron <Ronald.Cron@arm.com> Reviewed-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16511 6f19259b-4bc3-4df7-8a09-765794883524
529 lines
16 KiB
C
529 lines
16 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2012-2014, ARM Limited. All rights reserved.
|
|
*
|
|
* This program and the accompanying materials
|
|
* are licensed and made available under the terms and conditions of the BSD License
|
|
* which accompanies this distribution. The full text of the license may be found at
|
|
* http://opensource.org/licenses/bsd-license.php
|
|
*
|
|
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
*
|
|
**/
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
#include <Protocol/DevicePathFromText.h>
|
|
#include <Protocol/DriverBinding.h>
|
|
|
|
#include "BootMonFsInternal.h"
|
|
|
|
EFI_DEVICE_PATH* mBootMonFsSupportedDevicePaths;
|
|
LIST_ENTRY mInstances;
|
|
|
|
EFI_FILE_PROTOCOL mBootMonFsRootTemplate = {
|
|
EFI_FILE_PROTOCOL_REVISION,
|
|
BootMonFsOpenFile,
|
|
BootMonFsCloseFile,
|
|
BootMonFsDeleteFail,
|
|
BootMonFsReadDirectory,
|
|
BootMonFsWriteFile,
|
|
BootMonFsGetPositionUnsupported, // UEFI Spec: GetPosition not valid on dirs
|
|
BootMonFsSetDirPosition,
|
|
BootMonFsGetInfo,
|
|
BootMonFsSetInfo,
|
|
BootMonFsFlushDirectory
|
|
};
|
|
|
|
EFI_FILE_PROTOCOL mBootMonFsFileTemplate = {
|
|
EFI_FILE_PROTOCOL_REVISION,
|
|
BootMonFsOpenFile,
|
|
BootMonFsCloseFile,
|
|
BootMonFsDelete,
|
|
BootMonFsReadFile,
|
|
BootMonFsWriteFile,
|
|
BootMonFsGetPosition,
|
|
BootMonFsSetPosition,
|
|
BootMonFsGetInfo,
|
|
BootMonFsSetInfo,
|
|
BootMonFsFlushFile
|
|
};
|
|
|
|
/**
|
|
Search for a file given its name coded in Ascii.
|
|
|
|
When searching through the files of the volume, if a file is currently not
|
|
open, its name was written on the media and is kept in RAM in the
|
|
"HwDescription.Footer.Filename[]" field of the file's description.
|
|
|
|
If a file is currently open, its name might not have been written on the
|
|
media yet, and as the "HwDescription" is a mirror in RAM of what is on the
|
|
media the "HwDescription.Footer.Filename[]" might be outdated. In that case,
|
|
the up to date name of the file is stored in the "Info" field of the file's
|
|
description.
|
|
|
|
@param[in] Instance Pointer to the description of the volume in which
|
|
the file has to be search for.
|
|
@param[in] AsciiFileName Name of the file.
|
|
|
|
@param[out] File Pointer to the description of the file if the
|
|
file was found.
|
|
|
|
@retval EFI_SUCCESS The file was found.
|
|
@retval EFI_NOT_FOUND The file was not found.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
BootMonGetFileFromAsciiFileName (
|
|
IN BOOTMON_FS_INSTANCE *Instance,
|
|
IN CHAR8* AsciiFileName,
|
|
OUT BOOTMON_FS_FILE **File
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
BOOTMON_FS_FILE *FileEntry;
|
|
CHAR8 OpenFileAsciiFileName[MAX_NAME_LENGTH];
|
|
CHAR8 *AsciiFileNameToCompare;
|
|
|
|
// Go through all the files in the list and return the file handle
|
|
for (Entry = GetFirstNode (&Instance->RootFile->Link);
|
|
!IsNull (&Instance->RootFile->Link, Entry);
|
|
Entry = GetNextNode (&Instance->RootFile->Link, Entry)
|
|
)
|
|
{
|
|
FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (Entry);
|
|
if (FileEntry->Info != NULL) {
|
|
UnicodeStrToAsciiStr (FileEntry->Info->FileName, OpenFileAsciiFileName);
|
|
AsciiFileNameToCompare = OpenFileAsciiFileName;
|
|
} else {
|
|
AsciiFileNameToCompare = FileEntry->HwDescription.Footer.Filename;
|
|
}
|
|
|
|
if (AsciiStrCmp (AsciiFileNameToCompare, AsciiFileName) == 0) {
|
|
*File = FileEntry;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
EFI_STATUS
|
|
BootMonGetFileFromPosition (
|
|
IN BOOTMON_FS_INSTANCE *Instance,
|
|
IN UINTN Position,
|
|
OUT BOOTMON_FS_FILE **File
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
BOOTMON_FS_FILE *FileEntry;
|
|
|
|
// Go through all the files in the list and return the file handle
|
|
for (Entry = GetFirstNode (&Instance->RootFile->Link);
|
|
!IsNull (&Instance->RootFile->Link, Entry) && (&Instance->RootFile->Link != Entry);
|
|
Entry = GetNextNode (&Instance->RootFile->Link, Entry)
|
|
)
|
|
{
|
|
if (Position == 0) {
|
|
FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (Entry);
|
|
*File = FileEntry;
|
|
return EFI_SUCCESS;
|
|
}
|
|
Position--;
|
|
}
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
EFI_STATUS
|
|
BootMonFsCreateFile (
|
|
IN BOOTMON_FS_INSTANCE *Instance,
|
|
OUT BOOTMON_FS_FILE **File
|
|
)
|
|
{
|
|
BOOTMON_FS_FILE *NewFile;
|
|
|
|
NewFile = (BOOTMON_FS_FILE*)AllocateZeroPool (sizeof (BOOTMON_FS_FILE));
|
|
if (NewFile == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
NewFile->Signature = BOOTMON_FS_FILE_SIGNATURE;
|
|
InitializeListHead (&NewFile->Link);
|
|
InitializeListHead (&NewFile->RegionToFlushLink);
|
|
NewFile->Instance = Instance;
|
|
|
|
// If the created file is the root file then create a directory EFI_FILE_PROTOCOL
|
|
if (Instance->RootFile == *File) {
|
|
CopyMem (&NewFile->File, &mBootMonFsRootTemplate, sizeof (mBootMonFsRootTemplate));
|
|
} else {
|
|
CopyMem (&NewFile->File, &mBootMonFsFileTemplate, sizeof (mBootMonFsFileTemplate));
|
|
}
|
|
*File = NewFile;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
SupportedDevicePathsInit (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16* DevicePathListStr;
|
|
CHAR16* DevicePathStr;
|
|
CHAR16* NextDevicePathStr;
|
|
EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
|
|
EFI_DEVICE_PATH_PROTOCOL *Instance;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
// Initialize Variable
|
|
DevicePathListStr = (CHAR16*)PcdGetPtr (PcdBootMonFsSupportedDevicePaths);
|
|
mBootMonFsSupportedDevicePaths = NULL;
|
|
|
|
// Extract the Device Path instances from the multi-device path string
|
|
while ((DevicePathListStr != NULL) && (DevicePathListStr[0] != L'\0')) {
|
|
NextDevicePathStr = StrStr (DevicePathListStr, L";");
|
|
if (NextDevicePathStr == NULL) {
|
|
DevicePathStr = DevicePathListStr;
|
|
DevicePathListStr = NULL;
|
|
} else {
|
|
DevicePathStr = (CHAR16*)AllocateCopyPool ((NextDevicePathStr - DevicePathListStr + 1) * sizeof (CHAR16), DevicePathListStr);
|
|
if (DevicePathStr == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
*(DevicePathStr + (NextDevicePathStr - DevicePathListStr)) = L'\0';
|
|
DevicePathListStr = NextDevicePathStr;
|
|
if (DevicePathListStr[0] == L';') {
|
|
DevicePathListStr++;
|
|
}
|
|
}
|
|
|
|
Instance = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (DevicePathStr);
|
|
ASSERT (Instance != NULL);
|
|
mBootMonFsSupportedDevicePaths = AppendDevicePathInstance (mBootMonFsSupportedDevicePaths, Instance);
|
|
|
|
if (NextDevicePathStr != NULL) {
|
|
FreePool (DevicePathStr);
|
|
}
|
|
FreePool (Instance);
|
|
}
|
|
|
|
if (mBootMonFsSupportedDevicePaths == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
} else {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootMonFsDriverSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL
|
|
)
|
|
{
|
|
EFI_DISK_IO_PROTOCOL *DiskIo;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePathProtocol;
|
|
EFI_DEVICE_PATH_PROTOCOL *SupportedDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *SupportedDevicePaths;
|
|
EFI_STATUS Status;
|
|
UINTN Size1;
|
|
UINTN Size2;
|
|
|
|
//
|
|
// Open the IO Abstraction(s) needed to perform the supported test
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiDiskIoProtocolGuid,
|
|
(VOID **) &DiskIo,
|
|
gImageHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Close the I/O Abstraction(s) used to perform the supported test
|
|
//
|
|
gBS->CloseProtocol (
|
|
ControllerHandle,
|
|
&gEfiDiskIoProtocolGuid,
|
|
gImageHandle,
|
|
ControllerHandle
|
|
);
|
|
|
|
// Check that BlockIo protocol instance exists
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiBlockIoProtocolGuid,
|
|
NULL,
|
|
gImageHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Check if a DevicePath is attached to the handle
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **)&DevicePathProtocol,
|
|
gImageHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Check if the Device Path is the one which contains the Boot Monitor File System
|
|
Size1 = GetDevicePathSize (DevicePathProtocol);
|
|
|
|
// Go through the list of Device Path Instances
|
|
Status = EFI_UNSUPPORTED;
|
|
SupportedDevicePaths = mBootMonFsSupportedDevicePaths;
|
|
while (SupportedDevicePaths != NULL) {
|
|
SupportedDevicePath = GetNextDevicePathInstance (&SupportedDevicePaths, &Size2);
|
|
|
|
if ((Size1 == Size2) && (CompareMem (DevicePathProtocol, SupportedDevicePath, Size1) == 0)) {
|
|
// The Device Path is supported
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gBS->CloseProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid, gImageHandle, ControllerHandle);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootMonFsDriverStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL
|
|
)
|
|
{
|
|
BOOTMON_FS_INSTANCE *Instance;
|
|
EFI_STATUS Status;
|
|
UINTN VolumeNameSize;
|
|
EFI_FILE_INFO *Info;
|
|
|
|
Instance = AllocateZeroPool (sizeof (BOOTMON_FS_INSTANCE));
|
|
if (Instance == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
// Initialize the BlockIo of the Instance
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiBlockIoProtocolGuid,
|
|
(VOID **)&(Instance->BlockIo),
|
|
gImageHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiDiskIoProtocolGuid,
|
|
(VOID **)&(Instance->DiskIo),
|
|
gImageHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Initialize the attributes of the Instance
|
|
//
|
|
Instance->Signature = BOOTMON_FS_SIGNATURE;
|
|
Instance->ControllerHandle = ControllerHandle;
|
|
Instance->Media = Instance->BlockIo->Media;
|
|
Instance->Binding = DriverBinding;
|
|
|
|
// Initialize the Simple File System Protocol
|
|
Instance->Fs.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
|
|
Instance->Fs.OpenVolume = OpenBootMonFsOpenVolume;
|
|
|
|
// Volume name + L' ' + '2' digit number
|
|
VolumeNameSize = StrSize (BOOTMON_FS_VOLUME_LABEL) + (3 * sizeof (CHAR16));
|
|
|
|
// Initialize FileSystem Information
|
|
Instance->FsInfo.Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + VolumeNameSize;
|
|
Instance->FsInfo.BlockSize = Instance->Media->BlockSize;
|
|
Instance->FsInfo.ReadOnly = FALSE;
|
|
Instance->FsInfo.VolumeSize =
|
|
Instance->Media->BlockSize * (Instance->Media->LastBlock - Instance->Media->LowestAlignedLba);
|
|
CopyMem (Instance->FsInfo.VolumeLabel, BOOTMON_FS_VOLUME_LABEL, StrSize (BOOTMON_FS_VOLUME_LABEL));
|
|
|
|
// Initialize the root file
|
|
Status = BootMonFsCreateFile (Instance, &Instance->RootFile);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
Info = AllocateZeroPool (sizeof (EFI_FILE_INFO));
|
|
if (Info == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Error;
|
|
}
|
|
Instance->RootFile->Info = Info;
|
|
|
|
// Initialize the DevicePath of the Instance
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **)&(Instance->DevicePath),
|
|
gImageHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Install the Simple File System Protocol
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ControllerHandle,
|
|
&gEfiSimpleFileSystemProtocolGuid, &Instance->Fs,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
InsertTailList (&mInstances, &Instance->Link);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
Error:
|
|
|
|
if (Instance->RootFile != NULL) {
|
|
if (Instance->RootFile->Info != NULL) {
|
|
FreePool (Instance->RootFile->Info);
|
|
}
|
|
FreePool (Instance->RootFile);
|
|
}
|
|
FreePool (Instance);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootMonFsDriverStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
|
|
)
|
|
{
|
|
BOOTMON_FS_INSTANCE *Instance;
|
|
LIST_ENTRY *Link;
|
|
EFI_STATUS Status;
|
|
BOOLEAN InstanceFound;
|
|
|
|
// Find instance from ControllerHandle.
|
|
Instance = NULL;
|
|
InstanceFound = FALSE;
|
|
// For each instance in mInstances:
|
|
for (Link = GetFirstNode (&mInstances); !IsNull (&mInstances, Link); Link = GetNextNode (&mInstances, Link)) {
|
|
Instance = BOOTMON_FS_FROM_LINK (Link);
|
|
|
|
if (Instance->ControllerHandle == ControllerHandle) {
|
|
InstanceFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT (InstanceFound == TRUE);
|
|
|
|
gBS->CloseProtocol (
|
|
ControllerHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
DriverBinding->ImageHandle,
|
|
ControllerHandle);
|
|
|
|
gBS->CloseProtocol (
|
|
ControllerHandle,
|
|
&gEfiDiskIoProtocolGuid,
|
|
DriverBinding->ImageHandle,
|
|
ControllerHandle);
|
|
|
|
gBS->CloseProtocol (
|
|
ControllerHandle,
|
|
&gEfiBlockIoProtocolGuid,
|
|
DriverBinding->ImageHandle,
|
|
ControllerHandle);
|
|
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
&ControllerHandle,
|
|
&gEfiSimpleFileSystemProtocolGuid, &Instance->Fs,
|
|
NULL);
|
|
|
|
FreePool (Instance->RootFile->Info);
|
|
FreePool (Instance->RootFile);
|
|
FreePool (Instance);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Simple Network Protocol Driver Global Variables
|
|
//
|
|
EFI_DRIVER_BINDING_PROTOCOL mBootMonFsDriverBinding = {
|
|
BootMonFsDriverSupported,
|
|
BootMonFsDriverStart,
|
|
BootMonFsDriverStop,
|
|
0xa,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootMonFsEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
InitializeListHead (&mInstances);
|
|
|
|
// Initialize the list of Device Paths that could support BootMonFs
|
|
Status = SupportedDevicePathsInit ();
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ImageHandle,
|
|
&gEfiDriverBindingProtocolGuid, &mBootMonFsDriverBinding,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
} else {
|
|
DEBUG((EFI_D_ERROR,"Warning: No Device Paths supporting BootMonFs have been defined in the PCD.\n"));
|
|
}
|
|
|
|
return Status;
|
|
}
|