1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 FilObSup.c 8 9 Abstract: 10 11 This module implements the Fat File object support routines. 12 13 14 --*/ 15 16 #include "fatprocs.h" 17 18 // 19 // The Bug check file id for this module 20 // 21 22 #define BugCheckFileId (FAT_BUG_CHECK_FILOBSUP) 23 24 // 25 // The debug trace level 26 // 27 28 #define Dbg (DEBUG_TRACE_FILOBSUP) 29 30 #ifdef ALLOC_PRAGMA 31 #pragma alloc_text(PAGE, FatForceCacheMiss) 32 #pragma alloc_text(PAGE, FatPurgeReferencedFileObjects) 33 #pragma alloc_text(PAGE, FatSetFileObject) 34 #pragma alloc_text(PAGE, FatDecodeFileObject) 35 #endif 36 37 38 VOID 39 FatSetFileObject ( 40 IN PFILE_OBJECT FileObject OPTIONAL, 41 IN TYPE_OF_OPEN TypeOfOpen, 42 IN PVOID VcbOrFcbOrDcb, 43 IN PCCB Ccb OPTIONAL 44 ) 45 46 /*++ 47 48 Routine Description: 49 50 This routine sets the file system pointers within the file object 51 52 Arguments: 53 54 FileObject - Supplies a pointer to the file object being modified, and 55 can optionally be null. 56 57 TypeOfOpen - Supplies the type of open denoted by the file object. 58 This is only used by this procedure for sanity checking. 59 60 VcbOrFcbOrDcb - Supplies a pointer to either a vcb, fcb, or dcb 61 62 Ccb - Optionally supplies a pointer to a ccb 63 64 Return Value: 65 66 None. 67 68 --*/ 69 70 { 71 PAGED_CODE(); 72 73 DebugTrace(+1, Dbg, "FatSetFileObject, FileObject = %p\n", FileObject ); 74 75 NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); 76 77 78 NT_ASSERT(((TypeOfOpen == UnopenedFileObject)) 79 80 || 81 82 ((TypeOfOpen == UserFileOpen) && 83 (NodeType(VcbOrFcbOrDcb) == FAT_NTC_FCB) && 84 (Ccb != NULL)) 85 86 || 87 88 ((TypeOfOpen == EaFile) && 89 (NodeType(VcbOrFcbOrDcb) == FAT_NTC_FCB) && 90 (Ccb == NULL)) 91 92 || 93 94 ((TypeOfOpen == UserDirectoryOpen) && 95 ((NodeType(VcbOrFcbOrDcb) == FAT_NTC_DCB) || (NodeType(VcbOrFcbOrDcb) == FAT_NTC_ROOT_DCB)) && 96 (Ccb != NULL)) 97 98 || 99 100 ((TypeOfOpen == UserVolumeOpen) && 101 (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) && 102 (Ccb != NULL)) 103 104 || 105 106 ((TypeOfOpen == VirtualVolumeFile) && 107 (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) && 108 (Ccb == NULL)) 109 110 || 111 112 ((TypeOfOpen == DirectoryFile) && 113 ((NodeType(VcbOrFcbOrDcb) == FAT_NTC_DCB) || (NodeType(VcbOrFcbOrDcb) == FAT_NTC_ROOT_DCB)) && 114 (Ccb == NULL)) 115 ); 116 117 118 UNREFERENCED_PARAMETER( TypeOfOpen ); 119 120 // 121 // If we were given an Fcb, Dcb, or Vcb, we have some processing to do. 122 // 123 124 NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); 125 126 if ( VcbOrFcbOrDcb != NULL ) { 127 128 // 129 // Set the Vpb field in the file object, and if we were given an 130 // Fcb or Dcb move the field over to point to the nonpaged Fcb/Dcb 131 // 132 133 if (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) { 134 135 FileObject->Vpb = ((PVCB)VcbOrFcbOrDcb)->Vpb; 136 137 } else { 138 139 FileObject->Vpb = ((PFCB)VcbOrFcbOrDcb)->Vcb->Vpb; 140 141 // 142 // If this is a temporary file, note it in the FcbState 143 // 144 145 if (FlagOn(((PFCB)VcbOrFcbOrDcb)->FcbState, FCB_STATE_TEMPORARY)) { 146 147 SetFlag(FileObject->Flags, FO_TEMPORARY_FILE); 148 } 149 } 150 } 151 152 NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); 153 154 // 155 // Now set the fscontext fields of the file object 156 // 157 158 if (ARGUMENT_PRESENT( FileObject )) { 159 160 FileObject->FsContext = VcbOrFcbOrDcb; 161 FileObject->FsContext2 = Ccb; 162 } 163 164 NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); 165 166 // 167 // And return to our caller 168 // 169 170 DebugTrace(-1, Dbg, "FatSetFileObject -> VOID\n", 0); 171 172 return; 173 } 174 175 TYPE_OF_OPEN 176 FatDecodeFileObject ( 177 _In_ PFILE_OBJECT FileObject, 178 _Outptr_ PVCB *Vcb, 179 _Outptr_ PFCB *FcbOrDcb, 180 _Outptr_ PCCB *Ccb 181 ) 182 183 /*++ 184 185 Routine Description: 186 187 This procedure takes a pointer to a file object, that has already been 188 opened by the Fat file system and figures out what really is opened. 189 190 Arguments: 191 192 FileObject - Supplies the file object pointer being interrogated 193 194 Vcb - Receives a pointer to the Vcb for the file object. 195 196 FcbOrDcb - Receives a pointer to the Fcb/Dcb for the file object, if 197 one exists. 198 199 Ccb - Receives a pointer to the Ccb for the file object, if one exists. 200 201 Return Value: 202 203 TYPE_OF_OPEN - returns the type of file denoted by the input file object. 204 205 UserFileOpen - The FO represents a user's opened data file. 206 Ccb, FcbOrDcb, and Vcb are set. FcbOrDcb points to an Fcb. 207 208 UserDirectoryOpen - The FO represents a user's opened directory. 209 Ccb, FcbOrDcb, and Vcb are set. FcbOrDcb points to a Dcb/RootDcb 210 211 UserVolumeOpen - The FO represents a user's opened volume. 212 Ccb and Vcb are set. FcbOrDcb is null. 213 214 VirtualVolumeFile - The FO represents the special virtual volume file. 215 Vcb is set, and Ccb and FcbOrDcb are null. 216 217 DirectoryFile - The FO represents a special directory file. 218 Vcb and FcbOrDcb are set. Ccb is null. FcbOrDcb points to a 219 Dcb/RootDcb. 220 221 EaFile - The FO represents an Ea Io stream file. 222 FcbOrDcb, and Vcb are set. FcbOrDcb points to an Fcb, and Ccb is 223 null. 224 225 --*/ 226 227 { 228 TYPE_OF_OPEN TypeOfOpen; 229 PVOID FsContext; 230 PVOID FsContext2; 231 232 PAGED_CODE(); 233 234 DebugTrace(+1, Dbg, "FatDecodeFileObject, FileObject = %p\n", FileObject); 235 236 // 237 // Reference the fs context fields of the file object, and zero out 238 // the out pointer parameters. 239 // 240 241 FsContext = FileObject->FsContext; 242 FsContext2 = FileObject->FsContext2; 243 244 // 245 // Special case the situation where FsContext is null 246 // 247 248 if (FsContext == NULL) { 249 250 *Ccb = NULL; 251 *FcbOrDcb = NULL; 252 *Vcb = NULL; 253 254 TypeOfOpen = UnopenedFileObject; 255 256 } else { 257 258 // 259 // Now we can case on the node type code of the fscontext pointer 260 // and set the appropriate out pointers 261 // 262 263 switch (NodeType(FsContext)) { 264 265 case FAT_NTC_VCB: 266 267 *Ccb = FsContext2; 268 *FcbOrDcb = NULL; 269 *Vcb = FsContext; 270 271 TypeOfOpen = ( *Ccb == NULL ? VirtualVolumeFile : UserVolumeOpen ); 272 273 break; 274 275 case FAT_NTC_ROOT_DCB: 276 case FAT_NTC_DCB: 277 278 *Ccb = FsContext2; 279 *FcbOrDcb = FsContext; 280 *Vcb = (*FcbOrDcb)->Vcb; 281 282 TypeOfOpen = ( *Ccb == NULL ? DirectoryFile : UserDirectoryOpen ); 283 284 DebugTrace(0, Dbg, "Referencing directory: %wZ\n", &(*FcbOrDcb)->FullFileName); 285 286 break; 287 288 case FAT_NTC_FCB: 289 290 *Ccb = FsContext2; 291 *FcbOrDcb = FsContext; 292 *Vcb = (*FcbOrDcb)->Vcb; 293 294 if (*Ccb != NULL ) { 295 296 TypeOfOpen = UserFileOpen; 297 DebugTrace(0, Dbg, "Referencing file: %wZ\n", &(*FcbOrDcb)->FullFileName); 298 299 } else { 300 301 // 302 // No Ccb means this is a special open. 303 // 304 305 306 if ( *FcbOrDcb == (*Vcb)->EaFcb ) { 307 308 TypeOfOpen = EaFile; 309 DebugTrace(0, Dbg, "Referencing EA file: %wZ\n", &(*FcbOrDcb)->FullFileName); 310 311 } else { 312 313 #ifdef _MSC_VER 314 #pragma prefast(suppress:28159, "things are seriously wrong if we get here") 315 #endif 316 FatBugCheck( NodeType(FsContext), 0, 0 ); 317 318 } 319 320 } 321 322 break; 323 324 default: 325 326 #ifdef _MSC_VER 327 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 328 #endif 329 FatBugCheck( NodeType(FsContext), 0, 0 ); 330 } 331 } 332 333 // 334 // and return to our caller 335 // 336 337 DebugTrace(0, Dbg, "FatDecodeFileObject -> VCB(%p)\n", *Vcb); 338 DebugTrace(0, Dbg, "FatDecodeFileObject -> FCB(%p)\n", *FcbOrDcb); 339 DebugTrace(0, Dbg, "FatDecodeFileObject -> CCB(%p)\n", *Ccb); 340 DebugTrace(-1, Dbg, "FatDecodeFileObject -> TypeOfOpen = %08lx\n", TypeOfOpen); 341 342 return TypeOfOpen; 343 } 344 345 _Requires_lock_held_(_Global_critical_region_) 346 VOID 347 FatPurgeReferencedFileObjects ( 348 IN PIRP_CONTEXT IrpContext, 349 IN PFCB Fcb, 350 IN FAT_FLUSH_TYPE FlushType 351 ) 352 353 /*++ 354 355 Routine Description: 356 357 This routine non-recursively walks from the given FcbOrDcb and trys 358 to force Cc or Mm to close any sections it may be holding on to. 359 360 Arguments: 361 362 Fcb - Supplies a pointer to either an fcb or a dcb 363 364 FlushType - Specifies the kind of flushing to perform 365 366 Return Value: 367 368 None. 369 370 --*/ 371 372 { 373 PFCB OriginalFcb = Fcb; 374 PFCB NextFcb; 375 376 PAGED_CODE(); 377 378 DebugTrace(+1, Dbg, "FatPurgeReferencedFileObjects, Fcb = %p\n", Fcb ); 379 380 NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); 381 382 // 383 // First, if we have a delayed close, force it closed. 384 // 385 386 FatFspClose(Fcb->Vcb); 387 388 // 389 // Walk the directory tree forcing sections closed. 390 // 391 // Note that it very important to get the next node to visit before 392 // acting on the current node. This is because acting on a node may 393 // make it, and an arbitrary number of direct ancestors, vanish. 394 // Since we never visit ancestors in our top-down enumeration scheme, we 395 // can safely continue the enumeration even when the tree is vanishing 396 // beneath us. This is way cool. 397 // 398 399 while ( Fcb != NULL ) { 400 401 NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, OriginalFcb); 402 403 // 404 // Check for the EA file fcb 405 // 406 407 if ( !FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_VOLUME_ID) ) { 408 409 FatForceCacheMiss( IrpContext, Fcb, FlushType ); 410 } 411 412 Fcb = NextFcb; 413 } 414 415 DebugTrace(-1, Dbg, "FatPurgeReferencedFileObjects (VOID)\n", 0 ); 416 417 return; 418 } 419 420 421 _Requires_lock_held_(_Global_critical_region_) 422 VOID 423 FatForceCacheMiss ( 424 IN PIRP_CONTEXT IrpContext, 425 IN PFCB Fcb, 426 IN FAT_FLUSH_TYPE FlushType 427 ) 428 429 /*++ 430 431 Routine Description: 432 433 The following routine asks either Cc or Mm to get rid of any cached 434 pages on a file. Note that this will fail if a user has mapped a file. 435 436 If there is a shared cache map, purge the cache section. Otherwise 437 we have to go and ask Mm to blow away the section. 438 439 NOTE: This caller MUST own the Vcb exclusive. 440 441 Arguments: 442 443 Fcb - Supplies a pointer to an fcb 444 445 FlushType - Specifies the kind of flushing to perform 446 447 Return Value: 448 449 None. 450 451 --*/ 452 453 { 454 PVCB Vcb; 455 BOOLEAN ChildrenAcquired = FALSE; 456 457 PAGED_CODE(); 458 459 // 460 // If we can't wait, bail. 461 // 462 463 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) || 464 FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); 465 466 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { 467 468 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 469 } 470 471 // 472 // If we are purging a directory file object, we must acquire all the 473 // FCBs exclusive so that the parent directory is not being pinned. 474 // Careful, we can collide with something acquiring up the tree like 475 // an unpin repinned flush (FsRtlAcquireFileForCcFlush ...) of a parent 476 // dir on extending writethrough of a child file (oops). So get things 477 // going up the tree, not down. 478 // 479 480 if ((NodeType(Fcb) != FAT_NTC_FCB) && 481 !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) { 482 483 PLIST_ENTRY Links; 484 PFCB TempFcb; 485 486 ChildrenAcquired = TRUE; 487 488 for (Links = Fcb->Specific.Dcb.ParentDcbQueue.Flink; 489 Links != &Fcb->Specific.Dcb.ParentDcbQueue; 490 Links = Links->Flink) { 491 492 TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); 493 494 (VOID)FatAcquireExclusiveFcb( IrpContext, TempFcb ); 495 } 496 } 497 498 (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); 499 500 // 501 // We use this flag to indicate to a close beneath us that 502 // the Fcb resource should be freed before deleting the Fcb. 503 // 504 505 Vcb = Fcb->Vcb; 506 507 SetFlag( Fcb->FcbState, FCB_STATE_FORCE_MISS_IN_PROGRESS ); 508 509 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); 510 511 _SEH2_TRY { 512 513 BOOLEAN DataSectionExists; 514 BOOLEAN ImageSectionExists; 515 516 PSECTION_OBJECT_POINTERS Section; 517 518 if ( FlushType ) { 519 520 (VOID)FatFlushFile( IrpContext, Fcb, FlushType ); 521 } 522 523 // 524 // The Flush may have made the Fcb go away 525 // 526 527 if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB)) { 528 529 Section = &Fcb->NonPaged->SectionObjectPointers; 530 531 DataSectionExists = (BOOLEAN)(Section->DataSectionObject != NULL); 532 ImageSectionExists = (BOOLEAN)(Section->ImageSectionObject != NULL); 533 534 // 535 // Note, it is critical to do the Image section first as the 536 // purge of the data section may cause the image section to go 537 // away, but the opposite is not true. 538 // 539 540 if (ImageSectionExists) { 541 542 (VOID)MmFlushImageSection( Section, MmFlushForWrite ); 543 } 544 545 if (DataSectionExists) { 546 547 CcPurgeCacheSection( Section, NULL, 0, FALSE ); 548 } 549 } 550 551 } _SEH2_FINALLY { 552 553 // 554 // If we purging a directory file object, release all the Fcb 555 // resources that we acquired above. The Dcb cannot have vanished 556 // if there were Fcbs underneath it, and the Fcbs couldn't have gone 557 // away since I own the Vcb. 558 // 559 560 if (ChildrenAcquired) { 561 562 PLIST_ENTRY Links; 563 PFCB TempFcb; 564 565 for (Links = Fcb->Specific.Dcb.ParentDcbQueue.Flink; 566 Links != &Fcb->Specific.Dcb.ParentDcbQueue; 567 Links = Links->Flink) { 568 569 TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); 570 571 FatReleaseFcb( IrpContext, TempFcb ); 572 } 573 } 574 575 // 576 // Since we have the Vcb exclusive we know that if any closes 577 // come in it is because the CcPurgeCacheSection caused the 578 // Fcb to go away. Also in close, the Fcb was released 579 // before being freed. 580 // 581 582 if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) { 583 584 ClearFlag( Fcb->FcbState, FCB_STATE_FORCE_MISS_IN_PROGRESS ); 585 586 FatReleaseFcb( (IRPCONTEXT), Fcb ); 587 } 588 } _SEH2_END; 589 } 590 591 592