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