mirror of https://github.com/acidanthera/audk.git
794 lines
25 KiB
C
794 lines
25 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/PathLib.h>
|
|
#include "BootMonFsInternal.h"
|
|
|
|
// Clear a file's image description on storage media:
|
|
// UEFI allows you to seek past the end of a file, a subsequent write will grow
|
|
// the file. It does not specify how space between the former end of the file
|
|
// and the beginning of the write should be filled. It's therefore possible that
|
|
// BootMonFs metadata, that comes after the end of a file, could be left there
|
|
// and wrongly detected by BootMonFsImageInBlock.
|
|
STATIC
|
|
EFI_STATUS
|
|
InvalidateImageDescription (
|
|
IN BOOTMON_FS_FILE *File
|
|
)
|
|
{
|
|
EFI_DISK_IO_PROTOCOL *DiskIo;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
UINT32 MediaId;
|
|
VOID *Buffer;
|
|
EFI_STATUS Status;
|
|
|
|
DiskIo = File->Instance->DiskIo;
|
|
BlockIo = File->Instance->BlockIo;
|
|
MediaId = BlockIo->Media->MediaId;
|
|
|
|
Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION));
|
|
|
|
if (Buffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = DiskIo->WriteDisk (DiskIo,
|
|
MediaId,
|
|
File->HwDescAddress,
|
|
sizeof (HW_IMAGE_DESCRIPTION),
|
|
Buffer
|
|
);
|
|
|
|
FreePool(Buffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Write the description of a file to storage media.
|
|
|
|
This function uses DiskIo to write to the media, so call BlockIo->FlushBlocks()
|
|
after calling it to ensure the data are written on the media.
|
|
|
|
@param[in] File Description of the file whose description on the
|
|
storage media has to be updated.
|
|
@param[in] FileName Name of the file. Its length is assumed to be
|
|
lower than MAX_NAME_LENGTH.
|
|
@param[in] DataSize Number of data bytes of the file.
|
|
@param[in] FileStart File's starting position on media. FileStart must
|
|
be aligned to the media's block size.
|
|
|
|
@retval EFI_WRITE_PROTECTED The device cannot be written to.
|
|
@retval EFI_DEVICE_ERROR The device reported an error while performing
|
|
the write operation.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
WriteFileDescription (
|
|
IN BOOTMON_FS_FILE *File,
|
|
IN CHAR8 *FileName,
|
|
IN UINT32 DataSize,
|
|
IN UINT64 FileStart
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DISK_IO_PROTOCOL *DiskIo;
|
|
UINTN BlockSize;
|
|
UINT32 FileSize;
|
|
HW_IMAGE_DESCRIPTION *Description;
|
|
|
|
DiskIo = File->Instance->DiskIo;
|
|
BlockSize = File->Instance->BlockIo->Media->BlockSize;
|
|
ASSERT (FileStart % BlockSize == 0);
|
|
|
|
//
|
|
// Construct the file description
|
|
//
|
|
|
|
FileSize = DataSize + sizeof (HW_IMAGE_DESCRIPTION);
|
|
Description = &File->HwDescription;
|
|
Description->Attributes = 1;
|
|
Description->BlockStart = FileStart / BlockSize;
|
|
Description->BlockEnd = Description->BlockStart + (FileSize / BlockSize);
|
|
AsciiStrCpy (Description->Footer.Filename, FileName);
|
|
|
|
#ifdef MDE_CPU_ARM
|
|
Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET;
|
|
Description->Footer.Version = HW_IMAGE_FOOTER_VERSION;
|
|
#else
|
|
Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET2;
|
|
Description->Footer.Version = HW_IMAGE_FOOTER_VERSION2;
|
|
#endif
|
|
Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1;
|
|
Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2;
|
|
Description->RegionCount = 1;
|
|
Description->Region[0].Checksum = 0;
|
|
Description->Region[0].Offset = Description->BlockStart * BlockSize;
|
|
Description->Region[0].Size = DataSize;
|
|
|
|
Status = BootMonFsComputeFooterChecksum (Description);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
File->HwDescAddress = ((Description->BlockEnd + 1) * BlockSize) - sizeof (HW_IMAGE_DESCRIPTION);
|
|
|
|
// Update the file description on the media
|
|
Status = DiskIo->WriteDisk (
|
|
DiskIo,
|
|
File->Instance->Media->MediaId,
|
|
File->HwDescAddress,
|
|
sizeof (HW_IMAGE_DESCRIPTION),
|
|
Description
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
// Find a space on media for a file that has not yet been flushed to disk.
|
|
// Just returns the first space that's big enough.
|
|
// This function could easily be adapted to:
|
|
// - Find space for moving an existing file that has outgrown its space
|
|
// (We do not currently move files, just return EFI_VOLUME_FULL)
|
|
// - Find space for a fragment of a file that has outgrown its space
|
|
// (We do not currently fragment files - it's not clear whether fragmentation
|
|
// is actually part of BootMonFs as there is no spec)
|
|
// - Be more clever about finding space (choosing the largest or smallest
|
|
// suitable space)
|
|
// Parameters:
|
|
// File - the new (not yet flushed) file for which we need to find space.
|
|
// FileStart - the position on media of the file (in bytes).
|
|
STATIC
|
|
EFI_STATUS
|
|
BootMonFsFindSpaceForNewFile (
|
|
IN BOOTMON_FS_FILE *File,
|
|
IN UINT64 FileSize,
|
|
OUT UINT64 *FileStart
|
|
)
|
|
{
|
|
LIST_ENTRY *FileLink;
|
|
BOOTMON_FS_FILE *RootFile;
|
|
BOOTMON_FS_FILE *FileEntry;
|
|
UINTN BlockSize;
|
|
EFI_BLOCK_IO_MEDIA *Media;
|
|
|
|
Media = File->Instance->BlockIo->Media;
|
|
BlockSize = Media->BlockSize;
|
|
RootFile = File->Instance->RootFile;
|
|
|
|
// This function must only be called for file which has not been flushed into
|
|
// Flash yet
|
|
ASSERT (File->HwDescription.RegionCount == 0);
|
|
|
|
*FileStart = 0;
|
|
// Go through all the files in the list
|
|
for (FileLink = GetFirstNode (&RootFile->Link);
|
|
!IsNull (&RootFile->Link, FileLink);
|
|
FileLink = GetNextNode (&RootFile->Link, FileLink)
|
|
)
|
|
{
|
|
FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
|
|
// Skip files that aren't on disk yet
|
|
if (FileEntry->HwDescription.RegionCount == 0) {
|
|
continue;
|
|
}
|
|
|
|
// If the free space preceding the file is big enough to contain the new
|
|
// file then use it!
|
|
if (((FileEntry->HwDescription.BlockStart * BlockSize) - *FileStart)
|
|
>= FileSize) {
|
|
// The file list must be in disk-order
|
|
RemoveEntryList (&File->Link);
|
|
File->Link.BackLink = FileLink->BackLink;
|
|
File->Link.ForwardLink = FileLink;
|
|
FileLink->BackLink->ForwardLink = &File->Link;
|
|
FileLink->BackLink = &File->Link;
|
|
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
*FileStart = (FileEntry->HwDescription.BlockEnd + 1) * BlockSize;
|
|
}
|
|
}
|
|
// See if there's space after the last file
|
|
if ((((Media->LastBlock + 1) * BlockSize) - *FileStart) >= FileSize) {
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_VOLUME_FULL;
|
|
}
|
|
}
|
|
|
|
// Free the resources in the file's Region list.
|
|
STATIC
|
|
VOID
|
|
FreeFileRegions (
|
|
IN BOOTMON_FS_FILE *File
|
|
)
|
|
{
|
|
LIST_ENTRY *RegionToFlushLink;
|
|
BOOTMON_FS_FILE_REGION *Region;
|
|
|
|
RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
|
|
while (!IsNull (&File->RegionToFlushLink, RegionToFlushLink)) {
|
|
// Repeatedly remove the first node from the list and free its resources.
|
|
Region = (BOOTMON_FS_FILE_REGION *) RegionToFlushLink;
|
|
RemoveEntryList (RegionToFlushLink);
|
|
FreePool (Region->Buffer);
|
|
FreePool (Region);
|
|
|
|
RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Flush all modified data associated with a file to a device.
|
|
|
|
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the
|
|
file handle to flush.
|
|
|
|
@retval EFI_SUCCESS The data was flushed.
|
|
@retval EFI_ACCESS_DENIED The file was opened read-only.
|
|
@retval EFI_DEVICE_ERROR The device reported an error.
|
|
@retval EFI_VOLUME_FULL The volume is full.
|
|
@retval EFI_OUT_OF_RESOURCES Not enough resources were available to flush the data.
|
|
@retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.
|
|
|
|
**/
|
|
EFIAPI
|
|
EFI_STATUS
|
|
BootMonFsFlushFile (
|
|
IN EFI_FILE_PROTOCOL *This
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOTMON_FS_INSTANCE *Instance;
|
|
EFI_FILE_INFO *Info;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
EFI_BLOCK_IO_MEDIA *Media;
|
|
EFI_DISK_IO_PROTOCOL *DiskIo;
|
|
UINTN BlockSize;
|
|
CHAR8 AsciiFileName[MAX_NAME_LENGTH];
|
|
LIST_ENTRY *RegionToFlushLink;
|
|
BOOTMON_FS_FILE *File;
|
|
BOOTMON_FS_FILE *NextFile;
|
|
BOOTMON_FS_FILE_REGION *Region;
|
|
LIST_ENTRY *FileLink;
|
|
UINTN CurrentPhysicalSize;
|
|
UINT64 FileStart;
|
|
UINT64 FileEnd;
|
|
UINT64 RegionStart;
|
|
UINT64 RegionEnd;
|
|
UINT64 NewDataSize;
|
|
UINT64 NewFileSize;
|
|
UINT64 EndOfAppendSpace;
|
|
BOOLEAN HasSpace;
|
|
|
|
if (This == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
|
|
if (File->Info == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (File->OpenMode == EFI_FILE_MODE_READ) {
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
Instance = File->Instance;
|
|
Info = File->Info;
|
|
BlockIo = Instance->BlockIo;
|
|
Media = BlockIo->Media;
|
|
DiskIo = Instance->DiskIo;
|
|
BlockSize = Media->BlockSize;
|
|
|
|
UnicodeStrToAsciiStr (Info->FileName, AsciiFileName);
|
|
|
|
// If the file doesn't exist then find a space for it
|
|
if (File->HwDescription.RegionCount == 0) {
|
|
Status = BootMonFsFindSpaceForNewFile (
|
|
File,
|
|
Info->FileSize + sizeof (HW_IMAGE_DESCRIPTION),
|
|
&FileStart
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
} else {
|
|
FileStart = File->HwDescription.BlockStart * BlockSize;
|
|
}
|
|
// FileEnd is the current NOR address of the end of the file's data
|
|
FileEnd = FileStart + File->HwDescription.Region[0].Size;
|
|
|
|
for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
|
|
!IsNull (&File->RegionToFlushLink, RegionToFlushLink);
|
|
RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)
|
|
)
|
|
{
|
|
Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
|
|
if (Region->Size == 0) {
|
|
continue;
|
|
}
|
|
|
|
// RegionStart and RegionEnd are the the intended NOR address of the
|
|
// start and end of the region
|
|
RegionStart = FileStart + Region->Offset;
|
|
RegionEnd = RegionStart + Region->Size;
|
|
|
|
if (RegionEnd < FileEnd) {
|
|
// Handle regions representing edits to existing portions of the file
|
|
// Write the region data straight into the file
|
|
Status = DiskIo->WriteDisk (DiskIo,
|
|
Media->MediaId,
|
|
RegionStart,
|
|
Region->Size,
|
|
Region->Buffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
} else {
|
|
// Handle regions representing appends to the file
|
|
//
|
|
// Note: Since seeking past the end of the file with SetPosition() is
|
|
// valid, it's possible there will be a gap between the current end of
|
|
// the file and the beginning of the new region. Since the UEFI spec
|
|
// says nothing about this case (except "a subsequent write would grow
|
|
// the file"), we just leave garbage in the gap.
|
|
|
|
// Check if there is space to append the new region
|
|
HasSpace = FALSE;
|
|
NewDataSize = RegionEnd - FileStart;
|
|
NewFileSize = NewDataSize + sizeof (HW_IMAGE_DESCRIPTION);
|
|
CurrentPhysicalSize = BootMonFsGetPhysicalSize (File);
|
|
if (NewFileSize <= CurrentPhysicalSize) {
|
|
HasSpace = TRUE;
|
|
} else {
|
|
// Get the File Description for the next file
|
|
FileLink = GetNextNode (&Instance->RootFile->Link, &File->Link);
|
|
if (!IsNull (&Instance->RootFile->Link, FileLink)) {
|
|
NextFile = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
|
|
|
|
// If there is space between the beginning of the current file and the
|
|
// beginning of the next file then use it
|
|
EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize;
|
|
} else {
|
|
// We are flushing the last file.
|
|
EndOfAppendSpace = (Media->LastBlock + 1) * BlockSize;
|
|
}
|
|
if (EndOfAppendSpace - FileStart >= NewFileSize) {
|
|
HasSpace = TRUE;
|
|
}
|
|
}
|
|
|
|
if (HasSpace == TRUE) {
|
|
// Invalidate the current image description of the file if any.
|
|
if (File->HwDescAddress != 0) {
|
|
Status = InvalidateImageDescription (File);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// Write the new file data
|
|
Status = DiskIo->WriteDisk (
|
|
DiskIo,
|
|
Media->MediaId,
|
|
RegionStart,
|
|
Region->Size,
|
|
Region->Buffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = WriteFileDescription (File, AsciiFileName, NewDataSize, FileStart);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
} else {
|
|
// There isn't a space for the file.
|
|
// Options here are to move the file or fragment it. However as files
|
|
// may represent boot images at fixed positions, these options will
|
|
// break booting if the bootloader doesn't use BootMonFs to find the
|
|
// image.
|
|
|
|
return EFI_VOLUME_FULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeFileRegions (File);
|
|
Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
|
|
|
|
if ((AsciiStrCmp (AsciiFileName, File->HwDescription.Footer.Filename) != 0) ||
|
|
(Info->FileSize != File->HwDescription.Region[0].Size) ) {
|
|
Status = WriteFileDescription (File, AsciiFileName, Info->FileSize, FileStart);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by
|
|
// calling FlushBlocks on the same device's BlockIo).
|
|
BlockIo->FlushBlocks (BlockIo);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Close a specified file handle.
|
|
|
|
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
|
|
handle to close.
|
|
|
|
@retval EFI_SUCCESS The file was closed.
|
|
@retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open
|
|
file handle.
|
|
|
|
**/
|
|
EFIAPI
|
|
EFI_STATUS
|
|
BootMonFsCloseFile (
|
|
IN EFI_FILE_PROTOCOL *This
|
|
)
|
|
{
|
|
BOOTMON_FS_FILE *File;
|
|
|
|
if (This == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
|
|
if (File->Info == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// In the case of a file and not the root directory
|
|
if (This != &File->Instance->RootFile->File) {
|
|
This->Flush (This);
|
|
FreePool (File->Info);
|
|
File->Info = NULL;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Open a file on the boot monitor file system.
|
|
|
|
The boot monitor file system does not allow for sub-directories. There is only
|
|
one directory, the root one. On any attempt to create a directory, the function
|
|
returns in error with the EFI_WRITE_PROTECTED error code.
|
|
|
|
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is
|
|
the file handle to source location.
|
|
@param[out] NewHandle A pointer to the location to return the opened
|
|
handle for the new file.
|
|
@param[in] FileName The Null-terminated string of the name of the file
|
|
to be opened.
|
|
@param[in] OpenMode The mode to open the file : Read or Read/Write or
|
|
Read/Write/Create
|
|
@param[in] Attributes Attributes of the file in case of a file creation
|
|
|
|
@retval EFI_SUCCESS The file was open.
|
|
@retval EFI_NOT_FOUND The specified file could not be found or the specified
|
|
directory in which to create a file could not be found.
|
|
@retval EFI_DEVICE_ERROR The device reported an error.
|
|
@retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible
|
|
with the Boot Monitor file system.
|
|
@retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
|
|
@retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.
|
|
|
|
**/
|
|
EFIAPI
|
|
EFI_STATUS
|
|
BootMonFsOpenFile (
|
|
IN EFI_FILE_PROTOCOL *This,
|
|
OUT EFI_FILE_PROTOCOL **NewHandle,
|
|
IN CHAR16 *FileName,
|
|
IN UINT64 OpenMode,
|
|
IN UINT64 Attributes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOTMON_FS_FILE *Directory;
|
|
BOOTMON_FS_FILE *File;
|
|
BOOTMON_FS_INSTANCE *Instance;
|
|
CHAR8 *Buf;
|
|
CHAR16 *Path;
|
|
CHAR16 *Separator;
|
|
CHAR8 *AsciiFileName;
|
|
EFI_FILE_INFO *Info;
|
|
|
|
if (This == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
|
|
if (Directory->Info == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((FileName == NULL) || (NewHandle == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// The only valid modes are read, read/write, and read/write/create
|
|
//
|
|
if ( (OpenMode != EFI_FILE_MODE_READ) &&
|
|
(OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) &&
|
|
(OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Instance = Directory->Instance;
|
|
|
|
//
|
|
// If the instance has not been initialized yet then do it ...
|
|
//
|
|
if (!Instance->Initialized) {
|
|
Status = BootMonFsInitialize (Instance);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the file path to be able to work on it. We do not want to
|
|
// modify the input file name string "FileName".
|
|
//
|
|
Buf = AllocateCopyPool (StrSize (FileName), FileName);
|
|
if (Buf == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
Path = (CHAR16*)Buf;
|
|
AsciiFileName = NULL;
|
|
Info = NULL;
|
|
|
|
//
|
|
// Handle single periods, double periods and convert forward slashes '/'
|
|
// to backward '\' ones. Does not handle a '.' at the beginning of the
|
|
// path for the time being.
|
|
//
|
|
if (PathCleanUpDirectories (Path) == NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Detect if the first component of the path refers to a directory.
|
|
// This is done to return the correct error code when trying to
|
|
// access or create a directory other than the root directory.
|
|
//
|
|
|
|
//
|
|
// Search for the '\\' sequence and if found return in error
|
|
// with the EFI_INVALID_PARAMETER error code. ere in the path.
|
|
//
|
|
if (StrStr (Path, L"\\\\") != NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto Error;
|
|
}
|
|
//
|
|
// Get rid of the leading '\' if any.
|
|
//
|
|
Path += (Path[0] == L'\\');
|
|
|
|
//
|
|
// Look for a '\' in the file path. If one is found then
|
|
// the first component of the path refers to a directory
|
|
// that is not the root directory.
|
|
//
|
|
Separator = StrStr (Path, L"\\");
|
|
if (Separator != NULL) {
|
|
//
|
|
// In the case '<dir name>\' and a creation, return
|
|
// EFI_WRITE_PROTECTED if this is for a directory
|
|
// creation, EFI_INVALID_PARAMETER otherwise.
|
|
//
|
|
if ((*(Separator + 1) == '\0') && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
|
|
if (Attributes & EFI_FILE_DIRECTORY) {
|
|
Status = EFI_WRITE_PROTECTED;
|
|
} else {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
//
|
|
// Attempt to open a file or a directory that is not in the
|
|
// root directory or to open without creation a directory
|
|
// located in the root directory, returns EFI_NOT_FOUND.
|
|
//
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// BootMonFs interface requires ASCII filenames
|
|
//
|
|
AsciiFileName = AllocatePool (StrLen (Path) + 1);
|
|
if (AsciiFileName == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Error;
|
|
}
|
|
UnicodeStrToAsciiStr (Path, AsciiFileName);
|
|
if (AsciiStrSize (AsciiFileName) > MAX_NAME_LENGTH) {
|
|
AsciiFileName[MAX_NAME_LENGTH - 1] = '\0';
|
|
}
|
|
|
|
if ((AsciiFileName[0] == '\0') ||
|
|
(AsciiFileName[0] == '.' ) ) {
|
|
//
|
|
// Opening the root directory
|
|
//
|
|
|
|
*NewHandle = &Instance->RootFile->File;
|
|
Instance->RootFile->Position = 0;
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
|
|
if ((OpenMode & EFI_FILE_MODE_CREATE) &&
|
|
(Attributes & EFI_FILE_DIRECTORY) ) {
|
|
Status = EFI_WRITE_PROTECTED;
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to store the characteristics of the file while the
|
|
// file is open. We allocate the maximum size to not have to reallocate
|
|
// if the file name is changed.
|
|
//
|
|
Info = AllocateZeroPool (
|
|
SIZE_OF_EFI_FILE_INFO + (sizeof (CHAR16) * MAX_NAME_LENGTH));
|
|
if (Info == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Open or create a file in the root directory.
|
|
//
|
|
|
|
Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
if ((OpenMode & EFI_FILE_MODE_CREATE) == 0) {
|
|
goto Error;
|
|
}
|
|
|
|
Status = BootMonFsCreateFile (Instance, &File);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
InsertHeadList (&Instance->RootFile->Link, &File->Link);
|
|
Info->Attribute = Attributes;
|
|
} else {
|
|
//
|
|
// File already open, not supported yet.
|
|
//
|
|
if (File->Info != NULL) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
Info->FileSize = BootMonFsGetImageLength (File);
|
|
Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
|
|
AsciiStrToUnicodeStr (AsciiFileName, Info->FileName);
|
|
|
|
File->Info = Info;
|
|
Info = NULL;
|
|
File->Position = 0;
|
|
File->OpenMode = OpenMode;
|
|
|
|
*NewHandle = &File->File;
|
|
}
|
|
|
|
Error:
|
|
|
|
FreePool (Buf);
|
|
if (AsciiFileName != NULL) {
|
|
FreePool (AsciiFileName);
|
|
}
|
|
if (Info != NULL) {
|
|
FreePool (Info);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
// Delete() for the root directory's EFI_FILE_PROTOCOL instance
|
|
EFIAPI
|
|
EFI_STATUS
|
|
BootMonFsDeleteFail (
|
|
IN EFI_FILE_PROTOCOL *This
|
|
)
|
|
{
|
|
This->Close(This);
|
|
// You can't delete the root directory
|
|
return EFI_WARN_DELETE_FAILURE;
|
|
}
|
|
|
|
/**
|
|
Close and delete a file from the boot monitor file system.
|
|
|
|
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
|
|
handle to delete.
|
|
|
|
@retval EFI_SUCCESS The file was closed and deleted.
|
|
@retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open
|
|
file handle.
|
|
@retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted.
|
|
|
|
**/
|
|
EFIAPI
|
|
EFI_STATUS
|
|
BootMonFsDelete (
|
|
IN EFI_FILE_PROTOCOL *This
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOTMON_FS_FILE *File;
|
|
LIST_ENTRY *RegionToFlushLink;
|
|
BOOTMON_FS_FILE_REGION *Region;
|
|
|
|
if (This == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
|
|
if (File->Info == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!IsListEmpty (&File->RegionToFlushLink)) {
|
|
// Free the entries from the Buffer List
|
|
RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
|
|
do {
|
|
Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
|
|
|
|
//
|
|
// Get next element of the list before deleting the region description
|
|
// that contain the LIST_ENTRY structure.
|
|
//
|
|
RegionToFlushLink = RemoveEntryList (RegionToFlushLink);
|
|
|
|
// Free the buffers
|
|
FreePool (Region->Buffer);
|
|
FreePool (Region);
|
|
} while (!IsListEmpty (&File->RegionToFlushLink));
|
|
}
|
|
|
|
// If (RegionCount is greater than 0) then the file already exists
|
|
if (File->HwDescription.RegionCount > 0) {
|
|
// Invalidate the last Block
|
|
Status = InvalidateImageDescription (File);
|
|
ASSERT_EFI_ERROR (Status);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_WARN_DELETE_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Remove the entry from the list
|
|
RemoveEntryList (&File->Link);
|
|
FreePool (File->Info);
|
|
FreePool (File);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|