ReactOS 0.4.16-dev-334-g4d9f67c
pnp.c
Go to the documentation of this file.
1/*++
2
3Copyright (c) 1997-2000 Microsoft Corporation
4
5Module Name:
6
7 Pnp.c
8
9Abstract:
10
11 This module implements the Plug and Play routines for CDFS called by
12 the dispatch driver.
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_PNP)
24
25_Requires_lock_held_(_Global_critical_region_)
28CdPnpQueryRemove (
29 _Inout_ PIRP_CONTEXT IrpContext,
32 );
33
34_Requires_lock_held_(_Global_critical_region_)
37CdPnpRemove (
38 _Inout_ PIRP_CONTEXT IrpContext,
41 );
42
43_Requires_lock_held_(_Global_critical_region_)
46CdPnpSurpriseRemove (
47 _Inout_ PIRP_CONTEXT IrpContext,
50 );
51
52_Requires_lock_held_(_Global_critical_region_)
55CdPnpCancelRemove (
56 _Inout_ PIRP_CONTEXT IrpContext,
59 );
60
61// Tell prefast this is a completion routine.
62IO_COMPLETION_ROUTINE CdPnpCompletionRoutine;
63
65NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
69 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
70 );
71
72#ifdef ALLOC_PRAGMA
73#pragma alloc_text(PAGE, CdCommonPnp)
74#pragma alloc_text(PAGE, CdPnpCancelRemove)
75#pragma alloc_text(PAGE, CdPnpQueryRemove)
76#pragma alloc_text(PAGE, CdPnpRemove)
77#pragma alloc_text(PAGE, CdPnpSurpriseRemove)
78#endif
79
80_Requires_lock_held_(_Global_critical_region_)
82CdCommonPnp (
83 _Inout_ PIRP_CONTEXT IrpContext,
85 )
86
87/*++
88
89Routine Description:
90
91 This is the common routine for doing PnP operations called
92 by both the fsd and fsp threads
93
94Arguments:
95
96 Irp - Supplies the Irp to process
97
98Return Value:
99
100 NTSTATUS - The return status for the operation
101
102--*/
103
104{
106 BOOLEAN PassThrough = FALSE;
107
109
110 PVOLUME_DEVICE_OBJECT OurDeviceObject;
111 PVCB Vcb;
112
113 PAGED_CODE();
114
115 // Global lock object is acquired based on internal book-keeping
117
118 //
119 // Get the current Irp stack location.
120 //
121
123
124 //
125 // Find our Vcb. This is tricky since we have no file object in the Irp.
126 //
127
128 OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject;
129
130 //
131 // IO holds a handle reference on our VDO and holds the device lock, which
132 // syncs us against mounts/verifies. However we hold no reference on the
133 // volume, which may already have been torn down (and the Vpb freed), for
134 // example by a force dismount. Check for this condition. We must hold this
135 // lock until the pnp worker functions take additional locks/refs on the Vcb.
136 //
137
138 CdAcquireCdData( IrpContext);
139
140 //
141 // Make sure this device object really is big enough to be a volume device
142 // object. If it isn't, we need to get out before we try to reference some
143 // field that takes us past the end of an ordinary device object.
144 //
145
146#ifdef _MSC_VER
147#pragma prefast(suppress: 28175, "this is a filesystem driver, touching the size member is allowed")
148#endif
149 if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
150 NodeType( &OurDeviceObject->Vcb ) != CDFS_NTC_VCB) {
151
152 //
153 // We were called with something we don't understand.
154 //
155
157 CdReleaseCdData( IrpContext);
158 CdCompleteRequest( IrpContext, Irp, Status );
159 return Status;
160 }
161
162 //
163 // Force all PnP operations to be synchronous.
164 //
165
166 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
167
168 Vcb = &OurDeviceObject->Vcb;
169
170 //
171 // Check that the Vcb hasn't already been deleted. If so, just pass the
172 // request through to the driver below, we don't need to do anything.
173 //
174
175 if (NULL == Vcb->Vpb) {
176
177 PassThrough = TRUE;
178 }
179 else {
180
181 //
182 // Case on the minor code.
183 //
184
185 switch ( IrpSp->MinorFunction ) {
186
188
189 Status = CdPnpQueryRemove( IrpContext, Irp, Vcb );
190 break;
191
193
194 Status = CdPnpSurpriseRemove( IrpContext, Irp, Vcb );
195 break;
196
198
199 Status = CdPnpRemove( IrpContext, Irp, Vcb );
200 break;
201
203
204 Status = CdPnpCancelRemove( IrpContext, Irp, Vcb );
205 break;
206
207 default:
208
209 PassThrough = TRUE;
210 break;
211 }
212 }
213
214 if (PassThrough) {
215
216 CdReleaseCdData( IrpContext);
217
218 //
219 // Just pass the IRP on. As we do not need to be in the
220 // way on return, ellide ourselves out of the stack.
221 //
222
224
225 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
226
227 //
228 // Cleanup our Irp Context. The driver has completed the Irp.
229 //
230
231 CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
232 }
233
234 return Status;
235}
236
237_Requires_lock_held_(_Global_critical_region_)
240CdPnpQueryRemove (
241 _Inout_ PIRP_CONTEXT IrpContext,
244 )
245
246/*++
247
248Routine Description:
249
250 This routine handles the PnP query remove operation. The filesystem
251 is responsible for answering whether there are any reasons it sees
252 that the volume can not go away (and the device removed). Initiation
253 of the dismount begins when we answer yes to this question.
254
255 Query will be followed by a Cancel or Remove.
256
257Arguments:
258
259 Irp - Supplies the Irp to process
260
261 Vcb - Supplies the volume being queried.
262
263Return Value:
264
265 NTSTATUS - The return status for the operation
266
267--*/
268
269{
272 BOOLEAN VcbPresent = TRUE;
273
274 PAGED_CODE();
275
277
278 //
279 // Having said yes to a QUERY, any communication with the
280 // underlying storage stack is undefined (and may block)
281 // until the bounding CANCEL or REMOVE is sent.
282 //
283 // Acquire the global resource so that we can try to vaporize the volume,
284 // and the vcb resource itself.
285 //
286
287 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
288
289 //
290 // Drop a reference on the Vcb to keep it around after we drop the locks.
291 //
292
293 CdLockVcb( IrpContext, Vcb);
294 Vcb->VcbReference += 1;
295 CdUnlockVcb( IrpContext, Vcb);
296
297 CdReleaseCdData( IrpContext);
298
299 Status = CdLockVolumeInternal( IrpContext, Vcb, NULL );
300
301 //
302 // Reacquire the global lock, which means dropping the Vcb resource.
303 //
304
305 CdReleaseVcb( IrpContext, Vcb );
306
307 CdAcquireCdData( IrpContext );
308 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
309
310 //
311 // Remove our extra reference.
312 //
313
314 CdLockVcb( IrpContext, Vcb);
315 Vcb->VcbReference -= 1;
316 CdUnlockVcb( IrpContext, Vcb);
317
318 if (NT_SUCCESS( Status )) {
319
320 //
321 // We need to pass this down before starting the dismount, which
322 // could disconnect us immediately from the stack.
323 //
324
325 //
326 // Get the next stack location, and copy over the stack location
327 //
328
330
331 //
332 // Set up the completion routine
333 //
334
338 &Event,
339 TRUE,
340 TRUE,
341 TRUE );
342
343 //
344 // Send the request and wait.
345 //
346
347 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
348
349 if (Status == STATUS_PENDING) {
350
352 Executive,
354 FALSE,
355 NULL );
356
357 Status = Irp->IoStatus.Status;
358 }
359
360 //
361 // Now if no one below us failed already, initiate the dismount
362 // on this volume, make it go away. PnP needs to see our internal
363 // streams close and drop their references to the target device.
364 //
365 // Since we were able to lock the volume, we are guaranteed to
366 // move this volume into dismount state and disconnect it from
367 // the underlying storage stack. The force on our part is actually
368 // unnecesary, though complete.
369 //
370 // What is not strictly guaranteed, though, is that the closes
371 // for the metadata streams take effect synchronously underneath
372 // of this call. This would leave references on the target device
373 // even though we are disconnected!
374 //
375
376 if (NT_SUCCESS( Status )) {
377
378 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE );
379
380 NT_ASSERT( !VcbPresent || Vcb->VcbCondition == VcbDismountInProgress );
381 }
382
383 //
384 // Note: Normally everything will complete and the internal streams will
385 // vaporise. However there is some code in the system which drops additional
386 // references on fileobjects, including our internal stream file objects,
387 // for (WMI) tracing purposes. If that happens to run concurrently with our
388 // teardown, our internal streams will not vaporise until those references
389 // are removed. So it's possible that the volume still remains at this
390 // point. The pnp query remove will fail due to our references on the device.
391 // To be cleaner we will return an error here. We could pend the pnp
392 // IRP until the volume goes away, but since we don't know when that will
393 // be, and this is a very rare case, we'll just fail the query.
394 //
395 // The reason this is the case is that handles/fileobjects place a reference
396 // on the device objects they overly. In the filesystem case, these references
397 // are on our target devices. PnP correcly thinks that if references remain
398 // on the device objects in the stack that someone has a handle, and that this
399 // counts as a reason to not succeed the query - even though every interrogated
400 // driver thinks that it is OK.
401 //
402
403 if (NT_SUCCESS( Status) && VcbPresent && (Vcb->VcbReference != 0)) {
404
406 }
407 }
408
409 //
410 // Release the Vcb if it could still remain.
411 //
412
413 if (VcbPresent) {
414
415 CdReleaseVcb( IrpContext, Vcb );
416 }
417 else {
419 }
420
421 CdReleaseCdData( IrpContext );
422
423 //
424 // Cleanup our IrpContext and complete the IRP if neccesary.
425 //
426
427 CdCompleteRequest( IrpContext, Irp, Status );
428
429 return Status;
430}
431
432_Requires_lock_held_(_Global_critical_region_)
435CdPnpRemove (
436 _Inout_ PIRP_CONTEXT IrpContext,
439 )
440
441/*++
442
443Routine Description:
444
445 This routine handles the PnP remove operation. This is our notification
446 that the underlying storage device for the volume we have is gone, and
447 an excellent indication that the volume will never reappear. The filesystem
448 is responsible for initiation or completion the dismount.
449
450Arguments:
451
452 Irp - Supplies the Irp to process
453
454 Vcb - Supplies the volume being removed.
455
456Return Value:
457
458 NTSTATUS - The return status for the operation
459
460--*/
461
462{
465 BOOLEAN VcbPresent = TRUE;
466
467 PAGED_CODE();
468
470
471 //
472 // REMOVE - a storage device is now gone. We either got
473 // QUERY'd and said yes OR got a SURPRISE OR a storage
474 // stack failed to spin back up from a sleep/stop state
475 // (the only case in which this will be the first warning).
476 //
477 // Note that it is entirely unlikely that we will be around
478 // for a REMOVE in the first two cases, as we try to intiate
479 // dismount.
480 //
481
482 //
483 // Acquire the global resource so that we can try to vaporize
484 // the volume, and the vcb resource itself.
485 //
486
487 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
488
489 //
490 // The device will be going away. Remove our lock and find
491 // out if we ever had one in the first place.
492 //
493
494 Status = CdUnlockVolumeInternal( IrpContext, Vcb, NULL );
495
496 //
497 // If the volume had not been locked, we must invalidate the
498 // volume to ensure it goes away properly. The remove will
499 // succeed.
500 //
501
502 if (!NT_SUCCESS( Status )) {
503
504 CdLockVcb( IrpContext, Vcb );
505
506 if (Vcb->VcbCondition != VcbDismountInProgress) {
507
509 }
510
511 CdUnlockVcb( IrpContext, Vcb );
512
514 }
515
516 //
517 // We need to pass this down before starting the dismount, which
518 // could disconnect us immediately from the stack.
519 //
520
521 //
522 // Get the next stack location, and copy over the stack location
523 //
524
526
527 //
528 // Set up the completion routine
529 //
530
534 &Event,
535 TRUE,
536 TRUE,
537 TRUE );
538
539 //
540 // Send the request and wait.
541 //
542
543 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
544
545 if (Status == STATUS_PENDING) {
546
548 Executive,
550 FALSE,
551 NULL );
552
553 Status = Irp->IoStatus.Status;
554 }
555
556 //
557 // Now make our dismount happen. This may not vaporize the
558 // Vcb, of course, since there could be any number of handles
559 // outstanding if we were not preceeded by a QUERY.
560 //
561 // PnP will take care of disconnecting this stack if we
562 // couldn't get off of it immediately.
563 //
564
565
566 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE );
567
568 //
569 // Release the Vcb if it could still remain.
570 //
571
572 if (VcbPresent) {
573
574 CdReleaseVcb( IrpContext, Vcb );
575 }
576 else {
578 }
579
580 CdReleaseCdData( IrpContext );
581
582 //
583 // Cleanup our IrpContext and complete the IRP.
584 //
585
586 CdCompleteRequest( IrpContext, Irp, Status );
587
588 return Status;
589}
590
591_Requires_lock_held_(_Global_critical_region_)
594CdPnpSurpriseRemove (
595 _Inout_ PIRP_CONTEXT IrpContext,
598 )
599
600/*++
601
602Routine Description:
603
604 This routine handles the PnP surprise remove operation. This is another
605 type of notification that the underlying storage device for the volume we
606 have is gone, and is excellent indication that the volume will never reappear.
607 The filesystem is responsible for initiation or completion the dismount.
608
609 For the most part, only "real" drivers care about the distinction of a
610 surprise remove, which is a result of our noticing that a user (usually)
611 physically reached into the machine and pulled something out.
612
613 Surprise will be followed by a Remove when all references have been shut down.
614
615Arguments:
616
617 Irp - Supplies the Irp to process
618
619 Vcb - Supplies the volume being removed.
620
621Return Value:
622
623 NTSTATUS - The return status for the operation
624
625--*/
626
627{
630 BOOLEAN VcbPresent = TRUE;
631
632 PAGED_CODE();
633
635
636 //
637 // SURPRISE - a device was physically yanked away without
638 // any warning. This means external forces.
639 //
640
641 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
642
643 //
644 // Invalidate the volume right now.
645 //
646 // The intent here is to make every subsequent operation
647 // on the volume fail and grease the rails toward dismount.
648 // By definition there is no going back from a SURPRISE.
649 //
650
651 CdLockVcb( IrpContext, Vcb );
652
653 if (Vcb->VcbCondition != VcbDismountInProgress) {
654
656 }
657
658 CdUnlockVcb( IrpContext, Vcb );
659
660 //
661 // We need to pass this down before starting the dismount, which
662 // could disconnect us immediately from the stack.
663 //
664
665 //
666 // Get the next stack location, and copy over the stack location
667 //
668
670
671 //
672 // Set up the completion routine
673 //
674
678 &Event,
679 TRUE,
680 TRUE,
681 TRUE );
682
683 //
684 // Send the request and wait.
685 //
686
687 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
688
689 if (Status == STATUS_PENDING) {
690
692 Executive,
694 FALSE,
695 NULL );
696
697 Status = Irp->IoStatus.Status;
698 }
699
700 //
701 // Now make our dismount happen. This may not vaporize the
702 // Vcb, of course, since there could be any number of handles
703 // outstanding since this is an out of band notification.
704 //
705
706
707 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE );
708
709 //
710 // Release the Vcb if it could still remain.
711 //
712
713 if (VcbPresent) {
714
715 CdReleaseVcb( IrpContext, Vcb );
716 }
717 else {
719 }
720
721 CdReleaseCdData( IrpContext );
722
723 //
724 // Cleanup our IrpContext and complete the IRP.
725 //
726
727 CdCompleteRequest( IrpContext, Irp, Status );
728
729 return Status;
730}
731
732_Requires_lock_held_(_Global_critical_region_)
735CdPnpCancelRemove (
736 _Inout_ PIRP_CONTEXT IrpContext,
739 )
740
741/*++
742
743Routine Description:
744
745 This routine handles the PnP cancel remove operation. This is our
746 notification that a previously proposed remove (query) was eventually
747 vetoed by a component. The filesystem is responsible for cleaning up
748 and getting ready for more IO.
749
750Arguments:
751
752 Irp - Supplies the Irp to process
753
754 Vcb - Supplies the volume being removed.
755
756Return Value:
757
758 NTSTATUS - The return status for the operation
759
760--*/
761
762{
764
765 PAGED_CODE();
766
768
769 //
770 // CANCEL - a previous QUERY has been rescinded as a result
771 // of someone vetoing. Since PnP cannot figure out who may
772 // have gotten the QUERY (think about it: stacked drivers),
773 // we must expect to deal with getting a CANCEL without having
774 // seen the QUERY.
775 //
776 // For CDFS, this is quite easy. In fact, we can't get a
777 // CANCEL if the underlying drivers succeeded the QUERY since
778 // we disconnect the Vpb on our dismount initiation. This is
779 // actually pretty important because if PnP could get to us
780 // after the disconnect we'd be thoroughly unsynchronized
781 // with respect to the Vcb getting torn apart - merely referencing
782 // the volume device object is insufficient to keep us intact.
783 //
784
785 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
786 CdReleaseCdData( IrpContext);
787
788 //
789 // Unlock the volume. This is benign if we never had seen
790 // a QUERY.
791 //
792
793 (VOID) CdUnlockVolumeInternal( IrpContext, Vcb, NULL );
794
795 CdReleaseVcb( IrpContext, Vcb );
796
797 //
798 // Send the request. The underlying driver will complete the
799 // IRP. Since we don't need to be in the way, simply ellide
800 // ourselves out of the IRP stack.
801 //
802
804
805 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
806
807 CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
808
809 return Status;
810}
811
812
813//
814// Local support routine
815//
816
818NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
821 _In_ PIRP Irp,
822 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
823 )
824{
825 PKEVENT Event = (PKEVENT) Contxt;
826 _Analysis_assume_(Contxt != NULL);
827
828 KeSetEvent( Event, 0, FALSE );
829
831
834 UNREFERENCED_PARAMETER( Contxt );
835}
836
837
static PIO_STACK_LOCATION IoGetCurrentIrpStackLocation(PIRP Irp)
#define PAGED_CODE()
unsigned char BOOLEAN
#define VOID
Definition: acefi.h:82
LONG NTSTATUS
Definition: precomp.h:26
VOID CdCompleteRequest(_Inout_opt_ PIRP_CONTEXT IrpContext, _Inout_opt_ PIRP Irp, _In_ NTSTATUS Status)
Definition: cddata.c:914
CD_DATA CdData
Definition: cddata.c:42
#define ASSERT_EXCLUSIVE_CDDATA
Definition: cddata.h:257
#define CdAcquireCdData(IC)
Definition: cdprocs.h:973
#define CdUpdateVcbCondition(V, C)
Definition: cdprocs.h:1449
#define CdReleaseVcb(IC, V)
Definition: cdprocs.h:985
NTSTATUS CdUnlockVolumeInternal(_In_ PIRP_CONTEXT IrpContext, _Inout_ PVCB Vcb, _In_opt_ PFILE_OBJECT FileObject)
Definition: fsctrl.c:288
#define CdLockVcb(IC, V)
Definition: cdprocs.h:1023
#define CdReleaseCdData(IC)
Definition: cdprocs.h:976
#define CdUnlockVcb(IC, V)
Definition: cdprocs.h:1028
#define CdAcquireVcbExclusive(IC, V, I)
Definition: cdprocs.h:979
#define IRP_CONTEXT_FLAG_WAIT
Definition: cdstruc.h:1215
VOLUME_DEVICE_OBJECT * PVOLUME_DEVICE_OBJECT
Definition: cdstruc.h:769
@ VcbDismountInProgress
Definition: cdstruc.h:494
@ VcbInvalid
Definition: cdstruc.h:493
#define _Analysis_assume_lock_not_held_(lock)
#define _Requires_lock_held_(lock)
#define _Releases_nonreentrant_lock_(lock)
#define _Analysis_suppress_lock_checking_(lock)
_In_ PIRP Irp
Definition: csq.h:116
#define STATUS_PENDING
Definition: d3dkmdt.h:43
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define CDFS_NTC_VCB
Definition: nodetype.h:28
#define NodeType(P)
Definition: nodetype.h:51
NTSTATUS NTAPI CdPnpCompletionRoutine(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt)
Definition: pnp.c:819
_In_ PIO_STACK_LOCATION IrpSp
Definition: create.c:4137
#define KeWaitForSingleObject(pEvt, foo, a, b, c)
Definition: env_spec_w32.h:478
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
#define PKEVENT
Definition: env_spec_w32.h:70
#define KeSetEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:476
#define SetFlag(_F, _SF)
Definition: ext2fs.h:187
Status
Definition: gdiplustypes.h:25
#define IoSetCompletionRoutine(_Irp, _CompletionRoutine, _Context, _InvokeOnSuccess, _InvokeOnError, _InvokeOnCancel)
Definition: irp.cpp:490
#define KernelMode
Definition: asm.h:34
#define _Inout_
Definition: no_sal2.h:162
#define _In_reads_opt_(s)
Definition: no_sal2.h:222
#define _In_
Definition: no_sal2.h:158
#define _Analysis_assume_
Definition: no_sal2.h:388
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:325
@ NotificationEvent
#define IRP_MN_SURPRISE_REMOVAL
Definition: ntifs_ex.h:408
#define IoSkipCurrentIrpStackLocation(Irp)
Definition: ntifs_ex.h:421
#define IoCopyCurrentIrpStackLocationToNext(Irp)
Definition: ntifs_ex.h:413
#define IoCallDriver
Definition: irp.c:1225
#define Vcb
Definition: cdprocs.h:1415
#define STATUS_MORE_PROCESSING_REQUIRED
Definition: shellext.h:68
#define STATUS_SUCCESS
Definition: shellext.h:65
ERESOURCE DataResource
Definition: cdstruc.h:402
PDEVICE_OBJECT DeviceObject
Definition: iotypes.h:3223
Definition: cdstruc.h:498
DEVICE_OBJECT DeviceObject
Definition: cdstruc.h:729
#define NTAPI
Definition: typedefs.h:36
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
#define STATUS_DEVICE_BUSY
Definition: udferr_usr.h:129
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
#define IRP_MN_REMOVE_DEVICE
#define IRP_MN_CANCEL_REMOVE_DEVICE
#define IRP_MN_QUERY_REMOVE_DEVICE
@ Executive
Definition: ketypes.h:415
#define NT_ASSERT
Definition: rtlfuncs.h:3327