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 * 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 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 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 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 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 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 407 FatAcquireLockOrFail ( 408 VOID 409 ) 410 { 411 return EfiAcquireLockOrFail (&FatFsLock); 412 } 413 414 /** 415 416 Unlock the volume. 417 418 **/ 419 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 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 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 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 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 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 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