Home | History | Annotate | Download | only in EnhancedFatDxe
      1 /** @file
      2   Routines that check references and flush OFiles
      3 
      4 Copyright (c) 2005 - 2013, 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   Flushes all data associated with the file handle.
     21 
     22   @param  FHand                 - Handle to file to flush.
     23   @param  Token                 - A pointer to the token associated with the transaction.
     24 
     25   @retval EFI_SUCCESS           - Flushed the file successfully.
     26   @retval EFI_WRITE_PROTECTED   - The volume is read only.
     27   @retval EFI_ACCESS_DENIED     - The file is read only.
     28   @return Others                - Flushing of the file failed.
     29 
     30 **/
     31 EFI_STATUS
     32 EFIAPI
     33 FatFlushEx (
     34   IN EFI_FILE_PROTOCOL  *FHand,
     35   IN EFI_FILE_IO_TOKEN  *Token
     36   )
     37 {
     38   FAT_IFILE   *IFile;
     39   FAT_OFILE   *OFile;
     40   FAT_VOLUME  *Volume;
     41   EFI_STATUS  Status;
     42   FAT_TASK    *Task;
     43 
     44   IFile   = IFILE_FROM_FHAND (FHand);
     45   OFile   = IFile->OFile;
     46   Volume  = OFile->Volume;
     47   Task    = NULL;
     48 
     49   //
     50   // If the file has a permanent error, return it
     51   //
     52   if (EFI_ERROR (OFile->Error)) {
     53     return OFile->Error;
     54   }
     55 
     56   if (Volume->ReadOnly) {
     57     return EFI_WRITE_PROTECTED;
     58   }
     59   //
     60   // If read only, return error
     61   //
     62   if (IFile->ReadOnly) {
     63     return EFI_ACCESS_DENIED;
     64   }
     65 
     66   if (Token == NULL) {
     67     FatWaitNonblockingTask (IFile);
     68   } else {
     69     //
     70     // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2.
     71     // But if it calls, the below check can avoid crash.
     72     //
     73     if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) {
     74       return EFI_UNSUPPORTED;
     75     }
     76     Task = FatCreateTask (IFile, Token);
     77     if (Task == NULL) {
     78       return EFI_OUT_OF_RESOURCES;
     79     }
     80   }
     81 
     82   //
     83   // Flush the OFile
     84   //
     85   FatAcquireLock ();
     86   Status  = FatOFileFlush (OFile);
     87   Status  = FatCleanupVolume (OFile->Volume, OFile, Status, Task);
     88   FatReleaseLock ();
     89 
     90   if (Token != NULL) {
     91     if (!EFI_ERROR (Status)) {
     92       Status = FatQueueTask (IFile, Task);
     93     } else {
     94       FatDestroyTask (Task);
     95     }
     96   }
     97 
     98   return Status;
     99 }
    100 
    101 /**
    102 
    103   Flushes all data associated with the file handle.
    104 
    105   @param  FHand                 - Handle to file to flush.
    106 
    107   @retval EFI_SUCCESS           - Flushed the file successfully.
    108   @retval EFI_WRITE_PROTECTED   - The volume is read only.
    109   @retval EFI_ACCESS_DENIED     - The file is read only.
    110   @return Others                - Flushing of the file failed.
    111 
    112 **/
    113 EFI_STATUS
    114 EFIAPI
    115 FatFlush (
    116   IN EFI_FILE_PROTOCOL  *FHand
    117   )
    118 {
    119   return FatFlushEx (FHand, NULL);
    120 }
    121 
    122 /**
    123 
    124   Flushes & Closes the file handle.
    125 
    126   @param  FHand                 - Handle to the file to delete.
    127 
    128   @retval EFI_SUCCESS           - Closed the file successfully.
    129 
    130 **/
    131 EFI_STATUS
    132 EFIAPI
    133 FatClose (
    134   IN EFI_FILE_PROTOCOL  *FHand
    135   )
    136 {
    137   FAT_IFILE   *IFile;
    138   FAT_OFILE   *OFile;
    139   FAT_VOLUME  *Volume;
    140 
    141   IFile   = IFILE_FROM_FHAND (FHand);
    142   OFile   = IFile->OFile;
    143   Volume  = OFile->Volume;
    144 
    145   //
    146   // Lock the volume
    147   //
    148   FatAcquireLock ();
    149 
    150   //
    151   // Close the file instance handle
    152   //
    153   FatIFileClose (IFile);
    154 
    155   //
    156   // Done. Unlock the volume
    157   //
    158   FatCleanupVolume (Volume, OFile, EFI_SUCCESS, NULL);
    159   FatReleaseLock ();
    160 
    161   //
    162   // Close always succeed
    163   //
    164   return EFI_SUCCESS;
    165 }
    166 
    167 /**
    168 
    169   Close the open file instance.
    170 
    171   @param  IFile                 - Open file instance.
    172 
    173   @retval EFI_SUCCESS           - Closed the file successfully.
    174 
    175 **/
    176 EFI_STATUS
    177 FatIFileClose (
    178   FAT_IFILE           *IFile
    179   )
    180 {
    181   FAT_OFILE   *OFile;
    182   FAT_VOLUME  *Volume;
    183 
    184   OFile   = IFile->OFile;
    185   Volume  = OFile->Volume;
    186 
    187   ASSERT_VOLUME_LOCKED (Volume);
    188 
    189   FatWaitNonblockingTask (IFile);
    190 
    191   //
    192   // Remove the IFile struct
    193   //
    194   RemoveEntryList (&IFile->Link);
    195 
    196   //
    197   // Add the OFile to the check reference list
    198   //
    199   if (OFile->CheckLink.ForwardLink == NULL) {
    200     InsertHeadList (&Volume->CheckRef, &OFile->CheckLink);
    201   }
    202   //
    203   // Done. Free the open instance structure
    204   //
    205   FreePool (IFile);
    206   return EFI_SUCCESS;
    207 }
    208 
    209 /**
    210 
    211   Flush the data associated with an open file.
    212   In this implementation, only last Mod/Access time is updated.
    213 
    214   @param  OFile                 - The open file.
    215 
    216   @retval EFI_SUCCESS           - The OFile is flushed successfully.
    217   @return Others                - An error occurred when flushing this OFile.
    218 
    219 **/
    220 EFI_STATUS
    221 FatOFileFlush (
    222   IN FAT_OFILE    *OFile
    223   )
    224 {
    225   EFI_STATUS    Status;
    226   FAT_OFILE     *Parent;
    227   FAT_DIRENT    *DirEnt;
    228   FAT_DATE_TIME FatNow;
    229 
    230   //
    231   // Flush each entry up the tree while dirty
    232   //
    233   do {
    234     //
    235     // If the file has a permanant error, then don't write any
    236     // of its data to the device (may be from different media)
    237     //
    238     if (EFI_ERROR (OFile->Error)) {
    239       return OFile->Error;
    240     }
    241 
    242     Parent  = OFile->Parent;
    243     DirEnt  = OFile->DirEnt;
    244     if (OFile->Dirty) {
    245       //
    246       // Update the last modification time
    247       //
    248       FatGetCurrentFatTime (&FatNow);
    249       CopyMem (&DirEnt->Entry.FileLastAccess, &FatNow.Date, sizeof (FAT_DATE));
    250       if (!OFile->PreserveLastModification) {
    251         FatGetCurrentFatTime (&DirEnt->Entry.FileModificationTime);
    252       }
    253 
    254       OFile->PreserveLastModification = FALSE;
    255       if (OFile->Archive) {
    256         DirEnt->Entry.Attributes |= FAT_ATTRIBUTE_ARCHIVE;
    257         OFile->Archive = FALSE;
    258       }
    259       //
    260       // Write the directory entry
    261       //
    262       if (Parent != NULL && !DirEnt->Invalid) {
    263         //
    264         // Write the OFile's directory entry
    265         //
    266         Status = FatStoreDirEnt (Parent, DirEnt);
    267         if (EFI_ERROR (Status)) {
    268           return Status;
    269         }
    270       }
    271 
    272       OFile->Dirty = FALSE;
    273     }
    274     //
    275     // Check the parent
    276     //
    277     OFile = Parent;
    278   } while (OFile != NULL);
    279   return EFI_SUCCESS;
    280 }
    281 
    282 /**
    283 
    284   Check the references of the OFile.
    285   If the OFile (that is checked) is no longer
    286   referenced, then it is freed.
    287 
    288   @param  OFile                 - The OFile to be checked.
    289 
    290   @retval TRUE                  - The OFile is not referenced and freed.
    291   @retval FALSE                 - The OFile is kept.
    292 
    293 **/
    294 BOOLEAN
    295 FatCheckOFileRef (
    296   IN FAT_OFILE   *OFile
    297   )
    298 {
    299   //
    300   // If the OFile is on the check ref list, remove it
    301   //
    302   if (OFile->CheckLink.ForwardLink != NULL) {
    303     RemoveEntryList (&OFile->CheckLink);
    304     OFile->CheckLink.ForwardLink = NULL;
    305   }
    306 
    307   FatOFileFlush (OFile);
    308   //
    309   // Are there any references to this OFile?
    310   //
    311   if (!IsListEmpty (&OFile->Opens) || !IsListEmpty (&OFile->ChildHead)) {
    312     //
    313     // The OFile cannot be freed
    314     //
    315     return FALSE;
    316   }
    317   //
    318   // Free the Ofile
    319   //
    320   FatCloseDirEnt (OFile->DirEnt);
    321   return TRUE;
    322 }
    323 
    324 /**
    325 
    326   Check the references of all open files on the volume.
    327   Any open file (that is checked) that is no longer
    328   referenced, is freed - and it's parent open file
    329   is then referenced checked.
    330 
    331   @param  Volume                - The volume to check the pending open file list.
    332 
    333 **/
    334 STATIC
    335 VOID
    336 FatCheckVolumeRef (
    337   IN FAT_VOLUME   *Volume
    338   )
    339 {
    340   FAT_OFILE *OFile;
    341   FAT_OFILE *Parent;
    342 
    343   //
    344   // Check all files on the pending check list
    345   //
    346   while (!IsListEmpty (&Volume->CheckRef)) {
    347     //
    348     // Start with the first file listed
    349     //
    350     Parent = OFILE_FROM_CHECKLINK (Volume->CheckRef.ForwardLink);
    351     //
    352     // Go up the tree cleaning up any un-referenced OFiles
    353     //
    354     while (Parent != NULL) {
    355       OFile   = Parent;
    356       Parent  = OFile->Parent;
    357       if (!FatCheckOFileRef (OFile)) {
    358         break;
    359       }
    360     }
    361   }
    362 }
    363 
    364 /**
    365 
    366   Set error status for a specific OFile, reference checking the volume.
    367   If volume is already marked as invalid, and all resources are freed
    368   after reference checking, the file system protocol is uninstalled and
    369   the volume structure is freed.
    370 
    371   @param  Volume                - the Volume that is to be reference checked and unlocked.
    372   @param  OFile                 - the OFile whose permanent error code is to be set.
    373   @param  EfiStatus             - error code to be set.
    374   @param  Task                    point to task instance.
    375 
    376   @retval EFI_SUCCESS           - Clean up the volume successfully.
    377   @return Others                - Cleaning up of the volume is failed.
    378 
    379 **/
    380 EFI_STATUS
    381 FatCleanupVolume (
    382   IN FAT_VOLUME       *Volume,
    383   IN FAT_OFILE        *OFile,
    384   IN EFI_STATUS       EfiStatus,
    385   IN FAT_TASK         *Task
    386   )
    387 {
    388   EFI_STATUS  Status;
    389   //
    390   // Flag the OFile
    391   //
    392   if (OFile != NULL) {
    393     FatSetVolumeError (OFile, EfiStatus);
    394   }
    395   //
    396   // Clean up any dangling OFiles that don't have IFiles
    397   // we don't check return status here because we want the
    398   // volume be cleaned up even the volume is invalid.
    399   //
    400   FatCheckVolumeRef (Volume);
    401   if (Volume->Valid) {
    402     //
    403     // Update the free hint info. Volume->FreeInfoPos != 0
    404     // indicates this a FAT32 volume
    405     //
    406     if (Volume->FreeInfoValid && Volume->FatDirty && Volume->FreeInfoPos) {
    407       Status = FatDiskIo (Volume, WriteDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, Task);
    408       if (EFI_ERROR (Status)) {
    409         return Status;
    410       }
    411     }
    412     //
    413     // Update that the volume is not dirty
    414     //
    415     if (Volume->FatDirty && Volume->FatType != Fat12) {
    416       Volume->FatDirty  = FALSE;
    417       Status            = FatAccessVolumeDirty (Volume, WriteFat, &Volume->NotDirtyValue);
    418       if (EFI_ERROR (Status)) {
    419         return Status;
    420       }
    421     }
    422     //
    423     // Flush all dirty cache entries to disk
    424     //
    425     Status = FatVolumeFlushCache (Volume, Task);
    426     if (EFI_ERROR (Status)) {
    427       return Status;
    428     }
    429   }
    430   //
    431   // If the volume is cleared , remove it.
    432   // The only time volume be invalidated is in DriverBindingStop.
    433   //
    434   if (Volume->Root == NULL && !Volume->Valid) {
    435     //
    436     // Free the volume structure
    437     //
    438     FatFreeVolume (Volume);
    439   }
    440 
    441   return EfiStatus;
    442 }
    443 
    444 /**
    445 
    446   Set the OFile and its child OFile with the error Status
    447 
    448   @param  OFile                 - The OFile whose permanent error code is to be set.
    449   @param  Status                - Error code to be set.
    450 
    451 **/
    452 VOID
    453 FatSetVolumeError (
    454   IN FAT_OFILE            *OFile,
    455   IN EFI_STATUS           Status
    456   )
    457 {
    458   LIST_ENTRY      *Link;
    459   FAT_OFILE       *ChildOFile;
    460 
    461   //
    462   // If this OFile doesn't already have an error, set one
    463   //
    464   if (!EFI_ERROR (OFile->Error)) {
    465     OFile->Error = Status;
    466   }
    467   //
    468   // Set the error on each child OFile
    469   //
    470   for (Link = OFile->ChildHead.ForwardLink; Link != &OFile->ChildHead; Link = Link->ForwardLink) {
    471     ChildOFile = OFILE_FROM_CHILDLINK (Link);
    472     FatSetVolumeError (ChildOFile, Status);
    473   }
    474 }
    475