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