\r
Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION));\r
\r
+ if (Buffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
Status = DiskIo->WriteDisk (DiskIo,\r
MediaId,\r
File->HwDescAddress,\r
return Status;\r
}\r
\r
-// Flush file data that will extend the file's length. Update and, if necessary,\r
-// move the image description.\r
-// We need to pass the file's starting position on media (FileStart), because\r
-// if the file hasn't been flushed before its Description->BlockStart won't\r
-// have been initialised.\r
-// FileStart must be aligned to the media's block size.\r
-// Note that this function uses DiskIo to flush, so call BlockIo->FlushBlocks()\r
-// after calling it.\r
+/**\r
+ Write the description of a file to storage media.\r
+\r
+ This function uses DiskIo to write to the media, so call BlockIo->FlushBlocks()\r
+ after calling it to ensure the data are written on the media.\r
+\r
+ @param[in] File Description of the file whose description on the\r
+ storage media has to be updated.\r
+ @param[in] FileName Name of the file. Its length is assumed to be\r
+ lower than MAX_NAME_LENGTH.\r
+ @param[in] DataSize Number of data bytes of the file.\r
+ @param[in] FileStart File's starting position on media. FileStart must\r
+ be aligned to the media's block size.\r
+\r
+ @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while performing\r
+ the write operation.\r
+\r
+**/\r
STATIC\r
EFI_STATUS\r
-FlushAppendRegion (\r
- IN BOOTMON_FS_FILE *File,\r
- IN BOOTMON_FS_FILE_REGION *Region,\r
- IN UINT64 NewFileSize,\r
- IN UINT64 FileStart\r
+WriteFileDescription (\r
+ IN BOOTMON_FS_FILE *File,\r
+ IN CHAR8 *FileName,\r
+ IN UINT32 DataSize,\r
+ IN UINT64 FileStart\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_DISK_IO_PROTOCOL *DiskIo;\r
- UINTN BlockSize;\r
- HW_IMAGE_DESCRIPTION *Description;\r
-\r
- DiskIo = File->Instance->DiskIo;\r
+ EFI_STATUS Status;\r
+ EFI_DISK_IO_PROTOCOL *DiskIo;\r
+ UINTN BlockSize;\r
+ UINT32 FileSize;\r
+ HW_IMAGE_DESCRIPTION *Description;\r
\r
+ DiskIo = File->Instance->DiskIo;\r
BlockSize = File->Instance->BlockIo->Media->BlockSize;\r
-\r
ASSERT (FileStart % BlockSize == 0);\r
\r
- // Only invalidate the Image Description of files that have already been\r
- // written in Flash\r
- if (File->HwDescAddress != 0) {\r
- Status = InvalidateImageDescription (File);\r
- ASSERT_EFI_ERROR (Status);\r
- }\r
-\r
//\r
- // Update File Description\r
+ // Construct the file description\r
//\r
+\r
+ FileSize = DataSize + sizeof (HW_IMAGE_DESCRIPTION);\r
Description = &File->HwDescription;\r
Description->Attributes = 1;\r
Description->BlockStart = FileStart / BlockSize;\r
- Description->BlockEnd = Description->BlockStart + (NewFileSize / BlockSize);\r
- Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1;\r
- Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2;\r
+ Description->BlockEnd = Description->BlockStart + (FileSize / BlockSize);\r
+ AsciiStrCpy (Description->Footer.Filename, FileName);\r
+\r
#ifdef MDE_CPU_ARM\r
+ Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET;\r
Description->Footer.Version = HW_IMAGE_FOOTER_VERSION;\r
- Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET;\r
#else\r
+ Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET2;\r
Description->Footer.Version = HW_IMAGE_FOOTER_VERSION2;\r
- Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET2;\r
#endif\r
+ Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1;\r
+ Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2;\r
Description->RegionCount = 1;\r
Description->Region[0].Checksum = 0;\r
Description->Region[0].Offset = Description->BlockStart * BlockSize;\r
- Description->Region[0].Size = NewFileSize - sizeof (HW_IMAGE_DESCRIPTION);\r
+ Description->Region[0].Size = DataSize;\r
\r
Status = BootMonFsComputeFooterChecksum (Description);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
- // Write the new file data\r
- Status = DiskIo->WriteDisk (\r
- DiskIo,\r
- File->Instance->Media->MediaId,\r
- FileStart + Region->Offset,\r
- Region->Size,\r
- Region->Buffer\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- // Round the file size up to the nearest block size\r
- if ((NewFileSize % BlockSize) > 0) {\r
- NewFileSize += BlockSize - (NewFileSize % BlockSize);\r
- }\r
-\r
- File->HwDescAddress = (FileStart + NewFileSize) - sizeof (HW_IMAGE_DESCRIPTION);\r
+ File->HwDescAddress = ((Description->BlockEnd + 1) * BlockSize) - sizeof (HW_IMAGE_DESCRIPTION);\r
\r
// Update the file description on the media\r
Status = DiskIo->WriteDisk (\r
return Status;\r
}\r
\r
-BOOLEAN\r
-BootMonFsFileNeedFlush (\r
- IN BOOTMON_FS_FILE *File\r
- )\r
-{\r
- return !IsListEmpty (&File->RegionToFlushLink);\r
-}\r
-\r
// Find a space on media for a file that has not yet been flushed to disk.\r
// Just returns the first space that's big enough.\r
// This function could easily be adapted to:\r
EFI_STATUS\r
BootMonFsFindSpaceForNewFile (\r
IN BOOTMON_FS_FILE *File,\r
+ IN UINT64 FileSize,\r
OUT UINT64 *FileStart\r
)\r
{\r
BOOTMON_FS_FILE *RootFile;\r
BOOTMON_FS_FILE *FileEntry;\r
UINTN BlockSize;\r
- UINT64 FileSize;\r
EFI_BLOCK_IO_MEDIA *Media;\r
\r
Media = File->Instance->BlockIo->Media;\r
BlockSize = Media->BlockSize;\r
RootFile = File->Instance->RootFile;\r
\r
- if (IsListEmpty (&RootFile->Link)) {\r
- return EFI_SUCCESS;\r
- }\r
-\r
// This function must only be called for file which has not been flushed into\r
// Flash yet\r
ASSERT (File->HwDescription.RegionCount == 0);\r
\r
- // Find out how big the file will be\r
- FileSize = BootMonFsGetImageLength (File);\r
- // Add the file header to the file\r
- FileSize += sizeof (HW_IMAGE_DESCRIPTION);\r
-\r
*FileStart = 0;\r
// Go through all the files in the list\r
for (FileLink = GetFirstNode (&RootFile->Link);\r
}\r
}\r
\r
+/**\r
+ Flush all modified data associated with a file to a device.\r
+\r
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
+ file handle to flush.\r
+\r
+ @retval EFI_SUCCESS The data was flushed.\r
+ @retval EFI_ACCESS_DENIED The file was opened read-only.\r
+ @retval EFI_DEVICE_ERROR The device reported an error.\r
+ @retval EFI_VOLUME_FULL The volume is full.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to flush the data.\r
+ @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.\r
+\r
+**/\r
EFIAPI\r
EFI_STATUS\r
BootMonFsFlushFile (\r
{\r
EFI_STATUS Status;\r
BOOTMON_FS_INSTANCE *Instance;\r
+ EFI_FILE_INFO *Info;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_DISK_IO_PROTOCOL *DiskIo;\r
+ UINTN BlockSize;\r
+ CHAR8 AsciiFileName[MAX_NAME_LENGTH];\r
LIST_ENTRY *RegionToFlushLink;\r
BOOTMON_FS_FILE *File;\r
BOOTMON_FS_FILE *NextFile;\r
BOOTMON_FS_FILE_REGION *Region;\r
LIST_ENTRY *FileLink;\r
UINTN CurrentPhysicalSize;\r
- UINTN BlockSize;\r
UINT64 FileStart;\r
UINT64 FileEnd;\r
UINT64 RegionStart;\r
UINT64 RegionEnd;\r
+ UINT64 NewDataSize;\r
UINT64 NewFileSize;\r
UINT64 EndOfAppendSpace;\r
BOOLEAN HasSpace;\r
- EFI_DISK_IO_PROTOCOL *DiskIo;\r
- EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
\r
- Status = EFI_SUCCESS;\r
- FileStart = 0;\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
\r
File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
- if (File == NULL) {\r
+ if (File->Info == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
- // Check if the file needs to be flushed\r
- if (!BootMonFsFileNeedFlush (File)) {\r
- return Status;\r
+ if (File->OpenMode == EFI_FILE_MODE_READ) {\r
+ return EFI_ACCESS_DENIED;\r
}\r
\r
- Instance = File->Instance;\r
- BlockIo = Instance->BlockIo;\r
- DiskIo = Instance->DiskIo;\r
- BlockSize = BlockIo->Media->BlockSize;\r
+ Instance = File->Instance;\r
+ Info = File->Info;\r
+ BlockIo = Instance->BlockIo;\r
+ Media = BlockIo->Media;\r
+ DiskIo = Instance->DiskIo;\r
+ BlockSize = Media->BlockSize;\r
+\r
+ UnicodeStrToAsciiStr (Info->FileName, AsciiFileName);\r
\r
// If the file doesn't exist then find a space for it\r
if (File->HwDescription.RegionCount == 0) {\r
- Status = BootMonFsFindSpaceForNewFile (File, &FileStart);\r
- // FileStart has changed so we need to recompute RegionEnd\r
+ Status = BootMonFsFindSpaceForNewFile (\r
+ File,\r
+ Info->FileSize + sizeof (HW_IMAGE_DESCRIPTION),\r
+ &FileStart\r
+ );\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
} else {\r
FileStart = File->HwDescription.BlockStart * BlockSize;\r
}\r
-\r
// FileEnd is the current NOR address of the end of the file's data\r
FileEnd = FileStart + File->HwDescription.Region[0].Size;\r
\r
)\r
{\r
Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
+ if (Region->Size == 0) {\r
+ continue;\r
+ }\r
\r
// RegionStart and RegionEnd are the the intended NOR address of the\r
// start and end of the region\r
- RegionStart = FileStart + Region->Offset;\r
- RegionEnd = RegionStart + Region->Size;\r
+ RegionStart = FileStart + Region->Offset;\r
+ RegionEnd = RegionStart + Region->Size;\r
\r
if (RegionEnd < FileEnd) {\r
// Handle regions representing edits to existing portions of the file\r
// Write the region data straight into the file\r
Status = DiskIo->WriteDisk (DiskIo,\r
- BlockIo->Media->MediaId,\r
+ Media->MediaId,\r
RegionStart,\r
Region->Size,\r
Region->Buffer\r
\r
// Check if there is space to append the new region\r
HasSpace = FALSE;\r
- NewFileSize = (RegionEnd - FileStart) + sizeof (HW_IMAGE_DESCRIPTION);\r
+ NewDataSize = RegionEnd - FileStart;\r
+ NewFileSize = NewDataSize + sizeof (HW_IMAGE_DESCRIPTION);\r
CurrentPhysicalSize = BootMonFsGetPhysicalSize (File);\r
if (NewFileSize <= CurrentPhysicalSize) {\r
HasSpace = TRUE;\r
EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize;\r
} else {\r
// We are flushing the last file.\r
- EndOfAppendSpace = (BlockIo->Media->LastBlock + 1) * BlockSize;\r
+ EndOfAppendSpace = (Media->LastBlock + 1) * BlockSize;\r
}\r
if (EndOfAppendSpace - FileStart >= NewFileSize) {\r
HasSpace = TRUE;\r
}\r
\r
if (HasSpace == TRUE) {\r
- Status = FlushAppendRegion (File, Region, NewFileSize, FileStart);\r
+ // Invalidate the current image description of the file if any.\r
+ if (File->HwDescAddress != 0) {\r
+ Status = InvalidateImageDescription (File);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ // Write the new file data\r
+ Status = DiskIo->WriteDisk (\r
+ DiskIo,\r
+ Media->MediaId,\r
+ RegionStart,\r
+ Region->Size,\r
+ Region->Buffer\r
+ );\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
+\r
+ Status = WriteFileDescription (File, AsciiFileName, NewDataSize, FileStart);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
} else {\r
// There isn't a space for the file.\r
// Options here are to move the file or fragment it. However as files\r
}\r
\r
FreeFileRegions (File);\r
+ Info->PhysicalSize = BootMonFsGetPhysicalSize (File);\r
+\r
+ if ((AsciiStrCmp (AsciiFileName, File->HwDescription.Footer.Filename) != 0) ||\r
+ (Info->FileSize != File->HwDescription.Region[0].Size) ) {\r
+ Status = WriteFileDescription (File, AsciiFileName, Info->FileSize, FileStart);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
\r
// Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by\r
// calling FlushBlocks on the same device's BlockIo).\r
BlockIo->FlushBlocks (BlockIo);\r
\r
- return Status;\r
+ return EFI_SUCCESS;\r
}\r
\r
/**\r
- Closes a file on the Nor Flash FS volume.\r
+ Close a specified file handle.\r
\r
- @param This The EFI_FILE_PROTOCOL to close.\r
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
+ handle to close.\r
\r
- @return Always returns EFI_SUCCESS.\r
+ @retval EFI_SUCCESS The file was closed.\r
+ @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open\r
+ file handle.\r
\r
**/\r
EFIAPI\r
IN EFI_FILE_PROTOCOL *This\r
)\r
{\r
- // Flush the file if needed\r
- This->Flush (This);\r
- return EFI_SUCCESS;\r
-}\r
+ BOOTMON_FS_FILE *File;\r
\r
-// Create a new instance of BOOTMON_FS_FILE.\r
-// Uses BootMonFsCreateFile to\r
-STATIC\r
-EFI_STATUS\r
-CreateNewFile (\r
- IN BOOTMON_FS_INSTANCE *Instance,\r
- IN CHAR8* AsciiFileName,\r
- OUT BOOTMON_FS_FILE **NewHandle\r
- )\r
-{\r
- EFI_STATUS Status;\r
- BOOTMON_FS_FILE *File;\r
-\r
- Status = BootMonFsCreateFile (Instance, &File);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
}\r
\r
- // Remove the leading '\\'\r
- if (*AsciiFileName == '\\') {\r
- AsciiFileName++;\r
+ File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
+ if (File->Info == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
}\r
\r
- // Set the file name\r
- CopyMem (File->HwDescription.Footer.Filename, AsciiFileName, MAX_NAME_LENGTH);\r
-\r
- // Add the file to list of files of the File System\r
- InsertHeadList (&Instance->RootFile->Link, &File->Link);\r
+ // In the case of a file and not the root directory\r
+ if (This != &File->Instance->RootFile->File) {\r
+ This->Flush (This);\r
+ FreePool (File->Info);\r
+ File->Info = NULL;\r
+ }\r
\r
- *NewHandle = File;\r
- return Status;\r
+ return EFI_SUCCESS;\r
}\r
\r
/**\r
CHAR16 *Path;\r
CHAR16 *Separator;\r
CHAR8 *AsciiFileName;\r
+ EFI_FILE_INFO *Info;\r
\r
if (This == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
+ Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
+ if (Directory->Info == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
if ((FileName == NULL) || (NewHandle == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
- Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
- if (Directory == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
Instance = Directory->Instance;\r
\r
//\r
}\r
Path = (CHAR16*)Buf;\r
AsciiFileName = NULL;\r
+ Info = NULL;\r
\r
//\r
// Handle single periods, double periods and convert forward slashes '/'\r
goto Error;\r
}\r
\r
+ //\r
+ // Allocate a buffer to store the characteristics of the file while the\r
+ // file is open. We allocate the maximum size to not have to reallocate\r
+ // if the file name is changed.\r
+ //\r
+ Info = AllocateZeroPool (\r
+ SIZE_OF_EFI_FILE_INFO + (sizeof (CHAR16) * MAX_NAME_LENGTH));\r
+ if (Info == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
//\r
// Open or create a file in the root directory.\r
//\r
goto Error;\r
}\r
\r
- Status = CreateNewFile (Instance, AsciiFileName, &File);\r
- if (!EFI_ERROR (Status)) {\r
- File->OpenMode = OpenMode;\r
- *NewHandle = &File->File;\r
- File->Position = 0;\r
+ Status = BootMonFsCreateFile (Instance, &File);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
}\r
+ InsertHeadList (&Instance->RootFile->Link, &File->Link);\r
+ Info->Attribute = Attributes;\r
} else {\r
//\r
- // The file already exists.\r
+ // File already open, not supported yet.\r
//\r
- File->OpenMode = OpenMode;\r
- *NewHandle = &File->File;\r
- File->Position = 0;\r
+ if (File->Info != NULL) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Error;\r
+ }\r
}\r
+\r
+ Info->FileSize = BootMonFsGetImageLength (File);\r
+ Info->PhysicalSize = BootMonFsGetPhysicalSize (File);\r
+ AsciiStrToUnicodeStr (AsciiFileName, Info->FileName);\r
+\r
+ File->Info = Info;\r
+ Info = NULL;\r
+ File->Position = 0;\r
+ File->OpenMode = OpenMode;\r
+\r
+ *NewHandle = &File->File;\r
}\r
\r
Error:\r
if (AsciiFileName != NULL) {\r
FreePool (AsciiFileName);\r
}\r
+ if (Info != NULL) {\r
+ FreePool (Info);\r
+ }\r
\r
return Status;\r
}\r
// You can't delete the root directory\r
return EFI_WARN_DELETE_FAILURE;\r
}\r
+\r
+/**\r
+ Close and delete a file from the boot monitor file system.\r
+\r
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
+ handle to delete.\r
+\r
+ @retval EFI_SUCCESS The file was closed and deleted.\r
+ @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open\r
+ file handle.\r
+ @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted.\r
+\r
+**/\r
EFIAPI\r
EFI_STATUS\r
BootMonFsDelete (\r
BOOTMON_FS_FILE *File;\r
LIST_ENTRY *RegionToFlushLink;\r
BOOTMON_FS_FILE_REGION *Region;\r
- EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
- UINT8 *EmptyBuffer;\r
\r
- File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
- if (File == NULL) {\r
- return EFI_DEVICE_ERROR;\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
}\r
\r
- Status = EFI_SUCCESS;\r
+ File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
+ if (File->Info == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
\r
- if (BootMonFsFileNeedFlush (File)) {\r
+ if (!IsListEmpty (&File->RegionToFlushLink)) {\r
// Free the entries from the Buffer List\r
RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
do {\r
Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
\r
- // Get Next entry\r
+ //\r
+ // Get next element of the list before deleting the region description\r
+ // that contain the LIST_ENTRY structure.\r
+ //\r
RegionToFlushLink = RemoveEntryList (RegionToFlushLink);\r
\r
// Free the buffers\r
\r
// If (RegionCount is greater than 0) then the file already exists\r
if (File->HwDescription.RegionCount > 0) {\r
- BlockIo = File->Instance->BlockIo;\r
-\r
- // Create an empty buffer\r
- EmptyBuffer = AllocateZeroPool (BlockIo->Media->BlockSize);\r
- if (EmptyBuffer == NULL) {\r
- FreePool (File);\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
// Invalidate the last Block\r
Status = InvalidateImageDescription (File);\r
ASSERT_EFI_ERROR (Status);\r
-\r
- FreePool (EmptyBuffer);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_WARN_DELETE_FAILURE;\r
+ }\r
}\r
\r
// Remove the entry from the list\r
RemoveEntryList (&File->Link);\r
+ FreePool (File->Info);\r
FreePool (File);\r
- return Status;\r
-}\r
\r
+ return EFI_SUCCESS;\r
+}\r