• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
FatCreateTask(FAT_IFILE * IFile,EFI_FILE_IO_TOKEN * Token)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
FatDestroyTask(FAT_TASK * Task)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
FatWaitNonblockingTask(FAT_IFILE * IFile)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 *
FatDestroySubtask(FAT_SUBTASK * Subtask)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
FatQueueTask(IN FAT_IFILE * IFile,IN FAT_TASK * Task)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
FatAccessVolumeDirty(IN FAT_VOLUME * Volume,IN IO_MODE IoMode,IN VOID * DirtyValue)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
FatOnAccessComplete(IN EFI_EVENT Event,IN VOID * Context)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
FatDiskIo(IN FAT_VOLUME * Volume,IN IO_MODE IoMode,IN UINT64 Offset,IN UINTN BufferSize,IN OUT VOID * Buffer,IN FAT_TASK * Task)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
FatAcquireLock(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
FatAcquireLockOrFail(VOID)407 FatAcquireLockOrFail (
408   VOID
409   )
410 {
411   return EfiAcquireLockOrFail (&FatFsLock);
412 }
413 
414 /**
415 
416   Unlock the volume.
417 
418 **/
419 VOID
FatReleaseLock(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
FatFreeDirEnt(IN FAT_DIRENT * DirEnt)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
FatFreeVolume(IN FAT_VOLUME * Volume)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
FatEfiTimeToFatTime(IN EFI_TIME * ETime,OUT FAT_DATE_TIME * FTime)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
FatFatTimeToEfiTime(IN FAT_DATE_TIME * FTime,OUT EFI_TIME * ETime)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
FatGetCurrentFatTime(OUT FAT_DATE_TIME * FatNow)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
FatIsValidTime(IN EFI_TIME * Time)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