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