1 /** @file
2   Routines that check references and flush OFiles
3 
4 Copyright (c) 2005 - 2013, 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   Flushes all data associated with the file handle.
15 
16   @param  FHand                 - Handle to file to flush.
17   @param  Token                 - A pointer to the token associated with the transaction.
18 
19   @retval EFI_SUCCESS           - Flushed the file successfully.
20   @retval EFI_WRITE_PROTECTED   - The volume is read only.
21   @retval EFI_ACCESS_DENIED     - The file is read only.
22   @return Others                - Flushing of the file failed.
23 
24 **/
25 EFI_STATUS
26 EFIAPI
FatFlushEx(IN EFI_FILE_PROTOCOL * FHand,IN EFI_FILE_IO_TOKEN * Token)27 FatFlushEx (
28   IN EFI_FILE_PROTOCOL  *FHand,
29   IN EFI_FILE_IO_TOKEN  *Token
30   )
31 {
32   FAT_IFILE   *IFile;
33   FAT_OFILE   *OFile;
34   FAT_VOLUME  *Volume;
35   EFI_STATUS  Status;
36   FAT_TASK    *Task;
37 
38   IFile   = IFILE_FROM_FHAND (FHand);
39   OFile   = IFile->OFile;
40   Volume  = OFile->Volume;
41   Task    = NULL;
42 
43   //
44   // If the file has a permanent error, return it
45   //
46   if (EFI_ERROR (OFile->Error)) {
47     return OFile->Error;
48   }
49 
50   if (Volume->ReadOnly) {
51     return EFI_WRITE_PROTECTED;
52   }
53   //
54   // If read only, return error
55   //
56   if (IFile->ReadOnly) {
57     return EFI_ACCESS_DENIED;
58   }
59 
60   if (Token == NULL) {
61     FatWaitNonblockingTask (IFile);
62   } else {
63     //
64     // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2.
65     // But if it calls, the below check can avoid crash.
66     //
67     if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) {
68       return EFI_UNSUPPORTED;
69     }
70     Task = FatCreateTask (IFile, Token);
71     if (Task == NULL) {
72       return EFI_OUT_OF_RESOURCES;
73     }
74   }
75 
76   //
77   // Flush the OFile
78   //
79   FatAcquireLock ();
80   Status  = FatOFileFlush (OFile);
81   Status  = FatCleanupVolume (OFile->Volume, OFile, Status, Task);
82   FatReleaseLock ();
83 
84   if (Token != NULL) {
85     if (!EFI_ERROR (Status)) {
86       Status = FatQueueTask (IFile, Task);
87     } else {
88       FatDestroyTask (Task);
89     }
90   }
91 
92   return Status;
93 }
94 
95 /**
96 
97   Flushes all data associated with the file handle.
98 
99   @param  FHand                 - Handle to file to flush.
100 
101   @retval EFI_SUCCESS           - Flushed the file successfully.
102   @retval EFI_WRITE_PROTECTED   - The volume is read only.
103   @retval EFI_ACCESS_DENIED     - The file is read only.
104   @return Others                - Flushing of the file failed.
105 
106 **/
107 EFI_STATUS
108 EFIAPI
FatFlush(IN EFI_FILE_PROTOCOL * FHand)109 FatFlush (
110   IN EFI_FILE_PROTOCOL  *FHand
111   )
112 {
113   return FatFlushEx (FHand, NULL);
114 }
115 
116 /**
117 
118   Flushes & Closes the file handle.
119 
120   @param  FHand                 - Handle to the file to delete.
121 
122   @retval EFI_SUCCESS           - Closed the file successfully.
123 
124 **/
125 EFI_STATUS
126 EFIAPI
FatClose(IN EFI_FILE_PROTOCOL * FHand)127 FatClose (
128   IN EFI_FILE_PROTOCOL  *FHand
129   )
130 {
131   FAT_IFILE   *IFile;
132   FAT_OFILE   *OFile;
133   FAT_VOLUME  *Volume;
134 
135   IFile   = IFILE_FROM_FHAND (FHand);
136   OFile   = IFile->OFile;
137   Volume  = OFile->Volume;
138 
139   //
140   // Lock the volume
141   //
142   FatAcquireLock ();
143 
144   //
145   // Close the file instance handle
146   //
147   FatIFileClose (IFile);
148 
149   //
150   // Done. Unlock the volume
151   //
152   FatCleanupVolume (Volume, OFile, EFI_SUCCESS, NULL);
153   FatReleaseLock ();
154 
155   //
156   // Close always succeed
157   //
158   return EFI_SUCCESS;
159 }
160 
161 /**
162 
163   Close the open file instance.
164 
165   @param  IFile                 - Open file instance.
166 
167   @retval EFI_SUCCESS           - Closed the file successfully.
168 
169 **/
170 EFI_STATUS
FatIFileClose(FAT_IFILE * IFile)171 FatIFileClose (
172   FAT_IFILE           *IFile
173   )
174 {
175   FAT_OFILE   *OFile;
176   FAT_VOLUME  *Volume;
177 
178   OFile   = IFile->OFile;
179   Volume  = OFile->Volume;
180 
181   ASSERT_VOLUME_LOCKED (Volume);
182 
183   FatWaitNonblockingTask (IFile);
184 
185   //
186   // Remove the IFile struct
187   //
188   RemoveEntryList (&IFile->Link);
189 
190   //
191   // Add the OFile to the check reference list
192   //
193   if (OFile->CheckLink.ForwardLink == NULL) {
194     InsertHeadList (&Volume->CheckRef, &OFile->CheckLink);
195   }
196   //
197   // Done. Free the open instance structure
198   //
199   FreePool (IFile);
200   return EFI_SUCCESS;
201 }
202 
203 /**
204 
205   Flush the data associated with an open file.
206   In this implementation, only last Mod/Access time is updated.
207 
208   @param  OFile                 - The open file.
209 
210   @retval EFI_SUCCESS           - The OFile is flushed successfully.
211   @return Others                - An error occurred when flushing this OFile.
212 
213 **/
214 EFI_STATUS
FatOFileFlush(IN FAT_OFILE * OFile)215 FatOFileFlush (
216   IN FAT_OFILE    *OFile
217   )
218 {
219   EFI_STATUS    Status;
220   FAT_OFILE     *Parent;
221   FAT_DIRENT    *DirEnt;
222   FAT_DATE_TIME FatNow;
223 
224   //
225   // Flush each entry up the tree while dirty
226   //
227   do {
228     //
229     // If the file has a permanent error, then don't write any
230     // of its data to the device (may be from different media)
231     //
232     if (EFI_ERROR (OFile->Error)) {
233       return OFile->Error;
234     }
235 
236     Parent  = OFile->Parent;
237     DirEnt  = OFile->DirEnt;
238     if (OFile->Dirty) {
239       //
240       // Update the last modification time
241       //
242       FatGetCurrentFatTime (&FatNow);
243       CopyMem (&DirEnt->Entry.FileLastAccess, &FatNow.Date, sizeof (FAT_DATE));
244       if (!OFile->PreserveLastModification) {
245         FatGetCurrentFatTime (&DirEnt->Entry.FileModificationTime);
246       }
247 
248       OFile->PreserveLastModification = FALSE;
249       if (OFile->Archive) {
250         DirEnt->Entry.Attributes |= FAT_ATTRIBUTE_ARCHIVE;
251         OFile->Archive = FALSE;
252       }
253       //
254       // Write the directory entry
255       //
256       if (Parent != NULL && !DirEnt->Invalid) {
257         //
258         // Write the OFile's directory entry
259         //
260         Status = FatStoreDirEnt (Parent, DirEnt);
261         if (EFI_ERROR (Status)) {
262           return Status;
263         }
264       }
265 
266       OFile->Dirty = FALSE;
267     }
268     //
269     // Check the parent
270     //
271     OFile = Parent;
272   } while (OFile != NULL);
273   return EFI_SUCCESS;
274 }
275 
276 /**
277 
278   Check the references of the OFile.
279   If the OFile (that is checked) is no longer
280   referenced, then it is freed.
281 
282   @param  OFile                 - The OFile to be checked.
283 
284   @retval TRUE                  - The OFile is not referenced and freed.
285   @retval FALSE                 - The OFile is kept.
286 
287 **/
288 BOOLEAN
FatCheckOFileRef(IN FAT_OFILE * OFile)289 FatCheckOFileRef (
290   IN FAT_OFILE   *OFile
291   )
292 {
293   //
294   // If the OFile is on the check ref list, remove it
295   //
296   if (OFile->CheckLink.ForwardLink != NULL) {
297     RemoveEntryList (&OFile->CheckLink);
298     OFile->CheckLink.ForwardLink = NULL;
299   }
300 
301   FatOFileFlush (OFile);
302   //
303   // Are there any references to this OFile?
304   //
305   if (!IsListEmpty (&OFile->Opens) || !IsListEmpty (&OFile->ChildHead)) {
306     //
307     // The OFile cannot be freed
308     //
309     return FALSE;
310   }
311   //
312   // Free the Ofile
313   //
314   FatCloseDirEnt (OFile->DirEnt);
315   return TRUE;
316 }
317 
318 /**
319 
320   Check the references of all open files on the volume.
321   Any open file (that is checked) that is no longer
322   referenced, is freed - and its parent open file
323   is then referenced checked.
324 
325   @param  Volume                - The volume to check the pending open file list.
326 
327 **/
328 STATIC
329 VOID
FatCheckVolumeRef(IN FAT_VOLUME * Volume)330 FatCheckVolumeRef (
331   IN FAT_VOLUME   *Volume
332   )
333 {
334   FAT_OFILE *OFile;
335   FAT_OFILE *Parent;
336 
337   //
338   // Check all files on the pending check list
339   //
340   while (!IsListEmpty (&Volume->CheckRef)) {
341     //
342     // Start with the first file listed
343     //
344     Parent = OFILE_FROM_CHECKLINK (Volume->CheckRef.ForwardLink);
345     //
346     // Go up the tree cleaning up any un-referenced OFiles
347     //
348     while (Parent != NULL) {
349       OFile   = Parent;
350       Parent  = OFile->Parent;
351       if (!FatCheckOFileRef (OFile)) {
352         break;
353       }
354     }
355   }
356 }
357 
358 /**
359 
360   Set error status for a specific OFile, reference checking the volume.
361   If volume is already marked as invalid, and all resources are freed
362   after reference checking, the file system protocol is uninstalled and
363   the volume structure is freed.
364 
365   @param  Volume                - the Volume that is to be reference checked and unlocked.
366   @param  OFile                 - the OFile whose permanent error code is to be set.
367   @param  EfiStatus             - error code to be set.
368   @param  Task                    point to task instance.
369 
370   @retval EFI_SUCCESS           - Clean up the volume successfully.
371   @return Others                - Cleaning up of the volume is failed.
372 
373 **/
374 EFI_STATUS
FatCleanupVolume(IN FAT_VOLUME * Volume,IN FAT_OFILE * OFile,IN EFI_STATUS EfiStatus,IN FAT_TASK * Task)375 FatCleanupVolume (
376   IN FAT_VOLUME       *Volume,
377   IN FAT_OFILE        *OFile,
378   IN EFI_STATUS       EfiStatus,
379   IN FAT_TASK         *Task
380   )
381 {
382   EFI_STATUS  Status;
383   //
384   // Flag the OFile
385   //
386   if (OFile != NULL) {
387     FatSetVolumeError (OFile, EfiStatus);
388   }
389   //
390   // Clean up any dangling OFiles that don't have IFiles
391   // we don't check return status here because we want the
392   // volume be cleaned up even the volume is invalid.
393   //
394   FatCheckVolumeRef (Volume);
395   if (Volume->Valid) {
396     //
397     // Update the free hint info. Volume->FreeInfoPos != 0
398     // indicates this a FAT32 volume
399     //
400     if (Volume->FreeInfoValid && Volume->FatDirty && Volume->FreeInfoPos) {
401       Status = FatDiskIo (Volume, WriteDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, Task);
402       if (EFI_ERROR (Status)) {
403         return Status;
404       }
405     }
406     //
407     // Update that the volume is not dirty
408     //
409     if (Volume->FatDirty && Volume->FatType != Fat12) {
410       Volume->FatDirty  = FALSE;
411       Status            = FatAccessVolumeDirty (Volume, WriteFat, &Volume->NotDirtyValue);
412       if (EFI_ERROR (Status)) {
413         return Status;
414       }
415     }
416     //
417     // Flush all dirty cache entries to disk
418     //
419     Status = FatVolumeFlushCache (Volume, Task);
420     if (EFI_ERROR (Status)) {
421       return Status;
422     }
423   }
424   //
425   // If the volume is cleared , remove it.
426   // The only time volume be invalidated is in DriverBindingStop.
427   //
428   if (Volume->Root == NULL && !Volume->Valid) {
429     //
430     // Free the volume structure
431     //
432     FatFreeVolume (Volume);
433   }
434 
435   return EfiStatus;
436 }
437 
438 /**
439 
440   Set the OFile and its child OFile with the error Status
441 
442   @param  OFile                 - The OFile whose permanent error code is to be set.
443   @param  Status                - Error code to be set.
444 
445 **/
446 VOID
FatSetVolumeError(IN FAT_OFILE * OFile,IN EFI_STATUS Status)447 FatSetVolumeError (
448   IN FAT_OFILE            *OFile,
449   IN EFI_STATUS           Status
450   )
451 {
452   LIST_ENTRY      *Link;
453   FAT_OFILE       *ChildOFile;
454 
455   //
456   // If this OFile doesn't already have an error, set one
457   //
458   if (!EFI_ERROR (OFile->Error)) {
459     OFile->Error = Status;
460   }
461   //
462   // Set the error on each child OFile
463   //
464   for (Link = OFile->ChildHead.ForwardLink; Link != &OFile->ChildHead; Link = Link->ForwardLink) {
465     ChildOFile = OFILE_FROM_CHILDLINK (Link);
466     FatSetVolumeError (ChildOFile, Status);
467   }
468 }
469