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