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