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