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 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 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 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 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 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 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 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 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 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