Home | History | Annotate | Download | only in EnhancedFatDxe
      1 /** @file
      2   Miscellaneous functions.
      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 UINT8  mMonthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
     18 
     19 /**
     20 
     21   Create the task
     22 
     23   @param  IFile                 - The instance of the open file.
     24   @param  Token                 - A pointer to the token associated with the transaction.
     25 
     26   @return FAT_TASK *            - Return the task instance.
     27 
     28 **/
     29 FAT_TASK *
     30 FatCreateTask (
     31   FAT_IFILE           *IFile,
     32   EFI_FILE_IO_TOKEN   *Token
     33   )
     34 {
     35   FAT_TASK            *Task;
     36 
     37   Task = AllocateZeroPool (sizeof (*Task));
     38   if (Task != NULL) {
     39     Task->Signature   = FAT_TASK_SIGNATURE;
     40     Task->IFile       = IFile;
     41     Task->FileIoToken = Token;
     42     InitializeListHead (&Task->Subtasks);
     43     InitializeListHead (&Task->Link);
     44   }
     45   return Task;
     46 }
     47 
     48 /**
     49 
     50   Destroy the task.
     51 
     52   @param  Task                  - The task to be destroyed.
     53 
     54 **/
     55 VOID
     56 FatDestroyTask (
     57   FAT_TASK            *Task
     58   )
     59 {
     60   LIST_ENTRY          *Link;
     61   FAT_SUBTASK         *Subtask;
     62 
     63   Link = GetFirstNode (&Task->Subtasks);
     64   while (!IsNull (&Task->Subtasks, Link)) {
     65     Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);
     66     Link = FatDestroySubtask (Subtask);
     67   }
     68   FreePool (Task);
     69 }
     70 
     71 /**
     72 
     73   Wait all non-blocking requests complete.
     74 
     75   @param  IFile                 - The instance of the open file.
     76 
     77 **/
     78 VOID
     79 FatWaitNonblockingTask (
     80   FAT_IFILE           *IFile
     81   )
     82 {
     83   BOOLEAN             TaskQueueEmpty;
     84 
     85   do {
     86     EfiAcquireLock (&FatTaskLock);
     87     TaskQueueEmpty = IsListEmpty (&IFile->Tasks);
     88     EfiReleaseLock (&FatTaskLock);
     89   } while (!TaskQueueEmpty);
     90 }
     91 
     92 /**
     93 
     94   Remove the subtask from subtask list.
     95 
     96   @param  Subtask               - The subtask to be removed.
     97 
     98   @return LIST_ENTRY *          - The next node in the list.
     99 
    100 **/
    101 LIST_ENTRY *
    102 FatDestroySubtask (
    103   FAT_SUBTASK         *Subtask
    104   )
    105 {
    106   LIST_ENTRY          *Link;
    107 
    108   gBS->CloseEvent (Subtask->DiskIo2Token.Event);
    109 
    110   Link = RemoveEntryList (&Subtask->Link);
    111   FreePool (Subtask);
    112 
    113   return Link;
    114 }
    115 
    116 /**
    117 
    118   Execute the task.
    119 
    120   @param  IFile                 - The instance of the open file.
    121   @param  Task                  - The task to be executed.
    122 
    123   @retval EFI_SUCCESS           - The task was executed sucessfully.
    124   @return other                 - An error occurred when executing the task.
    125 
    126 **/
    127 EFI_STATUS
    128 FatQueueTask (
    129   IN FAT_IFILE        *IFile,
    130   IN FAT_TASK         *Task
    131   )
    132 {
    133   EFI_STATUS          Status;
    134   LIST_ENTRY          *Link;
    135   FAT_SUBTASK         *Subtask;
    136 
    137   //
    138   // Sometimes the Task doesn't contain any subtasks, signal the event directly.
    139   //
    140   if (IsListEmpty (&Task->Subtasks)) {
    141     Task->FileIoToken->Status = EFI_SUCCESS;
    142     gBS->SignalEvent (Task->FileIoToken->Event);
    143     FreePool (Task);
    144     return EFI_SUCCESS;
    145   }
    146 
    147   EfiAcquireLock (&FatTaskLock);
    148   InsertTailList (&IFile->Tasks, &Task->Link);
    149   EfiReleaseLock (&FatTaskLock);
    150 
    151   Status = EFI_SUCCESS;
    152   for ( Link = GetFirstNode (&Task->Subtasks)
    153       ; !IsNull (&Task->Subtasks, Link)
    154       ; Link = GetNextNode (&Task->Subtasks, Link)
    155       ) {
    156     Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);
    157     if (Subtask->Write) {
    158 
    159       Status = IFile->OFile->Volume->DiskIo2->WriteDiskEx (
    160                                                 IFile->OFile->Volume->DiskIo2,
    161                                                 IFile->OFile->Volume->MediaId,
    162                                                 Subtask->Offset,
    163                                                 &Subtask->DiskIo2Token,
    164                                                 Subtask->BufferSize,
    165                                                 Subtask->Buffer
    166                                                 );
    167     } else {
    168       Status = IFile->OFile->Volume->DiskIo2->ReadDiskEx (
    169                                                 IFile->OFile->Volume->DiskIo2,
    170                                                 IFile->OFile->Volume->MediaId,
    171                                                 Subtask->Offset,
    172                                                 &Subtask->DiskIo2Token,
    173                                                 Subtask->BufferSize,
    174                                                 Subtask->Buffer
    175                                                 );
    176     }
    177     if (EFI_ERROR (Status)) {
    178       break;
    179     }
    180   }
    181 
    182   if (EFI_ERROR (Status)) {
    183     EfiAcquireLock (&FatTaskLock);
    184     //
    185     // Remove all the remaining subtasks when failure.
    186     // We shouldn't remove all the tasks because the non-blocking requests have
    187     // been submitted and cannot be canceled.
    188     //
    189     while (!IsNull (&Task->Subtasks, Link)) {
    190       Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);
    191       Link = FatDestroySubtask (Subtask);
    192     }
    193 
    194     if (IsListEmpty (&Task->Subtasks)) {
    195       RemoveEntryList (&Task->Link);
    196       FreePool (Task);
    197     } else {
    198       //
    199       // If one or more subtasks have been already submitted, set FileIoToken
    200       // to NULL so that the callback won't signal the event.
    201       //
    202       Task->FileIoToken = NULL;
    203     }
    204 
    205     EfiReleaseLock (&FatTaskLock);
    206   }
    207 
    208   return Status;
    209 }
    210 
    211 /**
    212 
    213   Set the volume as dirty or not.
    214 
    215   @param  Volume                - FAT file system volume.
    216   @param  IoMode                - The access mode.
    217   @param  DirtyValue            - Set the volume as dirty or not.
    218 
    219   @retval EFI_SUCCESS           - Set the new FAT entry value sucessfully.
    220   @return other                 - An error occurred when operation the FAT entries.
    221 
    222 **/
    223 EFI_STATUS
    224 FatAccessVolumeDirty (
    225   IN FAT_VOLUME       *Volume,
    226   IN IO_MODE          IoMode,
    227   IN VOID             *DirtyValue
    228   )
    229 {
    230   UINTN WriteCount;
    231 
    232   WriteCount = Volume->FatEntrySize;
    233   return FatDiskIo (Volume, IoMode, Volume->FatPos + WriteCount, WriteCount, DirtyValue, NULL);
    234 }
    235 
    236 /**
    237   Invoke a notification event.
    238 
    239   @param  Event                 Event whose notification function is being invoked.
    240   @param  Context               The pointer to the notification function's context,
    241                                 which is implementation-dependent.
    242 
    243 **/
    244 VOID
    245 EFIAPI
    246 FatOnAccessComplete (
    247   IN  EFI_EVENT                Event,
    248   IN  VOID                     *Context
    249   )
    250 {
    251   EFI_STATUS             Status;
    252   FAT_SUBTASK            *Subtask;
    253   FAT_TASK               *Task;
    254 
    255   //
    256   // Avoid someone in future breaks the below assumption.
    257   //
    258   ASSERT (EfiGetCurrentTpl () == FatTaskLock.Tpl);
    259 
    260   Subtask = (FAT_SUBTASK *) Context;
    261   Task    = Subtask->Task;
    262   Status  = Subtask->DiskIo2Token.TransactionStatus;
    263 
    264   ASSERT (Task->Signature    == FAT_TASK_SIGNATURE);
    265   ASSERT (Subtask->Signature == FAT_SUBTASK_SIGNATURE);
    266 
    267   //
    268   // Remove the task unconditionally
    269   //
    270   FatDestroySubtask (Subtask);
    271 
    272   //
    273   // Task->FileIoToken is NULL which means the task will be ignored (just recycle the subtask and task memory).
    274   //
    275   if (Task->FileIoToken != NULL) {
    276     if (IsListEmpty (&Task->Subtasks) || EFI_ERROR (Status)) {
    277       Task->FileIoToken->Status = Status;
    278       gBS->SignalEvent (Task->FileIoToken->Event);
    279       //
    280       // Mark Task->FileIoToken to NULL so that the subtasks belonging to the task will be ignored.
    281       //
    282       Task->FileIoToken = NULL;
    283     }
    284   }
    285 
    286   if (IsListEmpty (&Task->Subtasks)) {
    287     RemoveEntryList (&Task->Link);
    288     FreePool (Task);
    289   }
    290 }
    291 
    292 /**
    293 
    294   General disk access function.
    295 
    296   @param  Volume                - FAT file system volume.
    297   @param  IoMode                - The access mode (disk read/write or cache access).
    298   @param  Offset                - The starting byte offset to read from.
    299   @param  BufferSize            - Size of Buffer.
    300   @param  Buffer                - Buffer containing read data.
    301   @param  Task                    point to task instance.
    302 
    303   @retval EFI_SUCCESS           - The operation is performed successfully.
    304   @retval EFI_VOLUME_CORRUPTED  - The accesss is
    305   @return Others                - The status of read/write the disk
    306 
    307 **/
    308 EFI_STATUS
    309 FatDiskIo (
    310   IN     FAT_VOLUME       *Volume,
    311   IN     IO_MODE          IoMode,
    312   IN     UINT64           Offset,
    313   IN     UINTN            BufferSize,
    314   IN OUT VOID             *Buffer,
    315   IN     FAT_TASK         *Task
    316   )
    317 {
    318   EFI_STATUS            Status;
    319   EFI_DISK_IO_PROTOCOL  *DiskIo;
    320   EFI_DISK_READ         IoFunction;
    321   FAT_SUBTASK           *Subtask;
    322 
    323   //
    324   // Verify the IO is in devices range
    325   //
    326   Status = EFI_VOLUME_CORRUPTED;
    327   if (Offset + BufferSize <= Volume->VolumeSize) {
    328     if (CACHE_ENABLED (IoMode)) {
    329       //
    330       // Access cache
    331       //
    332       Status = FatAccessCache (Volume, CACHE_TYPE (IoMode), RAW_ACCESS (IoMode), Offset, BufferSize, Buffer, Task);
    333     } else {
    334       //
    335       // Access disk directly
    336       //
    337       if (Task == NULL) {
    338         //
    339         // Blocking access
    340         //
    341         DiskIo      = Volume->DiskIo;
    342         IoFunction  = (IoMode == ReadDisk) ? DiskIo->ReadDisk : DiskIo->WriteDisk;
    343         Status      = IoFunction (DiskIo, Volume->MediaId, Offset, BufferSize, Buffer);
    344       } else {
    345         //
    346         // Non-blocking access
    347         //
    348         Subtask = AllocateZeroPool (sizeof (*Subtask));
    349         if (Subtask == NULL) {
    350           Status        = EFI_OUT_OF_RESOURCES;
    351         } else {
    352           Subtask->Signature  = FAT_SUBTASK_SIGNATURE;
    353           Subtask->Task       = Task;
    354           Subtask->Write      = (BOOLEAN) (IoMode == WriteDisk);
    355           Subtask->Offset     = Offset;
    356           Subtask->Buffer     = Buffer;
    357           Subtask->BufferSize = BufferSize;
    358           Status = gBS->CreateEvent (
    359                           EVT_NOTIFY_SIGNAL,
    360                           TPL_NOTIFY,
    361                           FatOnAccessComplete,
    362                           Subtask,
    363                           &Subtask->DiskIo2Token.Event
    364                           );
    365           if (!EFI_ERROR (Status)) {
    366             InsertTailList (&Task->Subtasks, &Subtask->Link);
    367           } else {
    368             FreePool (Subtask);
    369           }
    370         }
    371       }
    372     }
    373   }
    374 
    375   if (EFI_ERROR (Status)) {
    376     Volume->DiskError = TRUE;
    377     DEBUG ((EFI_D_ERROR, "FatDiskIo: error %r\n", Status));
    378   }
    379 
    380   return Status;
    381 }
    382 
    383 /**
    384 
    385   Lock the volume.
    386 
    387 **/
    388 VOID
    389 FatAcquireLock (
    390   VOID
    391   )
    392 {
    393   EfiAcquireLock (&FatFsLock);
    394 }
    395 
    396 /**
    397 
    398   Lock the volume.
    399   If the lock is already in the acquired state, then EFI_ACCESS_DENIED is returned.
    400   Otherwise, EFI_SUCCESS is returned.
    401 
    402   @retval EFI_SUCCESS           - The volume is locked.
    403   @retval EFI_ACCESS_DENIED     - The volume could not be locked because it is already locked.
    404 
    405 **/
    406 EFI_STATUS
    407 FatAcquireLockOrFail (
    408   VOID
    409   )
    410 {
    411   return EfiAcquireLockOrFail (&FatFsLock);
    412 }
    413 
    414 /**
    415 
    416   Unlock the volume.
    417 
    418 **/
    419 VOID
    420 FatReleaseLock (
    421   VOID
    422   )
    423 {
    424   EfiReleaseLock (&FatFsLock);
    425 }
    426 
    427 /**
    428 
    429   Free directory entry.
    430 
    431   @param  DirEnt                - The directory entry to be freed.
    432 
    433 **/
    434 VOID
    435 FatFreeDirEnt (
    436   IN FAT_DIRENT       *DirEnt
    437   )
    438 {
    439   if (DirEnt->FileString != NULL) {
    440     FreePool (DirEnt->FileString);
    441   }
    442 
    443   FreePool (DirEnt);
    444 }
    445 
    446 /**
    447 
    448   Free volume structure (including the contents of directory cache and disk cache).
    449 
    450   @param  Volume                - The volume structure to be freed.
    451 
    452 **/
    453 VOID
    454 FatFreeVolume (
    455   IN FAT_VOLUME       *Volume
    456   )
    457 {
    458   //
    459   // Free disk cache
    460   //
    461   if (Volume->CacheBuffer != NULL) {
    462     FreePool (Volume->CacheBuffer);
    463   }
    464   //
    465   // Free directory cache
    466   //
    467   FatCleanupODirCache (Volume);
    468   FreePool (Volume);
    469 }
    470 
    471 /**
    472 
    473   Translate EFI time to FAT time.
    474 
    475   @param  ETime                 - The time of EFI_TIME.
    476   @param  FTime                 - The time of FAT_DATE_TIME.
    477 
    478 **/
    479 VOID
    480 FatEfiTimeToFatTime (
    481   IN  EFI_TIME        *ETime,
    482   OUT FAT_DATE_TIME   *FTime
    483   )
    484 {
    485   //
    486   // ignores timezone info in source ETime
    487   //
    488   if (ETime->Year > 1980) {
    489     FTime->Date.Year = (UINT16) (ETime->Year - 1980);
    490   }
    491 
    492   if (ETime->Year >= 1980 + FAT_MAX_YEAR_FROM_1980) {
    493     FTime->Date.Year = FAT_MAX_YEAR_FROM_1980;
    494   }
    495 
    496   FTime->Date.Month         = ETime->Month;
    497   FTime->Date.Day           = ETime->Day;
    498   FTime->Time.Hour          = ETime->Hour;
    499   FTime->Time.Minute        = ETime->Minute;
    500   FTime->Time.DoubleSecond  = (UINT16) (ETime->Second / 2);
    501 }
    502 
    503 /**
    504 
    505   Translate Fat time to EFI time.
    506 
    507   @param  FTime                 - The time of FAT_DATE_TIME.
    508   @param  ETime                 - The time of EFI_TIME..
    509 
    510 **/
    511 VOID
    512 FatFatTimeToEfiTime (
    513   IN  FAT_DATE_TIME     *FTime,
    514   OUT EFI_TIME          *ETime
    515   )
    516 {
    517   ETime->Year       = (UINT16) (FTime->Date.Year + 1980);
    518   ETime->Month      = (UINT8) FTime->Date.Month;
    519   ETime->Day        = (UINT8) FTime->Date.Day;
    520   ETime->Hour       = (UINT8) FTime->Time.Hour;
    521   ETime->Minute     = (UINT8) FTime->Time.Minute;
    522   ETime->Second     = (UINT8) (FTime->Time.DoubleSecond * 2);
    523   ETime->Nanosecond = 0;
    524   ETime->TimeZone   = EFI_UNSPECIFIED_TIMEZONE;
    525   ETime->Daylight   = 0;
    526 }
    527 
    528 /**
    529 
    530   Get Current FAT time.
    531 
    532   @param  FatNow                - Current FAT time.
    533 
    534 **/
    535 VOID
    536 FatGetCurrentFatTime (
    537   OUT FAT_DATE_TIME   *FatNow
    538   )
    539 {
    540   EFI_STATUS Status;
    541   EFI_TIME   Now;
    542 
    543   Status = gRT->GetTime (&Now, NULL);
    544   if (!EFI_ERROR (Status)) {
    545     FatEfiTimeToFatTime (&Now, FatNow);
    546   } else {
    547     ZeroMem (&Now, sizeof (EFI_TIME));
    548     Now.Year = 1980;
    549     Now.Month = 1;
    550     Now.Day = 1;
    551     FatEfiTimeToFatTime (&Now, FatNow);
    552   }
    553 }
    554 
    555 /**
    556 
    557   Check whether a time is valid.
    558 
    559   @param  Time                  - The time of EFI_TIME.
    560 
    561   @retval TRUE                  - The time is valid.
    562   @retval FALSE                 - The time is not valid.
    563 
    564 **/
    565 BOOLEAN
    566 FatIsValidTime (
    567   IN EFI_TIME         *Time
    568   )
    569 {
    570   UINTN         Day;
    571   BOOLEAN       ValidTime;
    572 
    573   ValidTime = TRUE;
    574 
    575   //
    576   // Check the fields for range problems
    577   // Fat can only support from 1980
    578   //
    579   if (Time->Year < 1980 ||
    580       Time->Month < 1 ||
    581       Time->Month > 12 ||
    582       Time->Day < 1 ||
    583       Time->Day > 31 ||
    584       Time->Hour > 23 ||
    585       Time->Minute > 59 ||
    586       Time->Second > 59 ||
    587       Time->Nanosecond > 999999999
    588       ) {
    589 
    590     ValidTime = FALSE;
    591 
    592   } else {
    593     //
    594     // Perform a more specific check of the day of the month
    595     //
    596     Day = mMonthDays[Time->Month - 1];
    597     if (Time->Month == 2 && IS_LEAP_YEAR (Time->Year)) {
    598       Day += 1;
    599       //
    600       // 1 extra day this month
    601       //
    602     }
    603     if (Time->Day > Day) {
    604       ValidTime = FALSE;
    605     }
    606   }
    607 
    608   return ValidTime;
    609 }
    610