ad3c2604127434f866f85af317c95f0a4bb55fca
[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 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 = 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 }