/** @file Functions that perform file read/write. Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.
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); }