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