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