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