3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the Fat Resource acquisition routines
19 #pragma alloc_text(PAGE, FatAcquireFcbForLazyWrite)
20 #pragma alloc_text(PAGE, FatAcquireFcbForReadAhead)
21 #pragma alloc_text(PAGE, FatAcquireExclusiveFcb)
22 #pragma alloc_text(PAGE, FatAcquireSharedFcb)
23 #pragma alloc_text(PAGE, FatAcquireSharedFcbWaitForEx)
24 #pragma alloc_text(PAGE, FatAcquireExclusiveVcb_Real)
25 #pragma alloc_text(PAGE, FatAcquireSharedVcb)
26 #pragma alloc_text(PAGE, FatNoOpAcquire)
27 #pragma alloc_text(PAGE, FatNoOpRelease)
28 #pragma alloc_text(PAGE, FatReleaseFcbFromLazyWrite)
29 #pragma alloc_text(PAGE, FatReleaseFcbFromReadAhead)
30 #pragma alloc_text(PAGE, FatAcquireForCcFlush)
31 #pragma alloc_text(PAGE, FatReleaseForCcFlush)
32 #pragma alloc_text(PAGE, FatFilterCallbackAcquireForCreateSection)
35 _Requires_lock_held_(_Global_critical_region_
)
36 _When_(return != FALSE
&& NoOpCheck
!= FALSE
, _Acquires_exclusive_lock_(Vcb
->Resource
))
38 FatAcquireExclusiveVcb_Real (
39 IN PIRP_CONTEXT IrpContext
,
48 This routine acquires exclusive access to the Vcb.
50 After we acquire the resource check to see if this operation is legal.
51 If it isn't (ie. we get an exception), release the resource.
55 Vcb - Supplies the Vcb to acquire
57 NoOpCheck - if TRUE then don't do any verification of the request/volume state.
61 FINISHED - TRUE if we have the resource and FALSE if we needed to block
62 for the resource but Wait is FALSE.
70 #pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
72 if (ExAcquireResourceExclusiveLite( &Vcb
->Resource
, BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
))) {
78 FatVerifyOperationIsLegal( IrpContext
);
82 if ( _SEH2_AbnormalTermination() ) {
84 FatReleaseVcb( IrpContext
, Vcb
);
95 _Requires_lock_held_(_Global_critical_region_
)
96 _When_(return != 0, _Acquires_shared_lock_(Vcb
->Resource
))
99 IN PIRP_CONTEXT IrpContext
,
107 This routine acquires shared access to the Vcb.
109 After we acquire the resource check to see if this operation is legal.
110 If it isn't (ie. we get an exception), release the resource.
114 Vcb - Supplies the Vcb to acquire
118 FINISHED - TRUE if we have the resource and FALSE if we needed to block
119 for the resource but Wait is FALSE.
126 if (ExAcquireResourceSharedLite( &Vcb
->Resource
,
127 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
))) {
131 FatVerifyOperationIsLegal( IrpContext
);
135 if ( _SEH2_AbnormalTermination() ) {
137 FatReleaseVcb( IrpContext
, Vcb
);
147 _Requires_lock_held_(_Global_critical_region_
)
148 _Acquires_exclusive_lock_(*Fcb
->Header
.Resource
)
150 FatAcquireExclusiveFcb (
151 IN PIRP_CONTEXT IrpContext
,
159 This routine acquires exclusive access to the Fcb.
161 After we acquire the resource check to see if this operation is legal.
162 If it isn't (ie. we get an exception), release the resource.
166 Fcb - Supplies the Fcb to acquire
170 FINISHED - TRUE if we have the resource and FALSE if we needed to block
171 for the resource but Wait is FALSE.
181 #pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
183 if (ExAcquireResourceExclusiveLite( Fcb
->Header
.Resource
, BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
))) {
186 // Check for anything other than a non-cached write if the
187 // async count is non-zero in the Fcb, or if others are waiting
188 // for the resource. Then wait for all outstanding I/O to finish,
189 // drop the resource, and wait again.
192 if ((Fcb
->NonPaged
->OutstandingAsyncWrites
!= 0) &&
193 ((IrpContext
->MajorFunction
!= IRP_MJ_WRITE
) ||
194 !FlagOn(IrpContext
->OriginatingIrp
->Flags
, IRP_NOCACHE
) ||
195 (ExGetSharedWaiterCount(Fcb
->Header
.Resource
) != 0) ||
196 (ExGetExclusiveWaiterCount(Fcb
->Header
.Resource
) != 0))) {
198 KeWaitForSingleObject( Fcb
->NonPaged
->OutstandingAsyncEvent
,
202 (PLARGE_INTEGER
) NULL
);
204 FatReleaseFcb( IrpContext
, Fcb
);
206 goto RetryFcbExclusive
;
211 FatVerifyOperationIsLegal( IrpContext
);
215 if ( _SEH2_AbnormalTermination() ) {
217 FatReleaseFcb( IrpContext
, Fcb
);
228 _Requires_lock_held_(_Global_critical_region_
)
229 _Acquires_shared_lock_(*Fcb
->Header
.Resource
)
231 FatAcquireSharedFcb (
232 IN PIRP_CONTEXT IrpContext
,
240 This routine acquires shared access to the Fcb.
242 After we acquire the resource check to see if this operation is legal.
243 If it isn't (ie. we get an exception), release the resource.
247 Fcb - Supplies the Fcb to acquire
251 FINISHED - TRUE if we have the resource and FALSE if we needed to block
252 for the resource but Wait is FALSE.
261 if (ExAcquireResourceSharedLite( Fcb
->Header
.Resource
, BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
))) {
264 // Check for anything other than a non-cached write if the
265 // async count is non-zero in the Fcb, or if others are waiting
266 // for the resource. Then wait for all outstanding I/O to finish,
267 // drop the resource, and wait again.
270 if ((Fcb
->NonPaged
->OutstandingAsyncWrites
!= 0) &&
271 ((IrpContext
->MajorFunction
!= IRP_MJ_WRITE
) ||
272 !FlagOn(IrpContext
->OriginatingIrp
->Flags
, IRP_NOCACHE
) ||
273 (ExGetSharedWaiterCount(Fcb
->Header
.Resource
) != 0) ||
274 (ExGetExclusiveWaiterCount(Fcb
->Header
.Resource
) != 0))) {
276 KeWaitForSingleObject( Fcb
->NonPaged
->OutstandingAsyncEvent
,
280 (PLARGE_INTEGER
) NULL
);
282 FatReleaseFcb( IrpContext
, Fcb
);
289 FatVerifyOperationIsLegal( IrpContext
);
293 if ( _SEH2_AbnormalTermination() ) {
295 FatReleaseFcb( IrpContext
, Fcb
);
309 _Requires_lock_held_(_Global_critical_region_
)
310 _When_(return != 0, _Acquires_shared_lock_(*Fcb
->Header
.Resource
))
312 FatAcquireSharedFcbWaitForEx (
313 IN PIRP_CONTEXT IrpContext
,
321 This routine acquires shared access to the Fcb, waiting first for any
322 exclusive accessors to get the Fcb first.
324 After we acquire the resource check to see if this operation is legal.
325 If it isn't (ie. we get an exception), release the resource.
329 Fcb - Supplies the Fcb to acquire
333 FINISHED - TRUE if we have the resource and FALSE if we needed to block
334 for the resource but Wait is FALSE.
341 NT_ASSERT( FlagOn(IrpContext
->OriginatingIrp
->Flags
, IRP_NOCACHE
) );
342 NT_ASSERT( !FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
344 RetryFcbSharedWaitEx
:
346 if (ExAcquireSharedWaitForExclusive( Fcb
->Header
.Resource
, FALSE
)) {
349 // Check for anything other than a non-cached write if the
350 // async count is non-zero in the Fcb. Then wait for all
351 // outstanding I/O to finish, drop the resource, and wait again.
354 if ((Fcb
->NonPaged
->OutstandingAsyncWrites
!= 0) &&
355 (IrpContext
->MajorFunction
!= IRP_MJ_WRITE
)) {
357 KeWaitForSingleObject( Fcb
->NonPaged
->OutstandingAsyncEvent
,
361 (PLARGE_INTEGER
) NULL
);
363 FatReleaseFcb( IrpContext
, Fcb
);
365 goto RetryFcbSharedWaitEx
;
370 FatVerifyOperationIsLegal( IrpContext
);
374 if ( _SEH2_AbnormalTermination() ) {
376 FatReleaseFcb( IrpContext
, Fcb
);
390 _Requires_lock_held_(_Global_critical_region_
)
393 FatAcquireFcbForLazyWrite (
402 The address of this routine is specified when creating a CacheMap for
403 a file. It is subsequently called by the Lazy Writer prior to its
404 performing lazy writes to the file.
408 Fcb - The Fcb which was specified as a context parameter for this
411 Wait - TRUE if the caller is willing to block.
415 FALSE - if Wait was specified as FALSE and blocking would have
416 been required. The Fcb is not acquired.
418 TRUE - if the Fcb has been acquired
426 // Check here for the EA File. It turns out we need the normal
427 // resource shared in this case. Otherwise we take the paging
428 // I/O resource shared.
432 // Note that we do not need to disable APC delivery to guard
433 // against a rogue user issuing a suspend APC. That is because
434 // it is guaranteed that the caller is either in the system context,
435 // to which a user cannot deliver a suspend APC, or the caller has
436 // already disabled kernel APC delivery before calling. This is true
437 // for all the other pre-acquire routines as well.
440 if (!ExAcquireResourceSharedLite( Fcb
== ((PFCB
)Fcb
)->Vcb
->EaFcb
?
441 ((PFCB
)Fcb
)->Header
.Resource
:
442 ((PFCB
)Fcb
)->Header
.PagingIoResource
,
449 // We assume the Lazy Writer only acquires this Fcb once.
450 // Therefore, it should be guaranteed that this flag is currently
451 // clear (the ASSERT), and then we will set this flag, to insure
452 // that the Lazy Writer will never try to advance Valid Data, and
453 // also not deadlock by trying to get the Fcb exclusive.
457 NT_ASSERT( NodeType(((PFCB
)Fcb
)) == FAT_NTC_FCB
);
458 NT_ASSERT( ((PFCB
)Fcb
)->Specific
.Fcb
.LazyWriteThread
== NULL
);
460 ((PFCB
)Fcb
)->Specific
.Fcb
.LazyWriteThread
= PsGetCurrentThread();
462 NT_ASSERT( NULL
!= PsGetCurrentThread() );
464 if (NULL
== FatData
.LazyWriteThread
) {
466 FatData
.LazyWriteThread
= PsGetCurrentThread();
470 // This is a kludge because Cc is really the top level. When it
471 // enters the file system, we will think it is a resursive call
472 // and complete the request with hard errors or verify. It will
473 // then have to deal with them, somehow....
476 NT_ASSERT(IoGetTopLevelIrp() == NULL
);
478 IoSetTopLevelIrp((PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
484 _Requires_lock_held_(_Global_critical_region_
)
487 FatReleaseFcbFromLazyWrite (
495 The address of this routine is specified when creating a CacheMap for
496 a file. It is subsequently called by the Lazy Writer after its
497 performing lazy writes to the file.
501 Fcb - The Fcb which was specified as a context parameter for this
514 // Assert that this really is an fcb and that this thread really owns
515 // the lazy writer mark in the fcb.
518 NT_ASSERT( NodeType(((PFCB
)Fcb
)) == FAT_NTC_FCB
);
519 NT_ASSERT( NULL
!= PsGetCurrentThread() );
520 NT_ASSERT( ((PFCB
)Fcb
)->Specific
.Fcb
.LazyWriteThread
== PsGetCurrentThread() );
523 // Release the lazy writer mark.
526 ((PFCB
)Fcb
)->Specific
.Fcb
.LazyWriteThread
= NULL
;
529 // Check here for the EA File. It turns out we needed the normal
530 // resource shared in this case. Otherwise it was the PagingIoResource.
533 ExReleaseResourceLite( Fcb
== ((PFCB
)Fcb
)->Vcb
->EaFcb
?
534 ((PFCB
)Fcb
)->Header
.Resource
:
535 ((PFCB
)Fcb
)->Header
.PagingIoResource
);
538 // Clear the kludge at this point.
541 NT_ASSERT(IoGetTopLevelIrp() == (PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
543 IoSetTopLevelIrp( NULL
);
549 _Requires_lock_held_(_Global_critical_region_
)
552 FatAcquireFcbForReadAhead (
561 The address of this routine is specified when creating a CacheMap for
562 a file. It is subsequently called by the Lazy Writer prior to its
563 performing read ahead to the file.
567 Fcb - The Fcb which was specified as a context parameter for this
570 Wait - TRUE if the caller is willing to block.
574 FALSE - if Wait was specified as FALSE and blocking would have
575 been required. The Fcb is not acquired.
577 TRUE - if the Fcb has been acquired
585 // We acquire the normal file resource shared here to synchronize
586 // correctly with purges.
590 // Note that we do not need to disable APC delivery to guard
591 // against a rogue user issuing a suspend APC. That is because
592 // it is guaranteed that the caller is either in the system context,
593 // to which a user cannot deliver a suspend APC, or the caller has
594 // already disabled kernel APC delivery before calling. This is true
595 // for all the other pre-acquire routines as well.
598 if (!ExAcquireResourceSharedLite( ((PFCB
)Fcb
)->Header
.Resource
,
605 // This is a kludge because Cc is really the top level. We it
606 // enters the file system, we will think it is a resursive call
607 // and complete the request with hard errors or verify. It will
608 // have to deal with them, somehow....
611 NT_ASSERT(IoGetTopLevelIrp() == NULL
);
613 IoSetTopLevelIrp((PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
619 _Requires_lock_held_(_Global_critical_region_
)
622 FatReleaseFcbFromReadAhead (
630 The address of this routine is specified when creating a CacheMap for
631 a file. It is subsequently called by the Lazy Writer after its
636 Fcb - The Fcb which was specified as a context parameter for this
649 // Clear the kludge at this point.
652 NT_ASSERT(IoGetTopLevelIrp() == (PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
654 IoSetTopLevelIrp( NULL
);
656 ExReleaseResourceLite( ((PFCB
)Fcb
)->Header
.Resource
);
662 _Function_class_(FAST_IO_ACQUIRE_FOR_CCFLUSH
)\f
663 _Requires_lock_held_(_Global_critical_region_
)
666 FatAcquireForCcFlush (
667 IN PFILE_OBJECT FileObject
,
668 IN PDEVICE_OBJECT DeviceObject
674 PFSRTL_COMMON_FCB_HEADER Header
;
678 UNREFERENCED_PARAMETER( DeviceObject
);
681 // Once again, the hack for making this look like
682 // a recursive call if needed. We cannot let ourselves
683 // verify under something that has resources held.
685 // This value is good. We should never try to acquire
686 // the file this way underneath of the cache.
689 NT_ASSERT( IoGetTopLevelIrp() != (PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
691 if (IoGetTopLevelIrp() == NULL
) {
693 IoSetTopLevelIrp((PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
697 // Time for some exposition.
699 // Lockorder for FAT is main->bcb->pagingio. Invert this at your obvious peril.
700 // The default logic for AcquireForCcFlush breaks this since in writethrough
701 // unpinrepinned we will grab the bcb then Mm will use the callback (which
702 // orders us with respect to the MmCollidedFlushEvent) to help us. If for
703 // directories/ea we then grab the main we are out of order.
705 // Fortunately, we do not need main. We only need paging - just look at the write
706 // path. This is basic pre-acquisition.
708 // Regular files require both resources, and are safe since we never pin them.
712 // Note that we do not need to disable APC delivery to guard
713 // against a rogue user issuing a suspend APC. That is because
714 // it is guaranteed that the caller is either in the system context,
715 // to which a user cannot deliver a suspend APC, or the caller has
716 // already disabled kernel APC delivery before calling. This is true
717 // for all the other pre-acquire routines as well.
720 Type
= FatDecodeFileObject( FileObject
, &Vcb
, &Fcb
, &Ccb
);
721 Header
= (PFSRTL_COMMON_FCB_HEADER
) FileObject
->FsContext
;
723 if (Type
< DirectoryFile
) {
725 if (Header
->Resource
) {
727 if (!ExIsResourceAcquiredSharedLite( Header
->Resource
)) {
729 ExAcquireResourceExclusiveLite( Header
->Resource
, TRUE
);
733 ExAcquireResourceSharedLite( Header
->Resource
, TRUE
);
738 if (Header
->PagingIoResource
) {
740 ExAcquireResourceSharedLite( Header
->PagingIoResource
, TRUE
);
743 return STATUS_SUCCESS
;
747 _Requires_lock_held_(_Global_critical_region_
)
750 FatReleaseForCcFlush (
751 IN PFILE_OBJECT FileObject
,
752 IN PDEVICE_OBJECT DeviceObject
758 PFSRTL_COMMON_FCB_HEADER Header
;
762 UNREFERENCED_PARAMETER( DeviceObject
);
765 // Clear up our hint.
768 if (IoGetTopLevelIrp() == (PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
) {
770 IoSetTopLevelIrp( NULL
);
773 Type
= FatDecodeFileObject( FileObject
, &Vcb
, &Fcb
, &Ccb
);
774 Header
= (PFSRTL_COMMON_FCB_HEADER
) FileObject
->FsContext
;
776 if (Type
< DirectoryFile
) {
778 if (Header
->Resource
) {
780 ExReleaseResourceLite( Header
->Resource
);
784 if (Header
->PagingIoResource
) {
786 ExReleaseResourceLite( Header
->PagingIoResource
);
789 return STATUS_SUCCESS
;
804 This routine does nothing.
808 Fcb - The Fcb/Dcb/Vcb which was specified as a context parameter for this
811 Wait - TRUE if the caller is willing to block.
820 UNREFERENCED_PARAMETER( Fcb
);
821 UNREFERENCED_PARAMETER( Wait
);
826 // This is a kludge because Cc is really the top level. We it
827 // enters the file system, we will think it is a resursive call
828 // and complete the request with hard errors or verify. It will
829 // have to deal with them, somehow....
832 NT_ASSERT(IoGetTopLevelIrp() == NULL
);
834 IoSetTopLevelIrp((PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
850 This routine does nothing.
854 Fcb - The Fcb/Dcb/Vcb which was specified as a context parameter for this
867 // Clear the kludge at this point.
870 NT_ASSERT(IoGetTopLevelIrp() == (PIRP
)FSRTL_CACHE_TOP_LEVEL_IRP
);
872 IoSetTopLevelIrp( NULL
);
874 UNREFERENCED_PARAMETER( Fcb
);
880 _Requires_lock_held_(_Global_critical_region_
)
883 FatFilterCallbackAcquireForCreateSection (
884 IN PFS_FILTER_CALLBACK_DATA CallbackData
,
885 OUT PVOID
*CompletionContext
892 This is the callback routine for MM to use to acquire the file exclusively.
894 NOTE: This routine expects the default FSRTL routine to be used to release
895 the resource. If this routine is ever changed to acquire something
896 other than main, a corresponding release routine will be required.
900 FS_FILTER_CALLBACK_DATA - Filter based callback data that provides the file object we
903 CompletionContext - Ignored.
907 On success we return STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY.
909 If SyncType is SyncTypeCreateSection, we return a status that indicates whether there
910 are any writers to this file. Note that main is acquired, so new handles cannot be opened.
919 NT_ASSERT( CallbackData
->Operation
== FS_FILTER_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
);
920 NT_ASSERT( CallbackData
->SizeOfFsFilterCallbackData
== sizeof(FS_FILTER_CALLBACK_DATA
) );
923 // Grab the Fcb from the callback data file object.
926 Fcb
= CallbackData
->FileObject
->FsContext
;
929 // Take main exclusive.
933 // Note that we do not need to disable APC delivery to guard
934 // against a rogue user issuing a suspend APC. That is because
935 // it is guaranteed that the caller is either in the system context,
936 // to which a user cannot deliver a suspend APC, or the caller has
937 // already disabled kernel APC delivery before calling. This is true
938 // for all the other pre-acquire routines as well.
941 if (Fcb
->Header
.Resource
) {
943 ExAcquireResourceExclusiveLite( Fcb
->Header
.Resource
, TRUE
);
947 // Return the appropriate status based on the type of synchronization and whether anyone
948 // has write access to this file.
951 if (CallbackData
->Parameters
.AcquireForSectionSynchronization
.SyncType
!= SyncTypeCreateSection
) {
953 return STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY
;
955 } else if (Fcb
->ShareAccess
.Writers
== 0) {
957 return STATUS_FILE_LOCKED_WITH_ONLY_READERS
;
961 return STATUS_FILE_LOCKED_WITH_WRITERS
;
964 UNREFERENCED_PARAMETER( CompletionContext
);