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