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
FatGetPosition(IN EFI_FILE_PROTOCOL * FHand,OUT UINT64 * Position)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
FatSetPosition(IN EFI_FILE_PROTOCOL * FHand,IN UINT64 Position)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
FatIFileReadDir(IN FAT_IFILE * IFile,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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
FatIFileAccess(IN EFI_FILE_PROTOCOL * FHand,IN IO_MODE IoMode,IN OUT UINTN * BufferSize,IN OUT VOID * Buffer,IN EFI_FILE_IO_TOKEN * Token)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
FatRead(IN EFI_FILE_PROTOCOL * FHand,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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
FatReadEx(IN EFI_FILE_PROTOCOL * FHand,IN OUT EFI_FILE_IO_TOKEN * Token)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
FatWrite(IN EFI_FILE_PROTOCOL * FHand,IN OUT UINTN * BufferSize,IN VOID * Buffer)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
FatWriteEx(IN EFI_FILE_PROTOCOL * FHand,IN OUT EFI_FILE_IO_TOKEN * Token)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
FatAccessOFile(IN FAT_OFILE * OFile,IN IO_MODE IoMode,IN UINTN Position,IN OUT UINTN * DataBufferSize,IN OUT UINT8 * UserBuffer,IN FAT_TASK * Task)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
FatExpandOFile(IN FAT_OFILE * OFile,IN UINT64 ExpandedSize)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
FatWriteZeroPool(IN FAT_OFILE * OFile,IN UINTN WritePos)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
FatTruncateOFile(IN FAT_OFILE * OFile,IN UINTN TruncatedSize)613 FatTruncateOFile (
614   IN FAT_OFILE          *OFile,
615   IN UINTN              TruncatedSize
616   )
617 {
618   OFile->FileSize = TruncatedSize;
619   return FatShrinkEof (OFile);
620 }
621