xref: /reactos/drivers/filesystems/cdfs/cachesup.c (revision 34593d93)
1 /*++
2 
3 Copyright (c) 1990-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Cache.c
8 
9 Abstract:
10 
11     This module implements the cache management routines for the Cdfs
12     FSD and FSP, by calling the Common Cache Manager.
13 
14 
15 --*/
16 
17 #include "cdprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (CDFS_BUG_CHECK_CACHESUP)
24 
25 //
26 //  Local debug trace level
27 //
28 
29 #ifdef ALLOC_PRAGMA
30 #pragma alloc_text(PAGE, CdCompleteMdl)
31 #pragma alloc_text(PAGE, CdCreateInternalStream)
32 #pragma alloc_text(PAGE, CdDeleteInternalStream)
33 #pragma alloc_text(PAGE, CdPurgeVolume)
34 #endif
35 
36 
37 VOID
CdCreateInternalStream(_In_ PIRP_CONTEXT IrpContext,_In_ PVCB Vcb,_Inout_ PFCB Fcb,_In_ PUNICODE_STRING Name)38 CdCreateInternalStream (
39     _In_ PIRP_CONTEXT IrpContext,
40     _In_ PVCB Vcb,
41     _Inout_ PFCB Fcb,
42     _In_ PUNICODE_STRING Name
43     )
44 
45 /*++
46 
47 Routine Description:
48 
49     This function creates an internal stream file for interaction
50     with the cache manager.  The Fcb here can be for either a
51     directory stream or for a path table stream.
52 
53 Arguments:
54 
55     Vcb - Vcb for this volume.
56 
57     Fcb - Points to the Fcb for this file.  It is either an Index or
58         Path Table Fcb.
59 
60 Return Value:
61 
62     None.
63 
64 --*/
65 
66 {
67     PFILE_OBJECT StreamFile = NULL;
68     BOOLEAN DecrementReference = FALSE;
69 
70     BOOLEAN CleanupDirContext = FALSE;
71     BOOLEAN UpdateFcbSizes = FALSE;
72 
73     DIRENT Dirent = {0};
74     DIRENT_ENUM_CONTEXT DirContext = {0};
75 
76     PAGED_CODE();
77 
78     ASSERT_IRP_CONTEXT( IrpContext );
79     ASSERT_FCB( Fcb );
80 
81     //
82     //  We may only have the Fcb shared.  Lock the Fcb and do a
83     //  safe test to see if we need to really create the file object.
84     //
85 
86     CdLockFcb( IrpContext, Fcb );
87 
88     if (Fcb->FileObject != NULL) {
89 
90         CdUnlockFcb( IrpContext, Fcb );
91         return;
92     }
93 
94     //
95     //  Use a try-finally to facilitate cleanup.
96     //
97 
98     _SEH2_TRY {
99 
100         //
101         //  Create the internal stream.  The Vpb should be pointing at our volume
102         //  device object at this point.
103         //
104 
105         StreamFile = IoCreateStreamFileObjectLite( NULL, Vcb->Vpb->RealDevice );
106 
107         if (StreamFile == NULL) {
108 
109             CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
110         }
111 
112         //
113         //  Initialize the fields of the file object.
114         //
115 
116         StreamFile->ReadAccess = TRUE;
117         StreamFile->WriteAccess = FALSE;
118         StreamFile->DeleteAccess = FALSE;
119 
120         StreamFile->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject;
121 
122         //
123         //  Set the file object type and increment the Vcb counts.
124         //
125 
126         CdSetFileObject( IrpContext,
127                          StreamFile,
128                          StreamFileOpen,
129                          Fcb,
130                          NULL );
131 
132         //
133         //  We'll give stream file objects a name to aid IO profiling etc. We
134         //  NULL this in CdDeleteInternalStream before OB deletes the file object,
135         //  and before CdRemovePrefix is called (which frees Fcb names).
136         //
137 
138         StreamFile->FileName = *Name;
139 
140         //
141         //  We will reference the current Fcb twice to keep it from going
142         //  away in the error path.  Otherwise if we dereference it
143         //  below in the finally clause a close could cause the Fcb to
144         //  be deallocated.
145         //
146 
147         CdLockVcb( IrpContext, Vcb );
148         CdIncrementReferenceCounts( IrpContext, Fcb, 2, 0 );
149         CdUnlockVcb( IrpContext, Vcb );
150         DecrementReference = TRUE;
151 
152         //
153         //  Initialize the cache map for the file.
154         //
155 
156         CcInitializeCacheMap( StreamFile,
157                               (PCC_FILE_SIZES)&Fcb->AllocationSize,
158                               TRUE,
159                               &CdData.CacheManagerCallbacks,
160                               Fcb );
161 
162         //
163         //  Go ahead and store the stream file into the Fcb.
164         //
165 
166         Fcb->FileObject = StreamFile;
167         StreamFile = NULL;
168 
169         //
170         //  If this is the first file object for a directory then we need to
171         //  read the self entry for this directory and update the sizes
172         //  in the Fcb.  We know that the Fcb has been initialized so
173         //  that we have a least one sector available to read.
174         //
175 
176         if (!FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED )) {
177 
178             ULONG NewDataLength;
179 
180             //
181             //  Initialize the search structures.
182             //
183 
184             CdInitializeDirContext( IrpContext, &DirContext );
185             CdInitializeDirent( IrpContext, &Dirent );
186             CleanupDirContext = TRUE;
187 
188             //
189             //  Read the dirent from disk and transfer the data to the
190             //  in-memory dirent.
191             //
192 
193             CdLookupDirent( IrpContext,
194                             Fcb,
195                             Fcb->StreamOffset,
196                             &DirContext );
197 
198             CdUpdateDirentFromRawDirent( IrpContext, Fcb, &DirContext, &Dirent );
199 
200             //
201             //  Verify that this really for the self entry.  We do this by
202             //  updating the name in the dirent and then checking that it matches
203             //  one of the hard coded names.
204             //
205 
206             CdUpdateDirentName( IrpContext, &Dirent, FALSE );
207 
208             if (Dirent.CdFileName.FileName.Buffer != CdUnicodeSelfArray) {
209 
210                 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
211             }
212 
213             //
214             //  If the data sizes are different then update the header
215             //  and Mcb for this Fcb.
216             //
217 
218             NewDataLength = BlockAlign( Vcb, Dirent.DataLength + Fcb->StreamOffset );
219 
220             if (NewDataLength == 0) {
221 
222                 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
223             }
224 
225             if (NewDataLength != Fcb->FileSize.QuadPart) {
226 
227                 Fcb->AllocationSize.QuadPart =
228                 Fcb->FileSize.QuadPart =
229                 Fcb->ValidDataLength.QuadPart = NewDataLength;
230 
231                 CcSetFileSizes( Fcb->FileObject, (PCC_FILE_SIZES) &Fcb->AllocationSize );
232 
233                 CdTruncateAllocation( IrpContext, Fcb, 0 );
234                 CdAddInitialAllocation( IrpContext,
235                                         Fcb,
236                                         Dirent.StartingOffset,
237                                         NewDataLength );
238 
239                 UpdateFcbSizes = TRUE;
240             }
241 
242             //
243             //  Check for the existence flag and transform to hidden.
244             //
245 
246             if (FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_HIDDEN )) {
247 
248                 SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_HIDDEN );
249             }
250 
251             //
252             //  Convert the time to NT time.
253             //
254 
255             CdConvertCdTimeToNtTime( IrpContext,
256                                      Dirent.CdTime,
257                                      (PLARGE_INTEGER) &Fcb->CreationTime );
258 
259             //
260             //  Update the Fcb flags to indicate we have read the
261             //  self entry.
262             //
263 
264             SetFlag( Fcb->FcbState, FCB_STATE_INITIALIZED );
265 
266             //
267             //  If we updated the sizes then we want to purge the file.  Go
268             //  ahead and unpin and then purge the first page.
269             //
270 
271             CdCleanupDirContext( IrpContext, &DirContext );
272             CdCleanupDirent( IrpContext, &Dirent );
273             CleanupDirContext = FALSE;
274 
275             if (UpdateFcbSizes) {
276 
277                 CcPurgeCacheSection( &Fcb->FcbNonpaged->SegmentObject,
278                                      NULL,
279                                      0,
280                                      FALSE );
281             }
282         }
283 
284     } _SEH2_FINALLY {
285 
286         //
287         //  Cleanup any dirent structures we may have used.
288         //
289 
290         if (CleanupDirContext) {
291 
292             CdCleanupDirContext( IrpContext, &DirContext );
293             CdCleanupDirent( IrpContext, &Dirent );
294         }
295 
296         //
297         //  If we raised then we need to dereference the file object.
298         //
299 
300         if (StreamFile != NULL) {
301 
302             //
303             //  Null the name pointer, since the stream file object never actually
304             //  'owns' the names, we just point it to existing ones.
305             //
306 
307             StreamFile->FileName.Buffer = NULL;
308             StreamFile->FileName.MaximumLength = StreamFile->FileName.Length = 0;
309 
310             ObDereferenceObject( StreamFile );
311             Fcb->FileObject = NULL;
312         }
313 
314         //
315         //  Dereference and unlock the Fcb.
316         //
317 
318         if (DecrementReference) {
319 
320             CdLockVcb( IrpContext, Vcb );
321             CdDecrementReferenceCounts( IrpContext, Fcb, 1, 0 );
322             CdUnlockVcb( IrpContext, Vcb );
323         }
324 
325         CdUnlockFcb( IrpContext, Fcb );
326     } _SEH2_END;
327 
328     return;
329 }
330 
331 
332 VOID
CdDeleteInternalStream(_In_ PIRP_CONTEXT IrpContext,_Inout_ PFCB Fcb)333 CdDeleteInternalStream (
334     _In_ PIRP_CONTEXT IrpContext,
335     _Inout_ PFCB Fcb
336     )
337 
338 /*++
339 
340 Routine Description:
341 
342     This function creates an internal stream file for interaction
343     with the cache manager.  The Fcb here can be for either a
344     directory stream or for a path table stream.
345 
346 Arguments:
347 
348     Fcb - Points to the Fcb for this file.  It is either an Index or
349         Path Table Fcb.
350 
351 Return Value:
352 
353     None.
354 
355 --*/
356 
357 {
358     PFILE_OBJECT FileObject;
359 
360     PAGED_CODE();
361 
362     UNREFERENCED_PARAMETER( IrpContext );
363 
364     ASSERT_IRP_CONTEXT( IrpContext );
365     ASSERT_FCB( Fcb );
366 
367     //
368     //  Lock the Fcb.
369     //
370 
371     CdLockFcb( IrpContext, Fcb );
372 
373     //
374     //  Capture the file object.
375     //
376 
377     FileObject = Fcb->FileObject;
378     Fcb->FileObject = NULL;
379 
380     //
381     //  It is now safe to unlock the Fcb.
382     //
383 
384     CdUnlockFcb( IrpContext, Fcb );
385 
386     //
387     //  Dereference the file object if present.
388     //
389 
390     if (FileObject != NULL) {
391 
392         if (FileObject->PrivateCacheMap != NULL) {
393 
394             CcUninitializeCacheMap( FileObject, NULL, NULL );
395         }
396 
397         //
398         //  Null the name pointer, since the stream file object never actually
399         //  'owns' the names, we just point it to existing ones.
400         //
401 
402         FileObject->FileName.Buffer = NULL;
403         FileObject->FileName.MaximumLength = FileObject->FileName.Length = 0;
404 
405         ObDereferenceObject( FileObject );
406     }
407 }
408 
409 
410 NTSTATUS
CdCompleteMdl(_In_ PIRP_CONTEXT IrpContext,_Inout_ PIRP Irp)411 CdCompleteMdl (
412     _In_ PIRP_CONTEXT IrpContext,
413     _Inout_ PIRP Irp
414     )
415 
416 /*++
417 
418 Routine Description:
419 
420     This routine performs the function of completing Mdl reads.
421     It should be called only from CdFsdRead.
422 
423 Arguments:
424 
425     Irp - Supplies the originating Irp.
426 
427 Return Value:
428 
429     NTSTATUS - Will always be STATUS_SUCCESS.
430 
431 --*/
432 
433 {
434     PFILE_OBJECT FileObject;
435 
436     PAGED_CODE();
437 
438     //
439     // Do completion processing.
440     //
441 
442     FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject;
443 
444     CcMdlReadComplete( FileObject, Irp->MdlAddress );
445 
446     //
447     // Mdl is now deallocated.
448     //
449 
450     Irp->MdlAddress = NULL;
451 
452     //
453     // Complete the request and exit right away.
454     //
455 
456     CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
457 
458     return STATUS_SUCCESS;
459 }
460 
461 
462 
_Requires_lock_held_(_Global_critical_region_)463 _Requires_lock_held_(_Global_critical_region_)
464 NTSTATUS
465 CdPurgeVolume (
466     _In_ PIRP_CONTEXT IrpContext,
467     _In_ PVCB Vcb,
468     _In_ BOOLEAN DismountUnderway
469     )
470 
471 /*++
472 
473 Routine Description:
474 
475     This routine is called to purge the volume.  The purpose is to make all the stale file
476     objects in the system go away in order to lock the volume.
477 
478     The Vcb is already acquired exclusively.  We will lock out all file operations by
479     acquiring the global file resource.  Then we will walk through all of the Fcb's and
480     perform the purge.
481 
482 Arguments:
483 
484     Vcb - Vcb for the volume to purge.
485 
486     DismountUnderway - Indicates that we are trying to delete all of the objects.
487         We will purge the Path Table and VolumeDasd and dereference all
488         internal streams.
489 
490 Return Value:
491 
492     NTSTATUS - The first failure of the purge operation.
493 
494 --*/
495 
496 {
497     NTSTATUS Status = STATUS_SUCCESS;
498 
499     PVOID RestartKey = NULL;
500     PFCB ThisFcb = NULL;
501     PFCB NextFcb;
502 
503     BOOLEAN RemovedFcb;
504 
505     PAGED_CODE();
506 
507     ASSERT_EXCLUSIVE_VCB( Vcb);
508 
509     //
510     //  Force any remaining Fcb's in the delayed close queue to be closed.
511     //
512 
513     CdFspClose( Vcb );
514 
515     //
516     //  Acquire the global file resource.
517     //
518 
519     CdAcquireAllFiles( IrpContext, Vcb );
520 
521     //
522     //  Loop through each Fcb in the Fcb Table and perform the flush.
523     //
524 
525     while (TRUE) {
526 
527         //
528         //  Lock the Vcb to lookup the next Fcb.
529         //
530 
531         CdLockVcb( IrpContext, Vcb );
532         NextFcb = CdGetNextFcb( IrpContext, Vcb, &RestartKey );
533 
534         //
535         //  Reference the NextFcb if present.
536         //
537 
538         if (NextFcb != NULL) {
539 
540             NextFcb->FcbReference += 1;
541         }
542 
543         //
544         //  If the last Fcb is present then decrement reference count and call teardown
545         //  to see if it should be removed.
546         //
547 
548         if (ThisFcb != NULL) {
549 
550             ThisFcb->FcbReference -= 1;
551 
552             CdUnlockVcb( IrpContext, Vcb );
553 
554             CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb );
555 
556         } else {
557 
558             CdUnlockVcb( IrpContext, Vcb );
559         }
560 
561         //
562         //  Break out of the loop if no more Fcb's.
563         //
564 
565         if (NextFcb == NULL) {
566 
567             break;
568         }
569 
570         //
571         //  Move to the next Fcb.
572         //
573 
574         ThisFcb = NextFcb;
575 
576         //
577         //  If there is a image section then see if that can be closed.
578         //
579 
580         if (ThisFcb->FcbNonpaged->SegmentObject.ImageSectionObject != NULL) {
581 
582             MmFlushImageSection( &ThisFcb->FcbNonpaged->SegmentObject, MmFlushForWrite );
583         }
584 
585         //
586         //  If there is a data section then purge this.  If there is an image
587         //  section then we won't be able to.  Remember this if it is our first
588         //  error.
589         //
590 
591         if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
592             !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
593                                    NULL,
594                                    0,
595                                    FALSE ) &&
596             (Status == STATUS_SUCCESS)) {
597 
598             Status = STATUS_UNABLE_TO_DELETE_SECTION;
599         }
600 
601         //
602         //  Dereference the internal stream if dismounting.
603         //
604 
605         if (DismountUnderway &&
606             (SafeNodeType( ThisFcb ) != CDFS_NTC_FCB_DATA) &&
607             (ThisFcb->FileObject != NULL)) {
608 
609             CdDeleteInternalStream( IrpContext, ThisFcb );
610         }
611     }
612 
613     //
614     //  Now look at the path table and volume Dasd Fcb's.
615     //
616 
617     if (DismountUnderway) {
618 
619         if (Vcb->PathTableFcb != NULL) {
620 
621             ThisFcb = Vcb->PathTableFcb;
622             InterlockedIncrement( (LONG*)&Vcb->PathTableFcb->FcbReference );
623 
624             if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
625                 !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
626                                        NULL,
627                                        0,
628                                        FALSE ) &&
629                 (Status == STATUS_SUCCESS)) {
630 
631                 Status = STATUS_UNABLE_TO_DELETE_SECTION;
632             }
633 
634             CdDeleteInternalStream( IrpContext, ThisFcb );
635 
636             InterlockedDecrement( (LONG*)&ThisFcb->FcbReference );
637 
638             CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb );
639         }
640 
641         if (Vcb->VolumeDasdFcb != NULL) {
642 
643             ThisFcb = Vcb->VolumeDasdFcb;
644             InterlockedIncrement( (LONG*)&ThisFcb->FcbReference );
645 
646             if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
647                 !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
648                                        NULL,
649                                        0,
650                                        FALSE ) &&
651                 (Status == STATUS_SUCCESS)) {
652 
653                 Status = STATUS_UNABLE_TO_DELETE_SECTION;
654             }
655 
656             InterlockedDecrement( (LONG*)&ThisFcb->FcbReference );
657 
658             CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb );
659         }
660     }
661 
662     //
663     //  Release all of the files.
664     //
665 
666     CdReleaseAllFiles( IrpContext, Vcb );
667 
668     return Status;
669 }
670 
671 
672