1 /** @file 2 Functions that perform file read/write. 3 4 Copyright (c) 2005 - 2015, 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 18 /** 19 20 Get the file's position of the file. 21 22 23 @param FHand - The handle of file. 24 @param Position - The file's position of the file. 25 26 @retval EFI_SUCCESS - Get the info successfully. 27 @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. 28 @retval EFI_UNSUPPORTED - The open file is not a file. 29 30 **/ 31 EFI_STATUS 32 EFIAPI 33 FatGetPosition ( 34 IN EFI_FILE_PROTOCOL *FHand, 35 OUT UINT64 *Position 36 ) 37 { 38 FAT_IFILE *IFile; 39 FAT_OFILE *OFile; 40 41 IFile = IFILE_FROM_FHAND (FHand); 42 OFile = IFile->OFile; 43 44 if (OFile->Error == EFI_NOT_FOUND) { 45 return EFI_DEVICE_ERROR; 46 } 47 48 if (OFile->ODir != NULL) { 49 return EFI_UNSUPPORTED; 50 } 51 52 *Position = IFile->Position; 53 return EFI_SUCCESS; 54 } 55 56 /** 57 58 Set the file's position of the file. 59 60 @param FHand - The handle of file. 61 @param Position - The file's position of the file. 62 63 @retval EFI_SUCCESS - Set the info successfully. 64 @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. 65 @retval EFI_UNSUPPORTED - Set a directory with a not-zero position. 66 67 **/ 68 EFI_STATUS 69 EFIAPI 70 FatSetPosition ( 71 IN EFI_FILE_PROTOCOL *FHand, 72 IN UINT64 Position 73 ) 74 { 75 FAT_IFILE *IFile; 76 FAT_OFILE *OFile; 77 78 IFile = IFILE_FROM_FHAND (FHand); 79 OFile = IFile->OFile; 80 81 if (OFile->Error == EFI_NOT_FOUND) { 82 return EFI_DEVICE_ERROR; 83 } 84 85 FatWaitNonblockingTask (IFile); 86 87 // 88 // If this is a directory, we can only set back to position 0 89 // 90 if (OFile->ODir != NULL) { 91 if (Position != 0) { 92 // 93 // Reset current directory cursor; 94 // 95 return EFI_UNSUPPORTED; 96 } 97 98 FatResetODirCursor (OFile); 99 } 100 // 101 // Set the position 102 // 103 if (Position == (UINT64)-1) { 104 Position = OFile->FileSize; 105 } 106 // 107 // Set the position 108 // 109 IFile->Position = Position; 110 return EFI_SUCCESS; 111 } 112 113 /** 114 115 Get the file info from the open file of the IFile into Buffer. 116 117 @param IFile - The instance of the open file. 118 @param BufferSize - Size of Buffer. 119 @param Buffer - Buffer containing read data. 120 121 @retval EFI_SUCCESS - Get the file info successfully. 122 @retval other - An error occurred when operation the disk. 123 124 **/ 125 EFI_STATUS 126 FatIFileReadDir ( 127 IN FAT_IFILE *IFile, 128 IN OUT UINTN *BufferSize, 129 OUT VOID *Buffer 130 ) 131 { 132 EFI_STATUS Status; 133 FAT_OFILE *OFile; 134 FAT_ODIR *ODir; 135 FAT_DIRENT *DirEnt; 136 UINT32 CurrentPos; 137 138 OFile = IFile->OFile; 139 ODir = OFile->ODir; 140 CurrentPos = ((UINT32) IFile->Position) / sizeof (FAT_DIRECTORY_ENTRY); 141 142 // 143 // We need to relocate the directory 144 // 145 if (CurrentPos < ODir->CurrentPos) { 146 // 147 // The directory cursor has been modified by another IFile, we reset the cursor 148 // 149 FatResetODirCursor (OFile); 150 } 151 // 152 // We seek the next directory entry's position 153 // 154 do { 155 Status = FatGetNextDirEnt (OFile, &DirEnt); 156 if (EFI_ERROR (Status) || DirEnt == NULL) { 157 // 158 // Something error occurred or reach the end of directory, 159 // return 0 buffersize 160 // 161 *BufferSize = 0; 162 goto Done; 163 } 164 } while (ODir->CurrentPos <= CurrentPos); 165 Status = FatGetDirEntInfo (OFile->Volume, DirEnt, BufferSize, Buffer); 166 167 Done: 168 // 169 // Update IFile's Position 170 // 171 if (!EFI_ERROR (Status)) { 172 // 173 // Update IFile->Position, if everything is all right 174 // 175 CurrentPos = ODir->CurrentPos; 176 IFile->Position = (UINT64) (CurrentPos * sizeof (FAT_DIRECTORY_ENTRY)); 177 } 178 179 return Status; 180 } 181 182 /** 183 184 Get the file info from the open file of the IFile into Buffer. 185 186 @param FHand - The file handle to access. 187 @param IoMode - Indicate whether the access mode is reading or writing. 188 @param BufferSize - Size of Buffer. 189 @param Buffer - Buffer containing read data. 190 @param Token - A pointer to the token associated with the transaction. 191 192 @retval EFI_SUCCESS - Get the file info successfully. 193 @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. 194 @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. 195 @retval EFI_WRITE_PROTECTED - The disk is write protect. 196 @retval EFI_ACCESS_DENIED - The file is read-only. 197 @return other - An error occurred when operating on the disk. 198 199 **/ 200 EFI_STATUS 201 FatIFileAccess ( 202 IN EFI_FILE_PROTOCOL *FHand, 203 IN IO_MODE IoMode, 204 IN OUT UINTN *BufferSize, 205 IN OUT VOID *Buffer, 206 IN EFI_FILE_IO_TOKEN *Token 207 ) 208 { 209 EFI_STATUS Status; 210 FAT_IFILE *IFile; 211 FAT_OFILE *OFile; 212 FAT_VOLUME *Volume; 213 UINT64 EndPosition; 214 FAT_TASK *Task; 215 216 IFile = IFILE_FROM_FHAND (FHand); 217 OFile = IFile->OFile; 218 Volume = OFile->Volume; 219 Task = NULL; 220 221 // 222 // Write to a directory is unsupported 223 // 224 if ((OFile->ODir != NULL) && (IoMode == WriteData)) { 225 return EFI_UNSUPPORTED; 226 } 227 228 if (OFile->Error == EFI_NOT_FOUND) { 229 return EFI_DEVICE_ERROR; 230 } 231 232 if (IoMode == ReadData) { 233 // 234 // If position is at EOF, then return device error 235 // 236 if (IFile->Position > OFile->FileSize) { 237 return EFI_DEVICE_ERROR; 238 } 239 } else { 240 // 241 // Check if the we can write data 242 // 243 if (Volume->ReadOnly) { 244 return EFI_WRITE_PROTECTED; 245 } 246 247 if (IFile->ReadOnly) { 248 return EFI_ACCESS_DENIED; 249 } 250 } 251 252 if (Token == NULL) { 253 FatWaitNonblockingTask (IFile); 254 } else { 255 // 256 // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2. 257 // But if it calls, the below check can avoid crash. 258 // 259 if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) { 260 return EFI_UNSUPPORTED; 261 } 262 Task = FatCreateTask (IFile, Token); 263 if (Task == NULL) { 264 return EFI_OUT_OF_RESOURCES; 265 } 266 } 267 268 FatAcquireLock (); 269 270 Status = OFile->Error; 271 if (!EFI_ERROR (Status)) { 272 if (OFile->ODir != NULL) { 273 // 274 // Read a directory is supported 275 // 276 ASSERT (IoMode == ReadData); 277 Status = FatIFileReadDir (IFile, BufferSize, Buffer); 278 OFile = NULL; 279 } else { 280 // 281 // Access a file 282 // 283 EndPosition = IFile->Position + *BufferSize; 284 if (EndPosition > OFile->FileSize) { 285 // 286 // The position goes beyond the end of file 287 // 288 if (IoMode == ReadData) { 289 // 290 // Adjust the actual size read 291 // 292 *BufferSize -= (UINTN) EndPosition - OFile->FileSize; 293 } else { 294 // 295 // We expand the file size of OFile 296 // 297 Status = FatGrowEof (OFile, EndPosition); 298 if (EFI_ERROR (Status)) { 299 // 300 // Must update the file's info into the file's Directory Entry 301 // and then flush the dirty cache info into disk. 302 // 303 *BufferSize = 0; 304 FatOFileFlush (OFile); 305 OFile = NULL; 306 goto Done; 307 } 308 309 FatUpdateDirEntClusterSizeInfo (OFile); 310 } 311 } 312 313 Status = FatAccessOFile (OFile, IoMode, (UINTN) IFile->Position, BufferSize, Buffer, Task); 314 IFile->Position += *BufferSize; 315 } 316 } 317 318 if (Token != NULL) { 319 if (!EFI_ERROR (Status)) { 320 Status = FatQueueTask (IFile, Task); 321 } else { 322 FatDestroyTask (Task); 323 } 324 } 325 326 Done: 327 // 328 // On EFI_SUCCESS case, not calling FatCleanupVolume(): 329 // 1) The Cache flush operation is avoided to enhance 330 // performance. Caller is responsible to call Flush() when necessary. 331 // 2) The volume dirty bit is probably set already, and is expected to be 332 // cleaned in subsequent Flush() or other operations. 333 // 3) Write operation doesn't affect OFile/IFile structure, so 334 // Reference checking is not necessary. 335 // 336 if (EFI_ERROR (Status)) { 337 Status = FatCleanupVolume (Volume, OFile, Status, NULL); 338 } 339 340 FatReleaseLock (); 341 return Status; 342 } 343 344 /** 345 346 Get the file info. 347 348 @param FHand - The handle of the file. 349 @param BufferSize - Size of Buffer. 350 @param Buffer - Buffer containing read data. 351 352 353 @retval EFI_SUCCESS - Get the file info successfully. 354 @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. 355 @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. 356 @return other - An error occurred when operation the disk. 357 358 **/ 359 EFI_STATUS 360 EFIAPI 361 FatRead ( 362 IN EFI_FILE_PROTOCOL *FHand, 363 IN OUT UINTN *BufferSize, 364 OUT VOID *Buffer 365 ) 366 { 367 return FatIFileAccess (FHand, ReadData, BufferSize, Buffer, NULL); 368 } 369 370 /** 371 372 Get the file info. 373 374 @param FHand - The handle of the file. 375 @param Token - A pointer to the token associated with the transaction. 376 377 @retval EFI_SUCCESS - Get the file info successfully. 378 @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. 379 @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. 380 @return other - An error occurred when operation the disk. 381 382 **/ 383 EFI_STATUS 384 EFIAPI 385 FatReadEx ( 386 IN EFI_FILE_PROTOCOL *FHand, 387 IN OUT EFI_FILE_IO_TOKEN *Token 388 ) 389 { 390 return FatIFileAccess (FHand, ReadData, &Token->BufferSize, Token->Buffer, Token); 391 } 392 393 /** 394 395 Write the content of buffer into files. 396 397 @param FHand - The handle of the file. 398 @param BufferSize - Size of Buffer. 399 @param Buffer - Buffer containing write data. 400 401 @retval EFI_SUCCESS - Set the file info successfully. 402 @retval EFI_WRITE_PROTECTED - The disk is write protect. 403 @retval EFI_ACCESS_DENIED - The file is read-only. 404 @retval EFI_DEVICE_ERROR - The OFile is not valid. 405 @retval EFI_UNSUPPORTED - The open file is not a file. 406 - The writing file size is larger than 4GB. 407 @return other - An error occurred when operation the disk. 408 409 **/ 410 EFI_STATUS 411 EFIAPI 412 FatWrite ( 413 IN EFI_FILE_PROTOCOL *FHand, 414 IN OUT UINTN *BufferSize, 415 IN VOID *Buffer 416 ) 417 { 418 return FatIFileAccess (FHand, WriteData, BufferSize, Buffer, NULL); 419 } 420 421 /** 422 423 Get the file info. 424 425 @param FHand - The handle of the file. 426 @param Token - A pointer to the token associated with the transaction. 427 428 @retval EFI_SUCCESS - Get the file info successfully. 429 @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. 430 @retval EFI_VOLUME_CORRUPTED - The file type of open file is error. 431 @return other - An error occurred when operation the disk. 432 433 **/ 434 EFI_STATUS 435 EFIAPI 436 FatWriteEx ( 437 IN EFI_FILE_PROTOCOL *FHand, 438 IN OUT EFI_FILE_IO_TOKEN *Token 439 ) 440 { 441 return FatIFileAccess (FHand, WriteData, &Token->BufferSize, Token->Buffer, Token); 442 } 443 444 /** 445 446 This function reads data from a file or writes data to a file. 447 It uses OFile->PosRem to determine how much data can be accessed in one time. 448 449 @param OFile - The open file. 450 @param IoMode - Indicate whether the access mode is reading or writing. 451 @param Position - The position where data will be accessed. 452 @param DataBufferSize - Size of Buffer. 453 @param UserBuffer - Buffer containing data. 454 @param Task point to task instance. 455 456 @retval EFI_SUCCESS - Access the data successfully. 457 @return other - An error occurred when operating on the disk. 458 459 **/ 460 EFI_STATUS 461 FatAccessOFile ( 462 IN FAT_OFILE *OFile, 463 IN IO_MODE IoMode, 464 IN UINTN Position, 465 IN OUT UINTN *DataBufferSize, 466 IN OUT UINT8 *UserBuffer, 467 IN FAT_TASK *Task 468 ) 469 { 470 FAT_VOLUME *Volume; 471 UINTN Len; 472 EFI_STATUS Status; 473 UINTN BufferSize; 474 475 BufferSize = *DataBufferSize; 476 Volume = OFile->Volume; 477 ASSERT_VOLUME_LOCKED (Volume); 478 479 Status = EFI_SUCCESS; 480 while (BufferSize > 0) { 481 // 482 // Seek the OFile to the file position 483 // 484 Status = FatOFilePosition (OFile, Position, BufferSize); 485 if (EFI_ERROR (Status)) { 486 break; 487 } 488 // 489 // Clip length to block run 490 // 491 Len = BufferSize > OFile->PosRem ? OFile->PosRem : BufferSize; 492 493 // 494 // Write the data 495 // 496 Status = FatDiskIo (Volume, IoMode, OFile->PosDisk, Len, UserBuffer, Task); 497 if (EFI_ERROR (Status)) { 498 break; 499 } 500 // 501 // Data was successfully accessed 502 // 503 Position += Len; 504 UserBuffer += Len; 505 BufferSize -= Len; 506 if (IoMode == WriteData) { 507 OFile->Dirty = TRUE; 508 OFile->Archive = TRUE; 509 } 510 // 511 // Make sure no outbound occurred 512 // 513 ASSERT (Position <= OFile->FileSize); 514 } 515 // 516 // Update the number of bytes accessed 517 // 518 *DataBufferSize -= BufferSize; 519 return Status; 520 } 521 522 /** 523 524 Expand OFile by appending zero bytes at the end of OFile. 525 526 @param OFile - The open file. 527 @param ExpandedSize - The number of zero bytes appended at the end of the file. 528 529 @retval EFI_SUCCESS - The file is expanded successfully. 530 @return other - An error occurred when expanding file. 531 532 **/ 533 EFI_STATUS 534 FatExpandOFile ( 535 IN FAT_OFILE *OFile, 536 IN UINT64 ExpandedSize 537 ) 538 { 539 EFI_STATUS Status; 540 UINTN WritePos; 541 542 WritePos = OFile->FileSize; 543 Status = FatGrowEof (OFile, ExpandedSize); 544 if (!EFI_ERROR (Status)) { 545 Status = FatWriteZeroPool (OFile, WritePos); 546 } 547 548 return Status; 549 } 550 551 /** 552 553 Write zero pool from the WritePos to the end of OFile. 554 555 @param OFile - The open file to write zero pool. 556 @param WritePos - The number of zero bytes written. 557 558 @retval EFI_SUCCESS - Write the zero pool successfully. 559 @retval EFI_OUT_OF_RESOURCES - Not enough memory to perform the operation. 560 @return other - An error occurred when writing disk. 561 562 **/ 563 EFI_STATUS 564 FatWriteZeroPool ( 565 IN FAT_OFILE *OFile, 566 IN UINTN WritePos 567 ) 568 { 569 EFI_STATUS Status; 570 VOID *ZeroBuffer; 571 UINTN AppendedSize; 572 UINTN BufferSize; 573 UINTN WriteSize; 574 575 AppendedSize = OFile->FileSize - WritePos; 576 BufferSize = AppendedSize; 577 if (AppendedSize > FAT_MAX_ALLOCATE_SIZE) { 578 // 579 // If the appended size is larger, maybe we can not allocate the whole 580 // memory once. So if the growed size is larger than 10M, we just 581 // allocate 10M memory (one healthy system should have 10M available 582 // memory), and then write the zerobuffer to the file several times. 583 // 584 BufferSize = FAT_MAX_ALLOCATE_SIZE; 585 } 586 587 ZeroBuffer = AllocateZeroPool (BufferSize); 588 if (ZeroBuffer == NULL) { 589 return EFI_OUT_OF_RESOURCES; 590 } 591 592 do { 593 WriteSize = AppendedSize > BufferSize ? BufferSize : (UINTN) AppendedSize; 594 AppendedSize -= WriteSize; 595 Status = FatAccessOFile (OFile, WriteData, WritePos, &WriteSize, ZeroBuffer, NULL); 596 if (EFI_ERROR (Status)) { 597 break; 598 } 599 600 WritePos += WriteSize; 601 } while (AppendedSize > 0); 602 603 FreePool (ZeroBuffer); 604 return Status; 605 } 606 607 /** 608 609 Truncate the OFile to smaller file size. 610 611 @param OFile - The open file. 612 @param TruncatedSize - The new file size. 613 614 @retval EFI_SUCCESS - The file is truncated successfully. 615 @return other - An error occurred when truncating file. 616 617 **/ 618 EFI_STATUS 619 FatTruncateOFile ( 620 IN FAT_OFILE *OFile, 621 IN UINTN TruncatedSize 622 ) 623 { 624 OFile->FileSize = TruncatedSize; 625 return FatShrinkEof (OFile); 626 } 627