Home | History | Annotate | Download | only in EnhancedFatDxe
      1 /** @file
      2   Functions that perform file read/write.
      3 
      4 Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials are licensed and made available
      6 under the terms and conditions of the BSD License which accompanies this
      7 distribution. The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 
     14 **/
     15 
     16 #include "Fat.h"
     17 
     18 /**
     19 
     20   Get the file's position of the file.
     21 
     22 
     23   @param  FHand                 - The handle of file.
     24   @param  Position              - The file's position of the file.
     25 
     26   @retval EFI_SUCCESS           - Get the info successfully.
     27   @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
     28   @retval EFI_UNSUPPORTED       - The open file is not a file.
     29 
     30 **/
     31 EFI_STATUS
     32 EFIAPI
     33 FatGetPosition (
     34   IN  EFI_FILE_PROTOCOL *FHand,
     35   OUT UINT64            *Position
     36   )
     37 {
     38   FAT_IFILE *IFile;
     39   FAT_OFILE *OFile;
     40 
     41   IFile = IFILE_FROM_FHAND (FHand);
     42   OFile = IFile->OFile;
     43 
     44   if (OFile->Error == EFI_NOT_FOUND) {
     45     return EFI_DEVICE_ERROR;
     46   }
     47 
     48   if (OFile->ODir != NULL) {
     49     return EFI_UNSUPPORTED;
     50   }
     51 
     52   *Position = IFile->Position;
     53   return EFI_SUCCESS;
     54 }
     55 
     56 /**
     57 
     58   Set the file's position of the file.
     59 
     60   @param  FHand                 - The handle of file.
     61   @param  Position              - The file's position of the file.
     62 
     63   @retval EFI_SUCCESS           - Set the info successfully.
     64   @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
     65   @retval EFI_UNSUPPORTED       - Set a directory with a not-zero position.
     66 
     67 **/
     68 EFI_STATUS
     69 EFIAPI
     70 FatSetPosition (
     71   IN EFI_FILE_PROTOCOL  *FHand,
     72   IN UINT64             Position
     73   )
     74 {
     75   FAT_IFILE *IFile;
     76   FAT_OFILE *OFile;
     77 
     78   IFile = IFILE_FROM_FHAND (FHand);
     79   OFile = IFile->OFile;
     80 
     81   if (OFile->Error == EFI_NOT_FOUND) {
     82     return EFI_DEVICE_ERROR;
     83   }
     84 
     85   FatWaitNonblockingTask (IFile);
     86 
     87   //
     88   // If this is a directory, we can only set back to position 0
     89   //
     90   if (OFile->ODir != NULL) {
     91     if (Position != 0) {
     92       //
     93       // Reset current directory cursor;
     94       //
     95       return EFI_UNSUPPORTED;
     96     }
     97 
     98     FatResetODirCursor (OFile);
     99   }
    100   //
    101   // Set the position
    102   //
    103   if (Position == (UINT64)-1) {
    104     Position = OFile->FileSize;
    105   }
    106   //
    107   // Set the position
    108   //
    109   IFile->Position = Position;
    110   return EFI_SUCCESS;
    111 }
    112 
    113 /**
    114 
    115   Get the file info from the open file of the IFile into Buffer.
    116 
    117   @param  IFile                 - The instance of the open file.
    118   @param  BufferSize            - Size of Buffer.
    119   @param  Buffer                - Buffer containing read data.
    120 
    121   @retval EFI_SUCCESS           - Get the file info successfully.
    122   @retval other                 - An error occurred when operation the disk.
    123 
    124 **/
    125 EFI_STATUS
    126 FatIFileReadDir (
    127   IN     FAT_IFILE              *IFile,
    128   IN OUT UINTN                  *BufferSize,
    129      OUT VOID                   *Buffer
    130   )
    131 {
    132   EFI_STATUS  Status;
    133   FAT_OFILE   *OFile;
    134   FAT_ODIR    *ODir;
    135   FAT_DIRENT  *DirEnt;
    136   UINT32      CurrentPos;
    137 
    138   OFile       = IFile->OFile;
    139   ODir        = OFile->ODir;
    140   CurrentPos  = ((UINT32) IFile->Position) / sizeof (FAT_DIRECTORY_ENTRY);
    141 
    142   //
    143   // We need to relocate the directory
    144   //
    145   if (CurrentPos < ODir->CurrentPos) {
    146     //
    147     // The directory cursor has been modified by another IFile, we reset the cursor
    148     //
    149     FatResetODirCursor (OFile);
    150   }
    151   //
    152   // We seek the next directory entry's position
    153   //
    154   do {
    155     Status = FatGetNextDirEnt (OFile, &DirEnt);
    156     if (EFI_ERROR (Status) || DirEnt == NULL) {
    157       //
    158       // Something error occurred or reach the end of directory,
    159       // return 0 buffersize
    160       //
    161       *BufferSize = 0;
    162       goto Done;
    163     }
    164   } while (ODir->CurrentPos <= CurrentPos);
    165   Status = FatGetDirEntInfo (OFile->Volume, DirEnt, BufferSize, Buffer);
    166 
    167 Done:
    168   //
    169   // Update IFile's Position
    170   //
    171   if (!EFI_ERROR (Status)) {
    172     //
    173     // Update IFile->Position, if everything is all right
    174     //
    175     CurrentPos      = ODir->CurrentPos;
    176     IFile->Position = (UINT64) (CurrentPos * sizeof (FAT_DIRECTORY_ENTRY));
    177   }
    178 
    179   return Status;
    180 }
    181 
    182 /**
    183 
    184   Get the file info from the open file of the IFile into Buffer.
    185 
    186   @param FHand                 - The file handle to access.
    187   @param  IoMode                - Indicate whether the access mode is reading or writing.
    188   @param  BufferSize            - Size of Buffer.
    189   @param  Buffer                - Buffer containing read data.
    190   @param  Token                 - A pointer to the token associated with the transaction.
    191 
    192   @retval EFI_SUCCESS           - Get the file info successfully.
    193   @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
    194   @retval EFI_VOLUME_CORRUPTED  - The file type of open file is error.
    195   @retval EFI_WRITE_PROTECTED   - The disk is write protect.
    196   @retval EFI_ACCESS_DENIED     - The file is read-only.
    197   @return other                 - An error occurred when operating on the disk.
    198 
    199 **/
    200 EFI_STATUS
    201 FatIFileAccess (
    202   IN     EFI_FILE_PROTOCOL     *FHand,
    203   IN     IO_MODE               IoMode,
    204   IN OUT UINTN                 *BufferSize,
    205   IN OUT VOID                  *Buffer,
    206   IN     EFI_FILE_IO_TOKEN     *Token
    207   )
    208 {
    209   EFI_STATUS  Status;
    210   FAT_IFILE   *IFile;
    211   FAT_OFILE   *OFile;
    212   FAT_VOLUME  *Volume;
    213   UINT64      EndPosition;
    214   FAT_TASK    *Task;
    215 
    216   IFile  = IFILE_FROM_FHAND (FHand);
    217   OFile  = IFile->OFile;
    218   Volume = OFile->Volume;
    219   Task   = NULL;
    220 
    221   //
    222   // Write to a directory is unsupported
    223   //
    224   if ((OFile->ODir != NULL) && (IoMode == WriteData)) {
    225     return EFI_UNSUPPORTED;
    226   }
    227 
    228   if (OFile->Error == EFI_NOT_FOUND) {
    229     return EFI_DEVICE_ERROR;
    230   }
    231 
    232   if (IoMode == ReadData) {
    233     //
    234     // If position is at EOF, then return device error
    235     //
    236     if (IFile->Position > OFile->FileSize) {
    237       return EFI_DEVICE_ERROR;
    238     }
    239   } else {
    240     //
    241     // Check if the we can write data
    242     //
    243     if (Volume->ReadOnly) {
    244       return EFI_WRITE_PROTECTED;
    245     }
    246 
    247     if (IFile->ReadOnly) {
    248       return EFI_ACCESS_DENIED;
    249     }
    250   }
    251 
    252   if (Token == NULL) {
    253     FatWaitNonblockingTask (IFile);
    254   } else {
    255     //
    256     // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2.
    257     // But if it calls, the below check can avoid crash.
    258     //
    259     if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) {
    260       return EFI_UNSUPPORTED;
    261     }
    262     Task = FatCreateTask (IFile, Token);
    263     if (Task == NULL) {
    264       return EFI_OUT_OF_RESOURCES;
    265     }
    266   }
    267 
    268   FatAcquireLock ();
    269 
    270   Status = OFile->Error;
    271   if (!EFI_ERROR (Status)) {
    272     if (OFile->ODir != NULL) {
    273       //
    274       // Read a directory is supported
    275       //
    276       ASSERT (IoMode == ReadData);
    277       Status = FatIFileReadDir (IFile, BufferSize, Buffer);
    278       OFile = NULL;
    279     } else {
    280       //
    281       // Access a file
    282       //
    283       EndPosition = IFile->Position + *BufferSize;
    284       if (EndPosition > OFile->FileSize) {
    285         //
    286         // The position goes beyond the end of file
    287         //
    288         if (IoMode == ReadData) {
    289           //
    290           // Adjust the actual size read
    291           //
    292           *BufferSize -= (UINTN) EndPosition - OFile->FileSize;
    293         } else {
    294           //
    295           // We expand the file size of OFile
    296           //
    297           Status = FatGrowEof (OFile, EndPosition);
    298           if (EFI_ERROR (Status)) {
    299             //
    300             // Must update the file's info into the file's Directory Entry
    301             // and then flush the dirty cache info into disk.
    302             //
    303             *BufferSize = 0;
    304             FatOFileFlush (OFile);
    305             OFile = NULL;
    306             goto Done;
    307           }
    308 
    309           FatUpdateDirEntClusterSizeInfo (OFile);
    310         }
    311       }
    312 
    313       Status = FatAccessOFile (OFile, IoMode, (UINTN) IFile->Position, BufferSize, Buffer, Task);
    314       IFile->Position += *BufferSize;
    315     }
    316   }
    317 
    318   if (Token != NULL) {
    319     if (!EFI_ERROR (Status)) {
    320       Status = FatQueueTask (IFile, Task);
    321     } else {
    322       FatDestroyTask (Task);
    323     }
    324   }
    325 
    326 Done:
    327   //
    328   // On EFI_SUCCESS case, not calling FatCleanupVolume():
    329   // 1) The Cache flush operation is avoided to enhance
    330   // performance. Caller is responsible to call Flush() when necessary.
    331   // 2) The volume dirty bit is probably set already, and is expected to be
    332   // cleaned in subsequent Flush() or other operations.
    333   // 3) Write operation doesn't affect OFile/IFile structure, so
    334   // Reference checking is not necessary.
    335   //
    336   if (EFI_ERROR (Status)) {
    337     Status = FatCleanupVolume (Volume, OFile, Status, NULL);
    338   }
    339 
    340   FatReleaseLock ();
    341   return Status;
    342 }
    343 
    344 /**
    345 
    346   Get the file info.
    347 
    348   @param  FHand                 - The handle of the file.
    349   @param  BufferSize            - Size of Buffer.
    350   @param  Buffer                - Buffer containing read data.
    351 
    352 
    353   @retval EFI_SUCCESS           - Get the file info successfully.
    354   @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
    355   @retval EFI_VOLUME_CORRUPTED  - The file type of open file is error.
    356   @return other                 - An error occurred when operation the disk.
    357 
    358 **/
    359 EFI_STATUS
    360 EFIAPI
    361 FatRead (
    362   IN     EFI_FILE_PROTOCOL  *FHand,
    363   IN OUT UINTN              *BufferSize,
    364      OUT VOID               *Buffer
    365   )
    366 {
    367   return FatIFileAccess (FHand, ReadData, BufferSize, Buffer, NULL);
    368 }
    369 
    370 /**
    371 
    372   Get the file info.
    373 
    374   @param  FHand                 - The handle of the file.
    375   @param  Token                 - A pointer to the token associated with the transaction.
    376 
    377   @retval EFI_SUCCESS           - Get the file info successfully.
    378   @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
    379   @retval EFI_VOLUME_CORRUPTED  - The file type of open file is error.
    380   @return other                 - An error occurred when operation the disk.
    381 
    382 **/
    383 EFI_STATUS
    384 EFIAPI
    385 FatReadEx (
    386   IN     EFI_FILE_PROTOCOL  *FHand,
    387   IN OUT EFI_FILE_IO_TOKEN  *Token
    388   )
    389 {
    390   return FatIFileAccess (FHand, ReadData, &Token->BufferSize, Token->Buffer, Token);
    391 }
    392 
    393 /**
    394 
    395   Write the content of buffer into files.
    396 
    397   @param  FHand                 - The handle of the file.
    398   @param  BufferSize            - Size of Buffer.
    399   @param  Buffer                - Buffer containing write data.
    400 
    401   @retval EFI_SUCCESS           - Set the file info successfully.
    402   @retval EFI_WRITE_PROTECTED   - The disk is write protect.
    403   @retval EFI_ACCESS_DENIED     - The file is read-only.
    404   @retval EFI_DEVICE_ERROR      - The OFile is not valid.
    405   @retval EFI_UNSUPPORTED       - The open file is not a file.
    406                         - The writing file size is larger than 4GB.
    407   @return other                 - An error occurred when operation the disk.
    408 
    409 **/
    410 EFI_STATUS
    411 EFIAPI
    412 FatWrite (
    413   IN     EFI_FILE_PROTOCOL  *FHand,
    414   IN OUT UINTN              *BufferSize,
    415   IN     VOID               *Buffer
    416   )
    417 {
    418   return FatIFileAccess (FHand, WriteData, BufferSize, Buffer, NULL);
    419 }
    420 
    421 /**
    422 
    423   Get the file info.
    424 
    425   @param  FHand                 - The handle of the file.
    426   @param  Token                 - A pointer to the token associated with the transaction.
    427 
    428   @retval EFI_SUCCESS           - Get the file info successfully.
    429   @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
    430   @retval EFI_VOLUME_CORRUPTED  - The file type of open file is error.
    431   @return other                 - An error occurred when operation the disk.
    432 
    433 **/
    434 EFI_STATUS
    435 EFIAPI
    436 FatWriteEx (
    437   IN     EFI_FILE_PROTOCOL  *FHand,
    438   IN OUT EFI_FILE_IO_TOKEN  *Token
    439   )
    440 {
    441   return FatIFileAccess (FHand, WriteData, &Token->BufferSize, Token->Buffer, Token);
    442 }
    443 
    444 /**
    445 
    446   This function reads data from a file or writes data to a file.
    447   It uses OFile->PosRem to determine how much data can be accessed in one time.
    448 
    449   @param  OFile                 - The open file.
    450   @param  IoMode                - Indicate whether the access mode is reading or writing.
    451   @param  Position              - The position where data will be accessed.
    452   @param  DataBufferSize        - Size of Buffer.
    453   @param  UserBuffer            - Buffer containing data.
    454   @param  Task                    point to task instance.
    455 
    456   @retval EFI_SUCCESS           - Access the data successfully.
    457   @return other                 - An error occurred when operating on the disk.
    458 
    459 **/
    460 EFI_STATUS
    461 FatAccessOFile (
    462   IN     FAT_OFILE      *OFile,
    463   IN     IO_MODE        IoMode,
    464   IN     UINTN          Position,
    465   IN OUT UINTN          *DataBufferSize,
    466   IN OUT UINT8          *UserBuffer,
    467   IN FAT_TASK           *Task
    468   )
    469 {
    470   FAT_VOLUME  *Volume;
    471   UINTN       Len;
    472   EFI_STATUS  Status;
    473   UINTN       BufferSize;
    474 
    475   BufferSize  = *DataBufferSize;
    476   Volume      = OFile->Volume;
    477   ASSERT_VOLUME_LOCKED (Volume);
    478 
    479   Status = EFI_SUCCESS;
    480   while (BufferSize > 0) {
    481     //
    482     // Seek the OFile to the file position
    483     //
    484     Status = FatOFilePosition (OFile, Position, BufferSize);
    485     if (EFI_ERROR (Status)) {
    486       break;
    487     }
    488     //
    489     // Clip length to block run
    490     //
    491     Len = BufferSize > OFile->PosRem ? OFile->PosRem : BufferSize;
    492 
    493     //
    494     // Write the data
    495     //
    496     Status = FatDiskIo (Volume, IoMode, OFile->PosDisk, Len, UserBuffer, Task);
    497     if (EFI_ERROR (Status)) {
    498       break;
    499     }
    500     //
    501     // Data was successfully accessed
    502     //
    503     Position   += Len;
    504     UserBuffer += Len;
    505     BufferSize -= Len;
    506     if (IoMode == WriteData) {
    507       OFile->Dirty    = TRUE;
    508       OFile->Archive  = TRUE;
    509     }
    510     //
    511     // Make sure no outbound occurred
    512     //
    513     ASSERT (Position <= OFile->FileSize);
    514   }
    515   //
    516   // Update the number of bytes accessed
    517   //
    518   *DataBufferSize -= BufferSize;
    519   return Status;
    520 }
    521 
    522 /**
    523 
    524   Expand OFile by appending zero bytes at the end of OFile.
    525 
    526   @param  OFile                 - The open file.
    527   @param  ExpandedSize          - The number of zero bytes appended at the end of the file.
    528 
    529   @retval EFI_SUCCESS           - The file is expanded successfully.
    530   @return other                 - An error occurred when expanding file.
    531 
    532 **/
    533 EFI_STATUS
    534 FatExpandOFile (
    535   IN FAT_OFILE          *OFile,
    536   IN UINT64             ExpandedSize
    537   )
    538 {
    539   EFI_STATUS  Status;
    540   UINTN       WritePos;
    541 
    542   WritePos  = OFile->FileSize;
    543   Status    = FatGrowEof (OFile, ExpandedSize);
    544   if (!EFI_ERROR (Status)) {
    545     Status = FatWriteZeroPool (OFile, WritePos);
    546   }
    547 
    548   return Status;
    549 }
    550 
    551 /**
    552 
    553   Write zero pool from the WritePos to the end of OFile.
    554 
    555   @param  OFile                 - The open file to write zero pool.
    556   @param  WritePos              - The number of zero bytes written.
    557 
    558   @retval EFI_SUCCESS           - Write the zero pool successfully.
    559   @retval EFI_OUT_OF_RESOURCES  - Not enough memory to perform the operation.
    560   @return other                 - An error occurred when writing disk.
    561 
    562 **/
    563 EFI_STATUS
    564 FatWriteZeroPool (
    565   IN FAT_OFILE  *OFile,
    566   IN UINTN      WritePos
    567   )
    568 {
    569   EFI_STATUS  Status;
    570   VOID        *ZeroBuffer;
    571   UINTN       AppendedSize;
    572   UINTN       BufferSize;
    573   UINTN       WriteSize;
    574 
    575   AppendedSize  = OFile->FileSize - WritePos;
    576   BufferSize    = AppendedSize;
    577   if (AppendedSize > FAT_MAX_ALLOCATE_SIZE) {
    578     //
    579     // If the appended size is larger, maybe we can not allocate the whole
    580     // memory once. So if the growed size is larger than 10M, we just
    581     // allocate 10M memory (one healthy system should have 10M available
    582     // memory), and then write the zerobuffer to the file several times.
    583     //
    584     BufferSize = FAT_MAX_ALLOCATE_SIZE;
    585   }
    586 
    587   ZeroBuffer = AllocateZeroPool (BufferSize);
    588   if (ZeroBuffer == NULL) {
    589     return EFI_OUT_OF_RESOURCES;
    590   }
    591 
    592   do {
    593     WriteSize     = AppendedSize > BufferSize ? BufferSize : (UINTN) AppendedSize;
    594     AppendedSize -= WriteSize;
    595     Status = FatAccessOFile (OFile, WriteData, WritePos, &WriteSize, ZeroBuffer, NULL);
    596     if (EFI_ERROR (Status)) {
    597       break;
    598     }
    599 
    600     WritePos += WriteSize;
    601   } while (AppendedSize > 0);
    602 
    603   FreePool (ZeroBuffer);
    604   return Status;
    605 }
    606 
    607 /**
    608 
    609   Truncate the OFile to smaller file size.
    610 
    611   @param  OFile                 - The open file.
    612   @param  TruncatedSize         - The new file size.
    613 
    614   @retval EFI_SUCCESS           - The file is truncated successfully.
    615   @return other                 - An error occurred when truncating file.
    616 
    617 **/
    618 EFI_STATUS
    619 FatTruncateOFile (
    620   IN FAT_OFILE          *OFile,
    621   IN UINTN              TruncatedSize
    622   )
    623 {
    624   OFile->FileSize = TruncatedSize;
    625   return FatShrinkEof (OFile);
    626 }
    627