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