| /** @file | |
| Routines that check references and flush OFiles | |
| Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Fat.h" | |
| /** | |
| Flushes all data associated with the file handle. | |
| @param FHand - Handle to file to flush. | |
| @param Token - A pointer to the token associated with the transaction. | |
| @retval EFI_SUCCESS - Flushed the file successfully. | |
| @retval EFI_WRITE_PROTECTED - The volume is read only. | |
| @retval EFI_ACCESS_DENIED - The file is read only. | |
| @return Others - Flushing of the file failed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FatFlushEx ( | |
| IN EFI_FILE_PROTOCOL *FHand, | |
| IN EFI_FILE_IO_TOKEN *Token | |
| ) | |
| { | |
| FAT_IFILE *IFile; | |
| FAT_OFILE *OFile; | |
| FAT_VOLUME *Volume; | |
| EFI_STATUS Status; | |
| FAT_TASK *Task; | |
| IFile = IFILE_FROM_FHAND (FHand); | |
| OFile = IFile->OFile; | |
| Volume = OFile->Volume; | |
| Task = NULL; | |
| // | |
| // If the file has a permanent error, return it | |
| // | |
| if (EFI_ERROR (OFile->Error)) { | |
| return OFile->Error; | |
| } | |
| if (Volume->ReadOnly) { | |
| return EFI_WRITE_PROTECTED; | |
| } | |
| // | |
| // If read only, return error | |
| // | |
| if (IFile->ReadOnly) { | |
| return EFI_ACCESS_DENIED; | |
| } | |
| if (Token == NULL) { | |
| FatWaitNonblockingTask (IFile); | |
| } else { | |
| // | |
| // Caller shouldn't call the non-blocking interfaces if the low layer doesn't support DiskIo2. | |
| // But if it calls, the below check can avoid crash. | |
| // | |
| if (FHand->Revision < EFI_FILE_PROTOCOL_REVISION2) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Task = FatCreateTask (IFile, Token); | |
| if (Task == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| } | |
| // | |
| // Flush the OFile | |
| // | |
| FatAcquireLock (); | |
| Status = FatOFileFlush (OFile); | |
| Status = FatCleanupVolume (OFile->Volume, OFile, Status, Task); | |
| FatReleaseLock (); | |
| if (Token != NULL) { | |
| if (!EFI_ERROR (Status)) { | |
| Status = FatQueueTask (IFile, Task); | |
| } else { | |
| FatDestroyTask (Task); | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Flushes all data associated with the file handle. | |
| @param FHand - Handle to file to flush. | |
| @retval EFI_SUCCESS - Flushed the file successfully. | |
| @retval EFI_WRITE_PROTECTED - The volume is read only. | |
| @retval EFI_ACCESS_DENIED - The file is read only. | |
| @return Others - Flushing of the file failed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FatFlush ( | |
| IN EFI_FILE_PROTOCOL *FHand | |
| ) | |
| { | |
| return FatFlushEx (FHand, NULL); | |
| } | |
| /** | |
| Flushes & Closes the file handle. | |
| @param FHand - Handle to the file to delete. | |
| @retval EFI_SUCCESS - Closed the file successfully. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FatClose ( | |
| IN EFI_FILE_PROTOCOL *FHand | |
| ) | |
| { | |
| FAT_IFILE *IFile; | |
| FAT_OFILE *OFile; | |
| FAT_VOLUME *Volume; | |
| IFile = IFILE_FROM_FHAND (FHand); | |
| OFile = IFile->OFile; | |
| Volume = OFile->Volume; | |
| // | |
| // Lock the volume | |
| // | |
| FatAcquireLock (); | |
| // | |
| // Close the file instance handle | |
| // | |
| FatIFileClose (IFile); | |
| // | |
| // Done. Unlock the volume | |
| // | |
| FatCleanupVolume (Volume, OFile, EFI_SUCCESS, NULL); | |
| FatReleaseLock (); | |
| // | |
| // Close always succeed | |
| // | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Close the open file instance. | |
| @param IFile - Open file instance. | |
| @retval EFI_SUCCESS - Closed the file successfully. | |
| **/ | |
| EFI_STATUS | |
| FatIFileClose ( | |
| FAT_IFILE *IFile | |
| ) | |
| { | |
| FAT_OFILE *OFile; | |
| FAT_VOLUME *Volume; | |
| OFile = IFile->OFile; | |
| Volume = OFile->Volume; | |
| ASSERT_VOLUME_LOCKED (Volume); | |
| FatWaitNonblockingTask (IFile); | |
| // | |
| // Remove the IFile struct | |
| // | |
| RemoveEntryList (&IFile->Link); | |
| // | |
| // Add the OFile to the check reference list | |
| // | |
| if (OFile->CheckLink.ForwardLink == NULL) { | |
| InsertHeadList (&Volume->CheckRef, &OFile->CheckLink); | |
| } | |
| // | |
| // Done. Free the open instance structure | |
| // | |
| FreePool (IFile); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Flush the data associated with an open file. | |
| In this implementation, only last Mod/Access time is updated. | |
| @param OFile - The open file. | |
| @retval EFI_SUCCESS - The OFile is flushed successfully. | |
| @return Others - An error occurred when flushing this OFile. | |
| **/ | |
| EFI_STATUS | |
| FatOFileFlush ( | |
| IN FAT_OFILE *OFile | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FAT_OFILE *Parent; | |
| FAT_DIRENT *DirEnt; | |
| FAT_DATE_TIME FatNow; | |
| // | |
| // Flush each entry up the tree while dirty | |
| // | |
| do { | |
| // | |
| // If the file has a permanent error, then don't write any | |
| // of its data to the device (may be from different media) | |
| // | |
| if (EFI_ERROR (OFile->Error)) { | |
| return OFile->Error; | |
| } | |
| Parent = OFile->Parent; | |
| DirEnt = OFile->DirEnt; | |
| if (OFile->Dirty) { | |
| // | |
| // Update the last modification time | |
| // | |
| FatGetCurrentFatTime (&FatNow); | |
| CopyMem (&DirEnt->Entry.FileLastAccess, &FatNow.Date, sizeof (FAT_DATE)); | |
| if (!OFile->PreserveLastModification) { | |
| FatGetCurrentFatTime (&DirEnt->Entry.FileModificationTime); | |
| } | |
| OFile->PreserveLastModification = FALSE; | |
| if (OFile->Archive) { | |
| DirEnt->Entry.Attributes |= FAT_ATTRIBUTE_ARCHIVE; | |
| OFile->Archive = FALSE; | |
| } | |
| // | |
| // Write the directory entry | |
| // | |
| if ((Parent != NULL) && !DirEnt->Invalid) { | |
| // | |
| // Write the OFile's directory entry | |
| // | |
| Status = FatStoreDirEnt (Parent, DirEnt); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| OFile->Dirty = FALSE; | |
| } | |
| // | |
| // Check the parent | |
| // | |
| OFile = Parent; | |
| } while (OFile != NULL); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check the references of the OFile. | |
| If the OFile (that is checked) is no longer | |
| referenced, then it is freed. | |
| @param OFile - The OFile to be checked. | |
| @retval TRUE - The OFile is not referenced and freed. | |
| @retval FALSE - The OFile is kept. | |
| **/ | |
| BOOLEAN | |
| FatCheckOFileRef ( | |
| IN FAT_OFILE *OFile | |
| ) | |
| { | |
| // | |
| // If the OFile is on the check ref list, remove it | |
| // | |
| if (OFile->CheckLink.ForwardLink != NULL) { | |
| RemoveEntryList (&OFile->CheckLink); | |
| OFile->CheckLink.ForwardLink = NULL; | |
| } | |
| FatOFileFlush (OFile); | |
| // | |
| // Are there any references to this OFile? | |
| // | |
| if (!IsListEmpty (&OFile->Opens) || !IsListEmpty (&OFile->ChildHead)) { | |
| // | |
| // The OFile cannot be freed | |
| // | |
| return FALSE; | |
| } | |
| // | |
| // Free the Ofile | |
| // | |
| FatCloseDirEnt (OFile->DirEnt); | |
| return TRUE; | |
| } | |
| /** | |
| Check the references of all open files on the volume. | |
| Any open file (that is checked) that is no longer | |
| referenced, is freed - and its parent open file | |
| is then referenced checked. | |
| @param Volume - The volume to check the pending open file list. | |
| **/ | |
| STATIC | |
| VOID | |
| FatCheckVolumeRef ( | |
| IN FAT_VOLUME *Volume | |
| ) | |
| { | |
| FAT_OFILE *OFile; | |
| FAT_OFILE *Parent; | |
| // | |
| // Check all files on the pending check list | |
| // | |
| while (!IsListEmpty (&Volume->CheckRef)) { | |
| // | |
| // Start with the first file listed | |
| // | |
| Parent = OFILE_FROM_CHECKLINK (Volume->CheckRef.ForwardLink); | |
| // | |
| // Go up the tree cleaning up any un-referenced OFiles | |
| // | |
| while (Parent != NULL) { | |
| OFile = Parent; | |
| Parent = OFile->Parent; | |
| if (!FatCheckOFileRef (OFile)) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Set error status for a specific OFile, reference checking the volume. | |
| If volume is already marked as invalid, and all resources are freed | |
| after reference checking, the file system protocol is uninstalled and | |
| the volume structure is freed. | |
| @param Volume - the Volume that is to be reference checked and unlocked. | |
| @param OFile - the OFile whose permanent error code is to be set. | |
| @param EfiStatus - error code to be set. | |
| @param Task point to task instance. | |
| @retval EFI_SUCCESS - Clean up the volume successfully. | |
| @return Others - Cleaning up of the volume is failed. | |
| **/ | |
| EFI_STATUS | |
| FatCleanupVolume ( | |
| IN FAT_VOLUME *Volume, | |
| IN FAT_OFILE *OFile, | |
| IN EFI_STATUS EfiStatus, | |
| IN FAT_TASK *Task | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Flag the OFile | |
| // | |
| if (OFile != NULL) { | |
| FatSetVolumeError (OFile, EfiStatus); | |
| } | |
| // | |
| // Clean up any dangling OFiles that don't have IFiles | |
| // we don't check return status here because we want the | |
| // volume be cleaned up even the volume is invalid. | |
| // | |
| FatCheckVolumeRef (Volume); | |
| if (Volume->Valid) { | |
| // | |
| // Update the free hint info. Volume->FreeInfoPos != 0 | |
| // indicates this a FAT32 volume | |
| // | |
| if (Volume->FreeInfoValid && Volume->FatDirty && Volume->FreeInfoPos) { | |
| Status = FatDiskIo (Volume, WriteDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, Task); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| // | |
| // Update that the volume is not dirty | |
| // | |
| if (Volume->FatDirty && (Volume->FatType != Fat12)) { | |
| Volume->FatDirty = FALSE; | |
| Status = FatAccessVolumeDirty (Volume, WriteFat, &Volume->NotDirtyValue); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| // | |
| // Flush all dirty cache entries to disk | |
| // | |
| Status = FatVolumeFlushCache (Volume, Task); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| // | |
| // If the volume is cleared , remove it. | |
| // The only time volume be invalidated is in DriverBindingStop. | |
| // | |
| if ((Volume->Root == NULL) && !Volume->Valid) { | |
| // | |
| // Free the volume structure | |
| // | |
| FatFreeVolume (Volume); | |
| } | |
| return EfiStatus; | |
| } | |
| /** | |
| Set the OFile and its child OFile with the error Status | |
| @param OFile - The OFile whose permanent error code is to be set. | |
| @param Status - Error code to be set. | |
| **/ | |
| VOID | |
| FatSetVolumeError ( | |
| IN FAT_OFILE *OFile, | |
| IN EFI_STATUS Status | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| FAT_OFILE *ChildOFile; | |
| // | |
| // If this OFile doesn't already have an error, set one | |
| // | |
| if (!EFI_ERROR (OFile->Error)) { | |
| OFile->Error = Status; | |
| } | |
| // | |
| // Set the error on each child OFile | |
| // | |
| for (Link = OFile->ChildHead.ForwardLink; Link != &OFile->ChildHead; Link = Link->ForwardLink) { | |
| ChildOFile = OFILE_FROM_CHILDLINK (Link); | |
| FatSetVolumeError (ChildOFile, Status); | |
| } | |
| } |