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