blob: 8f525044d1f147e2cec8e62f20d0f289623b26e6 [file] [log] [blame]
/** @file
Functions that perform file read/write.
Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Fat.h"
/**
Get the file's position of the file.
@param FHand - The handle of file.
@param Position - The file's position of the file.
@retval EFI_SUCCESS - Get the info successfully.
@retval EFI_DEVICE_ERROR - Can not find the OFile for the file.
@retval EFI_UNSUPPORTED - The open file is not a file.
**/
EFI_STATUS
EFIAPI
FatGetPosition (
IN EFI_FILE_PROTOCOL *FHand,
OUT UINT64 *Position
)
{
FAT_IFILE *IFile;
FAT_OFILE *OFile;
IFile = IFILE_FROM_FHAND (FHand);
OFile = IFile->OFile;
if (OFile->Error == EFI_NOT_FOUND) {
return EFI_DEVICE_ERROR;
}
if (OFile->ODir != NULL) {
return EFI_UNSUPPORTED;
}
*Position = IFile->Position;
return EFI_SUCCESS;
}
/**
Set the file's position of the file.
@param FHand - The handle of file.
@param Position - The file's position of the file.
@retval EFI_SUCCESS - Set the info successfully.
@retval EFI_DEVICE_ERROR - Can not find the OFile for the file.
@retval EFI_UNSUPPORTED - Set a directory with a not-zero position.
**/
EFI_STATUS
EFIAPI
FatSetPosition (
IN EFI_FILE_PROTOCOL *FHand,
IN UINT64 Position
)
{
FAT_IFILE *IFile;
FAT_OFILE *OFile;
IFile = IFILE_FROM_FHAND (FHand);
OFile = IFile->OFile;
if (OFile->Error == EFI_NOT_FOUND) {
return EFI_DEVICE_ERROR;
}
FatWaitNonblockingTask (IFile);
//
// If this is a directory, we can only set back to position 0
//
if (OFile->ODir != NULL) {
if (Position != 0) {
//
// Reset current directory cursor;
//
return EFI_UNSUPPORTED;
}
FatResetODirCursor (OFile);
}
//
// Set the position
//
if (Position == (UINT64)-1) {
Position = OFile->FileSize;
}
//
// Set the position
//
IFile->Position = Position;
return EFI_SUCCESS;
}
/**
Get the file info from the open file of the IFile into Buffer.
@param IFile - The instance of the open file.
@param BufferSize - Size of Buffer.
@param Buffer - Buffer containing read data.
@retval EFI_SUCCESS - Get the file info successfully.
@retval other - An error occurred when operation the disk.
**/
EFI_STATUS
FatIFileReadDir (
IN FAT_IFILE *IFile,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
FAT_OFILE *OFile;
FAT_ODIR *ODir;
FAT_DIRENT *DirEnt;
UINT32 CurrentPos;
OFile = IFile->OFile;
ODir = OFile->ODir;
CurrentPos = ((UINT32)IFile->Position) / sizeof (FAT_DIRECTORY_ENTRY);
//
// We need to relocate the directory
//
if (CurrentPos < ODir->CurrentPos) {
//
// The directory cursor has been modified by another IFile, we reset the cursor
//
FatResetODirCursor (OFile);
}
//
// We seek the next directory entry's position
//
do {
Status = FatGetNextDirEnt (OFile, &DirEnt);
if (EFI_ERROR (Status) || (DirEnt == NULL)) {
//
// Something error occurred or reach the end of directory,
// return 0 buffersize
//
*BufferSize = 0;
goto Done;
}
} while (ODir->CurrentPos <= CurrentPos);
Status = FatGetDirEntInfo (OFile->Volume, DirEnt, BufferSize, Buffer);
Done:
//
// Update IFile's Position
//
if (!EFI_ERROR (Status)) {
//
// Update IFile->Position, if everything is all right
//
CurrentPos = ODir->CurrentPos;
IFile->Position = CurrentPos * sizeof (FAT_DIRECTORY_ENTRY);
}
return Status;
}
/**
Get the file info from the open file of the IFile into Buffer.
@param FHand - The file handle to access.
@param IoMode - Indicate whether the access mode is reading or writing.
@param BufferSize - Size of Buffer.
@param Buffer - Buffer containing read data.
@param Token - A pointer to the token associated with the transaction.
@retval EFI_SUCCESS - Get the file info successfully.
@retval EFI_DEVICE_ERROR - Can not find the OFile for the file.
@retval EFI_VOLUME_CORRUPTED - The file type of open file is error.
@retval EFI_WRITE_PROTECTED - The disk is write protect.
@retval EFI_ACCESS_DENIED - The file is read-only.
@return other - An error occurred when operating on the disk.
**/
EFI_STATUS
FatIFileAccess (
IN EFI_FILE_PROTOCOL *FHand,
IN IO_MODE IoMode,
IN OUT UINTN *BufferSize,
IN OUT VOID *Buffer,
IN EFI_FILE_IO_TOKEN *Token
)
{
EFI_STATUS Status;
FAT_IFILE *IFile;
FAT_OFILE *OFile;
FAT_VOLUME *Volume;
UINT64 EndPosition;
FAT_TASK *Task;
IFile = IFILE_FROM_FHAND (FHand);
OFile = IFile->OFile;
Volume = OFile->Volume;
Task = NULL;
//
// Write to a directory is unsupported
//
if ((OFile->ODir != NULL) && (IoMode == WriteData)) {
return EFI_UNSUPPORTED;
}
if (OFile->Error == EFI_NOT_FOUND) {
return EFI_DEVICE_ERROR;
}
if (IoMode == ReadData) {
//
// If position is at EOF, then return device error
//
if (IFile->Position > OFile->FileSize) {
return EFI_DEVICE_ERROR;
}
} else {
//
// Check if the we can write data
//
if (Volume->ReadOnly) {
return EFI_WRITE_PROTECTED;
}
if (IFile->ReadOnly) {
return EFI_ACCESS_DENIED;
}
}
if (Token == NULL) {
FatWaitNonblockingTask (IFile);
} else {
//
// Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2.
// But if it calls, the below check can avoid crash.
//
if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) {
return EFI_UNSUPPORTED;
}
Task = FatCreateTask (IFile, Token);
if (Task == NULL) {
return EFI_OUT_OF_RESOURCES;
}
}
FatAcquireLock ();
Status = OFile->Error;
if (!EFI_ERROR (Status)) {
if (OFile->ODir != NULL) {
//
// Read a directory is supported
//
ASSERT (IoMode == ReadData);
Status = FatIFileReadDir (IFile, BufferSize, Buffer);
OFile = NULL;
} else {
//
// Access a file
//
EndPosition = IFile->Position + *BufferSize;
if (EndPosition > OFile->FileSize) {
//
// The position goes beyond the end of file
//
if (IoMode == ReadData) {
//
// Adjust the actual size read
//
*BufferSize -= (UINTN)EndPosition - OFile->FileSize;
} else {
//
// We expand the file size of OFile
//
Status = FatGrowEof (OFile, EndPosition);
if (EFI_ERROR (Status)) {
//
// Must update the file's info into the file's Directory Entry
// and then flush the dirty cache info into disk.
//
*BufferSize = 0;
FatOFileFlush (OFile);
OFile = NULL;
goto Done;
}
FatUpdateDirEntClusterSizeInfo (OFile);
}
}
Status = FatAccessOFile (OFile, IoMode, (UINTN)IFile->Position, BufferSize, Buffer, Task);
IFile->Position += *BufferSize;
}
}
if (Token != NULL) {
if (!EFI_ERROR (Status)) {
Status = FatQueueTask (IFile, Task);
} else {
FatDestroyTask (Task);
}
}
Done:
//
// On EFI_SUCCESS case, not calling FatCleanupVolume():
// 1) The Cache flush operation is avoided to enhance
// performance. Caller is responsible to call Flush() when necessary.
// 2) The volume dirty bit is probably set already, and is expected to be
// cleaned in subsequent Flush() or other operations.
// 3) Write operation doesn't affect OFile/IFile structure, so
// Reference checking is not necessary.
//
if (EFI_ERROR (Status)) {
Status = FatCleanupVolume (Volume, OFile, Status, NULL);
}
FatReleaseLock ();
return Status;
}
/**
Get the file info.
@param FHand - The handle of the file.
@param BufferSize - Size of Buffer.
@param Buffer - Buffer containing read data.
@retval EFI_SUCCESS - Get the file info successfully.
@retval EFI_DEVICE_ERROR - Can not find the OFile for the file.
@retval EFI_VOLUME_CORRUPTED - The file type of open file is error.
@return other - An error occurred when operation the disk.
**/
EFI_STATUS
EFIAPI
FatRead (
IN EFI_FILE_PROTOCOL *FHand,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
return FatIFileAccess (FHand, ReadData, BufferSize, Buffer, NULL);
}
/**
Get the file info.
@param FHand - The handle of the file.
@param Token - A pointer to the token associated with the transaction.
@retval EFI_SUCCESS - Get the file info successfully.
@retval EFI_DEVICE_ERROR - Can not find the OFile for the file.
@retval EFI_VOLUME_CORRUPTED - The file type of open file is error.
@return other - An error occurred when operation the disk.
**/
EFI_STATUS
EFIAPI
FatReadEx (
IN EFI_FILE_PROTOCOL *FHand,
IN OUT EFI_FILE_IO_TOKEN *Token
)
{
return FatIFileAccess (FHand, ReadData, &Token->BufferSize, Token->Buffer, Token);
}
/**
Write the content of buffer into files.
@param FHand - The handle of the file.
@param BufferSize - Size of Buffer.
@param Buffer - Buffer containing write data.
@retval EFI_SUCCESS - Set the file info successfully.
@retval EFI_WRITE_PROTECTED - The disk is write protect.
@retval EFI_ACCESS_DENIED - The file is read-only.
@retval EFI_DEVICE_ERROR - The OFile is not valid.
@retval EFI_UNSUPPORTED - The open file is not a file.
- The writing file size is larger than 4GB.
@return other - An error occurred when operation the disk.
**/
EFI_STATUS
EFIAPI
FatWrite (
IN EFI_FILE_PROTOCOL *FHand,
IN OUT UINTN *BufferSize,
IN VOID *Buffer
)
{
return FatIFileAccess (FHand, WriteData, BufferSize, Buffer, NULL);
}
/**
Get the file info.
@param FHand - The handle of the file.
@param Token - A pointer to the token associated with the transaction.
@retval EFI_SUCCESS - Get the file info successfully.
@retval EFI_DEVICE_ERROR - Can not find the OFile for the file.
@retval EFI_VOLUME_CORRUPTED - The file type of open file is error.
@return other - An error occurred when operation the disk.
**/
EFI_STATUS
EFIAPI
FatWriteEx (
IN EFI_FILE_PROTOCOL *FHand,
IN OUT EFI_FILE_IO_TOKEN *Token
)
{
return FatIFileAccess (FHand, WriteData, &Token->BufferSize, Token->Buffer, Token);
}
/**
This function reads data from a file or writes data to a file.
It uses OFile->PosRem to determine how much data can be accessed in one time.
@param OFile - The open file.
@param IoMode - Indicate whether the access mode is reading or writing.
@param Position - The position where data will be accessed.
@param DataBufferSize - Size of Buffer.
@param UserBuffer - Buffer containing data.
@param Task point to task instance.
@retval EFI_SUCCESS - Access the data successfully.
@return other - An error occurred when operating on the disk.
**/
EFI_STATUS
FatAccessOFile (
IN FAT_OFILE *OFile,
IN IO_MODE IoMode,
IN UINTN Position,
IN OUT UINTN *DataBufferSize,
IN OUT UINT8 *UserBuffer,
IN FAT_TASK *Task
)
{
FAT_VOLUME *Volume;
UINTN Len;
EFI_STATUS Status;
UINTN BufferSize;
BufferSize = *DataBufferSize;
Volume = OFile->Volume;
ASSERT_VOLUME_LOCKED (Volume);
Status = EFI_SUCCESS;
while (BufferSize > 0) {
//
// Seek the OFile to the file position
//
Status = FatOFilePosition (OFile, Position, BufferSize);
if (EFI_ERROR (Status)) {
break;
}
//
// Clip length to block run
//
Len = BufferSize > OFile->PosRem ? OFile->PosRem : BufferSize;
//
// Write the data
//
Status = FatDiskIo (Volume, IoMode, OFile->PosDisk, Len, UserBuffer, Task);
if (EFI_ERROR (Status)) {
break;
}
//
// Data was successfully accessed
//
Position += Len;
UserBuffer += Len;
BufferSize -= Len;
if (IoMode == WriteData) {
OFile->Dirty = TRUE;
OFile->Archive = TRUE;
}
//
// Make sure no outbound occurred
//
ASSERT (Position <= OFile->FileSize);
}
//
// Update the number of bytes accessed
//
*DataBufferSize -= BufferSize;
return Status;
}
/**
Expand OFile by appending zero bytes at the end of OFile.
@param OFile - The open file.
@param ExpandedSize - The number of zero bytes appended at the end of the file.
@retval EFI_SUCCESS - The file is expanded successfully.
@return other - An error occurred when expanding file.
**/
EFI_STATUS
FatExpandOFile (
IN FAT_OFILE *OFile,
IN UINT64 ExpandedSize
)
{
EFI_STATUS Status;
UINTN WritePos;
WritePos = OFile->FileSize;
Status = FatGrowEof (OFile, ExpandedSize);
if (!EFI_ERROR (Status)) {
Status = FatWriteZeroPool (OFile, WritePos);
}
return Status;
}
/**
Write zero pool from the WritePos to the end of OFile.
@param OFile - The open file to write zero pool.
@param WritePos - The number of zero bytes written.
@retval EFI_SUCCESS - Write the zero pool successfully.
@retval EFI_OUT_OF_RESOURCES - Not enough memory to perform the operation.
@return other - An error occurred when writing disk.
**/
EFI_STATUS
FatWriteZeroPool (
IN FAT_OFILE *OFile,
IN UINTN WritePos
)
{
EFI_STATUS Status;
VOID *ZeroBuffer;
UINTN AppendedSize;
UINTN BufferSize;
UINTN WriteSize;
AppendedSize = OFile->FileSize - WritePos;
BufferSize = AppendedSize;
if (AppendedSize > FAT_MAX_ALLOCATE_SIZE) {
//
// If the appended size is larger, maybe we can not allocate the whole
// memory once. So if the growed size is larger than 10M, we just
// allocate 10M memory (one healthy system should have 10M available
// memory), and then write the zerobuffer to the file several times.
//
BufferSize = FAT_MAX_ALLOCATE_SIZE;
}
ZeroBuffer = AllocateZeroPool (BufferSize);
if (ZeroBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
do {
WriteSize = AppendedSize > BufferSize ? BufferSize : (UINTN)AppendedSize;
AppendedSize -= WriteSize;
Status = FatAccessOFile (OFile, WriteData, WritePos, &WriteSize, ZeroBuffer, NULL);
if (EFI_ERROR (Status)) {
break;
}
WritePos += WriteSize;
} while (AppendedSize > 0);
FreePool (ZeroBuffer);
return Status;
}
/**
Truncate the OFile to smaller file size.
@param OFile - The open file.
@param TruncatedSize - The new file size.
@retval EFI_SUCCESS - The file is truncated successfully.
@return other - An error occurred when truncating file.
**/
EFI_STATUS
FatTruncateOFile (
IN FAT_OFILE *OFile,
IN UINTN TruncatedSize
)
{
OFile->FileSize = TruncatedSize;
return FatShrinkEof (OFile);
}