WDK Mini Filter Example
ncdirnotify.c
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) 2009 Microsoft Corporation
4 
5 Module Name:
6 
7  ncdirnotify.c
8 
9 Abstract:
10 
11  Contains routines to process user-initiated directory change notifications.
12  Depending on path, we may need to suppress the real mapping and its
13  children from generating notifications to the user, or inject changes from
14  the user mapping to the user. In some cases we can optimize this merely
15  by transforming the real mapping to user mapping (if we are watching an
16  ancestor of both paths.)
17 
18 Environment:
19 
20  Kernel mode
21 
22 --*/
23 
24 #include "nc.h"
25 
26 //
27 // The following structures define a context which is allocated per sub-request.
28 // We record the type of the subrequest, and any state that is subrequest
29 // specific.
30 //
36 
41  PFLT_CALLBACK_DATA Request;
43 
44 NTSTATUS
46  _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext,
47  _In_ PFILE_OBJECT FileObject,
49  _In_ PFLT_CALLBACK_DATA Request,
50  _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext
51  );
52 
53 VOID
55  _In_ PNC_NOTIFY_REQUEST_CONTEXT NotifyRequestContext
56  );
57 
58 FLT_POSTOP_CALLBACK_STATUS
60  _Inout_ PFLT_CALLBACK_DATA Data,
61  _In_ PCFLT_RELATED_OBJECTS FltObjects,
62  _In_ PVOID CompletionContext,
63  _In_ FLT_POST_OPERATION_FLAGS Flags
64  );
65 
66 VOID
68  _Inout_ PFLT_CALLBACK_DATA Data,
69  _In_ PVOID CompletionContext
70  );
71 
72 NTSTATUS
74  _In_ PNC_INSTANCE_CONTEXT InstanceContext,
75  _In_ BOOLEAN IgnoreCase,
76  _In_ PUNICODE_STRING UserRequestName,
77  _In_ PUNICODE_STRING OpenedName,
78  _In_reads_bytes_(InputBufferLength) PFILE_NOTIFY_INFORMATION InputSystemBuffer,
79  _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PFILE_NOTIFY_INFORMATION OutputUserBuffer,
80  _In_ ULONG InputBufferLength,
81  _In_ ULONG OutputBufferLength,
82  _Out_ PULONG InputBufferConsumed,
83  _Out_ PULONG OutputBufferWritten,
84  _In_ BOOLEAN ReturnRealMappingPaths,
85  _In_ BOOLEAN ReturnInMappingOnly
86  );
87 
88 
89 
90 NTSTATUS
92  _In_ PFLT_CALLBACK_DATA PrimaryRequest,
93  _In_ PFLT_INSTANCE Instance,
94  _In_ PFILE_OBJECT FileObject,
95  _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext,
97  _Out_ PFLT_CALLBACK_DATA * SubRequest,
98  _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext
99  );
100 
101 VOID
103  _In_ PFLT_CALLBACK_DATA SubRequest
104  );
105 
106 NTSTATUS
108  _Inout_ PFLT_CALLBACK_DATA Data,
109  _Outptr_result_bytebuffer_maybenull_(*BufferSize) PVOID * DestBuffer,
110  _Out_ PULONG BufferSize
111  );
112 
113 _Pre_satisfies_(*LockHeld)
114 _Requires_lock_held_(_Global_critical_region_)
115 _Requires_lock_held_(*HandleContext->Lock)
116 _When_(*LockHeld == FALSE, _Releases_lock_(_Global_critical_region_))
117 _When_(*LockHeld == FALSE, _Releases_lock_(*HandleContext->Lock))
118 VOID
119 NcNotifyAbort(
120  _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext,
121  _In_ NTSTATUS Status,
122  _Inout_ PBOOLEAN LockHeld
123  );
124 
125 VOID
127  _In_ PFLT_CALLBACK_DATA Data
128  );
129 
130 VOID
132  _In_ PFLT_GENERIC_WORKITEM WorkItem,
133  _In_ PFLT_FILTER Filter,
134  _In_ PVOID HandlePtr
135  );
136 
137 VOID
139  _In_ PFLT_GENERIC_WORKITEM WorkItem,
140  _In_ PFLT_FILTER Filter,
141  _In_ PVOID RequestPtr
142  );
143 
144 #ifdef ALLOC_PRAGMA
145 #pragma alloc_text(PAGE, NcAllocateNotifyRequestContext)
146 #pragma alloc_text(PAGE, NcBuildSubNotifyRequest)
147 #pragma alloc_text(PAGE, NcCleanupSubNotifyRequest)
148 #pragma alloc_text(PAGE, NcCloseHandleWorkerRoutine)
149 #pragma alloc_text(PAGE, NcDirNotifyTranslateBuffers)
150 #pragma alloc_text(PAGE, NcFreeNotifyRequestContext)
151 #pragma alloc_text(PAGE, NcGetDestinationNotifyBuffer)
152 #pragma alloc_text(PAGE, NcNotifyAbort)
153 #pragma alloc_text(PAGE, NcNotifyCancelCallback)
154 #pragma alloc_text(PAGE, NcPreNotifyDirectory)
155 #pragma alloc_text(PAGE, NcPostNotifyDirectorySafe)
156 #pragma alloc_text(PAGE, NcPostNotifyDirectoryReal)
157 #pragma alloc_text(PAGE, NcReissueNotifyRequestWorkerRoutine)
158 #pragma alloc_text(PAGE, NcStreamHandleContextNotCleanup)
159 #pragma alloc_text(PAGE, NcStreamHandleContextNotCreate)
160 #pragma alloc_text(PAGE, NcStreamHandleContextNotClose)
161 #endif
162 
163 #define NcRemoveTrailingSlashIfPresent( US ) \
164  FLT_ASSERT( (US)->Length > 0 ); \
165  if ( (US)->Buffer[(US)->Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) { \
166  (US)->Length -= sizeof(WCHAR); \
167  }
168 
169 
170 NTSTATUS
172  _In_ PNC_INSTANCE_CONTEXT InstanceContext,
173  _In_ BOOLEAN IgnoreCase,
174  _In_ PUNICODE_STRING UserRequestName,
175  _In_ PUNICODE_STRING OpenedName,
176  _In_reads_bytes_(InputBufferLength) PFILE_NOTIFY_INFORMATION InputSystemBuffer,
177  _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PFILE_NOTIFY_INFORMATION OutputUserBuffer,
178  _In_ ULONG InputBufferLength,
179  _In_ ULONG OutputBufferLength,
180  _Out_ PULONG InputBufferConsumed,
181  _Out_ PULONG OutputBufferWritten,
182  _In_ BOOLEAN ReturnRealMappingPaths,
183  _In_ BOOLEAN ReturnInMappingOnly
184  )
185 /*++
186 
187 Routine Description:
188 
189  This routine is used to transform buffers from a filesystem view to the
190  user view. Depending on flags we either replace references to the real
191  mapping with the user mapping, suppress all entries from the real mapping,
192  or only return results from inside the real mapping.
193 
194 Arguments:
195 
196  InstanceContext - Pointer to the context describing this instance of the
197  filter.
198 
199  IgnoreCase - TRUE if comparisons should be case insensitive, FALSE if
200  comparisons should be case sensitive.
201 
202  UserRequestName - The string for the path that the user opened and is
203  requesting notifications on.
204 
205  OpenedName - The string for the path that we are processing notifications
206  on. "Typically" the same as UserRequestName, but may be different if
207  we are merging notifications from the user's handle and combining
208  from the real mapping. In this case, OpenedName may refer to the
209  path to the real mapping.
210 
211  InputSystemBuffer - The buffer we are processing from. Note that this
212  routine assumes the buffer is not volatile (cannot be externally
213  modified.) For this reason, the buffer is expected to be system
214  buffered by the caller if it is not already.
215 
216  Note however that the contents of the buffer may have originated from
217  a user buffer (via a memcpy), so although the contents are non-volatile,
218  they are not to be trusted.
219 
220  OutputUserBuffer - The buffer we are returning munged results into.
221  This buffer is expected to have been probed, and this function will
222  catch and return any invalid buffer exceptions.
223 
224  InputBufferLength - Size, in bytes, of the input buffer.
225 
226  OutputBufferLength - Size, in bytes, of the output buffer.
227 
228  InputBufferConsumed - Pointer to a ULONG which will contain, on output,
229  the number of bytes processed from the input buffer. This may be
230  zero, the length of the input buffer, or any value in between.
231  This value is undefined on failure.
232 
233  OutputBufferWritten - Pointer to a ULONG which will contain, on output,
234  the number of bytes written into the output buffer. This may be
235  zero, the length of the output buffer, or any value in between.
236  This value is undefined on failure.
237 
238  ReturnRealMappingPaths - TRUE if this function should transform and
239  return any paths that are within the real mapping. If FALSE, we
240  omit these entries.
241 
242  ReturnInMappingOnly - TRUE if this function should transform and return
243  paths from within the real mapping only, and omit all other paths.
244  FALSE if it should return all paths. Only meaningful if
245  ReturnRealMappingPaths is TRUE.
246 
247 Return Value:
248 
249  The return value is the Status of the operation.
250 
251 --*/
252 {
253  NTSTATUS Status = STATUS_SUCCESS;
254  NC_PATH_OVERLAP RealOverlap;
255 
256  //
257  // Pointers into the above buffers used as we read entries from
258  // one buffer and write them to the other. We keep a pointer to
259  // the previous destination entry to allow us to terminate the
260  // list when done.
261  //
262 
263  PFILE_NOTIFY_INFORMATION SourceEntry = NULL;
264  PFILE_NOTIFY_INFORMATION DestEntry = NULL;
265  PFILE_NOTIFY_INFORMATION PrevDestEntry = NULL;
266 
267  //
268  // A full path to the path returned from the filesystem that we are
269  // currently examining; the Remainder of that path if it turns out
270  // to be in a mapping; the transformed name if one is required.
271  //
272 
273  UNICODE_STRING NameString = EMPTY_UNICODE_STRING;
274  UNICODE_STRING Remainder;
275  UNICODE_STRING MungedName = EMPTY_UNICODE_STRING;
276 
277  //
278  // A pointer to one of the above buffers that we intend on returning to
279  // the application. Note that this may be NULL if an entry is being
280  // suppressed.
281  //
282 
283  PUNICODE_STRING ReturnName;
284  ULONG EntryLength;
285 
286  ULONG UlongResult;
287  PVOID PointerResult;
288 
289  PAGED_CODE();
290 
291  SourceEntry = InputSystemBuffer;
292  DestEntry = OutputUserBuffer;
293  *InputBufferConsumed = 0;
294  *OutputBufferWritten = 0;
295 
296  //
297  // This routine assumes it will only be called if there is work to do.
298  //
299 
300  FLT_ASSERT( InputBufferLength && OutputBufferLength );
301 
302  try {
303 
304  while (SourceEntry) {
305 
306  //
307  // The path returned is relative to the handle used to request
308  // the service. We now allocate and construct a full path name.
309  // Use safe math routines when consulting the buffer, since it is
310  // possible that the buffer contents were modified by malicious
311  // code before getting here.
312  //
313 
314  EntryLength = OpenedName->Length +
315  sizeof(WCHAR);
316 
317  if (EntryLength >= MAXUSHORT) {
318 
319  Status = STATUS_OBJECT_PATH_INVALID;
320  goto NcDirNotifyTranslateBuffersCleanup;
321  }
322 
323  Status = RtlULongAdd( EntryLength,
324  SourceEntry->FileNameLength,
325  &EntryLength );
326 
327  if (!NT_SUCCESS( Status ) ||
328  (EntryLength >= MAXUSHORT)) {
329 
330  Status = STATUS_OBJECT_PATH_INVALID;
331  goto NcDirNotifyTranslateBuffersCleanup;
332  }
333 
334  //
335  // Now check whether this entry walks off the end of the input buffer.
336  //
337 
338  Status = RtlULongAdd( *InputBufferConsumed,
339  FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName),
340  &UlongResult );
341 
342  if (NT_SUCCESS( Status )) {
343 
344  Status = RtlULongAdd( UlongResult,
345  SourceEntry->FileNameLength,
346  &UlongResult );
347  }
348 
349  if (!NT_SUCCESS( Status ) ||
350  (UlongResult > InputBufferLength)) {
351 
352  //
353  // We have an entry that walks off the end of the buffer.
354  // Since the buffer that we get from the filesystem is not
355  // system buffered, we cannot guarantee that a caller is
356  // not corrupting it in an in-flight request. This
357  // condition should never occur without aforementioned
358  // corruption.
359  //
360 
361  FLT_ASSERT( FALSE );
362  Status = STATUS_INVALID_USER_BUFFER;
363  goto NcDirNotifyTranslateBuffersCleanup;
364  }
365 
366  if (EntryLength > NameString.MaximumLength) {
367 
368  if (NameString.Buffer != NULL) {
369  NcFreeUnicodeString( &NameString );
370  }
371 
372  NameString.Buffer = ExAllocatePoolWithTag( PagedPool,
373  EntryLength,
374  NC_TAG );
375 
376  if (NameString.Buffer == NULL) {
377 
378  Status = STATUS_INSUFFICIENT_RESOURCES;
379  goto NcDirNotifyTranslateBuffersCleanup;
380  }
381 
382  NameString.MaximumLength = (USHORT)EntryLength;
383  }
384 
385  _Analysis_assume_(NameString.Buffer != NULL);
386  RtlCopyMemory( NameString.Buffer,
387  OpenedName->Buffer,
388  OpenedName->Length );
389 
390  NameString.Buffer[OpenedName->Length / sizeof(WCHAR)] = NC_SEPARATOR;
391 
392  RtlCopyMemory( Add2Ptr( NameString.Buffer, OpenedName->Length + sizeof(WCHAR) ),
393  SourceEntry->FileName,
394  SourceEntry->FileNameLength );
395 
396  NameString.Length = (USHORT)EntryLength;
397 
398  //
399  // Now see if this name needs to be munged.
400  //
401 
402  NcComparePath( &NameString,
403  &InstanceContext->Mapping.RealMapping,
404  &Remainder,
405  IgnoreCase,
406  TRUE,
407  &RealOverlap );
408 
409  if (RealOverlap.InMapping && ReturnRealMappingPaths) {
410 
411  //
412  // If the name is under the real mapping and we're returning
413  // paths under the real mapping, the query is a common
414  // ancestor of both real and user mappings. In this case,
415  // we need to translate real mapping paths to user mapping
416  // paths.
417  //
418 
419  Status = NcConstructPath( &InstanceContext->Mapping.UserMapping,
420  &Remainder,
421  TRUE,
422  &MungedName );
423 
424  if (!NT_SUCCESS( Status )) {
425  goto NcDirNotifyTranslateBuffersCleanup;
426  }
427 
428  ReturnName = &MungedName;
429 
430  } else if (ReturnInMappingOnly || RealOverlap.InMapping) {
431 
432  //
433  // If the name is in the real mapping but the caller is asking
434  // for a tree which is not an ancestor of the user mapping,
435  // then this name should not be visible under this tree. If
436  // the name is an ancestor of the real mapping, but the caller
437  // only requires children, this name should not be visible
438  // under this tree either.
439  //
440 
441  ReturnName = NULL;
442 
443  } else {
444 
445  ReturnName = &NameString;
446 
447  }
448 
449  if (ReturnName != NULL) {
450 
451  ULONG EntryLengthExact;
452 
453  //
454  // Since this API returns paths under the one the query was
455  // issued on, the path we have now had better be longer than
456  // that path length. Even if we refer to the same object,
457  // our path includes a trailing slash.
458  //
459 
460  FLT_ASSERT( ReturnName->Length > UserRequestName->Length );
461  _Analysis_assume_( ReturnName->Length > UserRequestName->Length );
462 
463  //
464  // We take care not to copy the leading slash. If we have
465  // a trailing slash as well as a leading slash, truncate the
466  // string. This can occur when we're injecting an entry for
467  // the mapping.
468  //
469 
470  if (ReturnName->Length - UserRequestName->Length > sizeof(WCHAR) &&
471  ReturnName->Buffer[ReturnName->Length/sizeof(WCHAR) - 1] == NC_SEPARATOR) {
472 
473  ReturnName->Length -= sizeof(WCHAR);
474  }
475 
476  EntryLengthExact = FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName );
477  EntryLengthExact += (ReturnName->Length - UserRequestName->Length - sizeof(WCHAR));
478 
479  EntryLength = AlignToSize( EntryLengthExact, 8);
480 
481  //
482  // We've done all we can. Return now to let our caller deal
483  // with the remaining buffer.
484  //
485 
486  Status = RtlULongAdd( EntryLength,
487  *OutputBufferWritten,
488  &UlongResult );
489 
490  if (!NT_SUCCESS( Status ) ||
491  (UlongResult > OutputBufferLength)) {
492 
493  if (PrevDestEntry != NULL) {
494  PrevDestEntry->NextEntryOffset = 0;
495  }
496 
497  SourceEntry = NULL;
498  DestEntry = NULL;
499  break;
500  }
501 
502  //
503  // Copy the relative path name, taking care to exclude the
504  // initial slash.
505  //
506 
507  DestEntry->FileNameLength = ReturnName->Length -
508  UserRequestName->Length -
509  sizeof(WCHAR);
510 
511  RtlCopyMemory( DestEntry->FileName,
512  Add2Ptr( ReturnName->Buffer, UserRequestName->Length + sizeof(WCHAR)),
513  ReturnName->Length - UserRequestName->Length - sizeof(WCHAR));
514  DestEntry->Action = SourceEntry->Action;
515  DestEntry->NextEntryOffset = EntryLength;
516 
517  //
518  // Advance the destination that we're writing new entries by
519  // however much we just consumed.
520  //
521 
522  PrevDestEntry = DestEntry;
523  *OutputBufferWritten += EntryLength;
524  DestEntry = Add2Ptr( DestEntry, EntryLength );
525 
526  if (MungedName.Buffer != NULL) {
527  ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG );
528  MungedName.Buffer = NULL;
529  MungedName.MaximumLength = MungedName.Length = 0;
530  }
531  }
532 
533  //
534  // Now calculate and advance the location we're reading and
535  // processing from. If we're have no more buffer left, we're
536  // done.
537  //
538 
539  //
540  // SourceEntry->NextEntryOffset is untrusted, since it may have been
541  // copied from a user-provided buffer.
542  //
543 
544  EntryLength = SourceEntry->NextEntryOffset;
545 
546  PointerResult = Add2Ptr( SourceEntry, EntryLength );
547 
548  Status = RtlULongAdd( *InputBufferConsumed,
549  EntryLength,
550  InputBufferConsumed );
551 
552  //
553  // There's a problem if one of the following happened:
554  //
555  // 1) We overflowed when accounting for for consumed input buffer
556  // 2) We wrapped when advancing SourceEntry
557  // 3) PointerResult is not within InputSystemBuffer
558  //
559 
560  if (!NT_SUCCESS( Status ) ||
561  (PointerResult < (PVOID)SourceEntry) ||
562  (PointerResult < Add2Ptr( InputSystemBuffer, sizeof(FILE_NOTIFY_INFORMATION) ))) {
563 
564  FLT_ASSERT( FALSE );
565 
566  Status = STATUS_INVALID_USER_BUFFER;
567  goto NcDirNotifyTranslateBuffersCleanup;
568  }
569 
570  SourceEntry = (PFILE_NOTIFY_INFORMATION)PointerResult;
571 
572  if ((EntryLength == 0)) {
573 
574  if (PrevDestEntry != NULL) {
575  PrevDestEntry->NextEntryOffset = 0;
576  }
577 
578  *InputBufferConsumed = InputBufferLength;
579  SourceEntry = NULL;
580  }
581 
582  //
583  // If we've just advanced our next location beyond the end of the
584  // input buffer, or there isn't enough room in it for even a FILE_NOTIFY_INFORMATION
585  // structure, terminate the loop by setting SourceEntry to NULL so
586  // we can at least return the valid entries we have.
587  //
588 
589  FLT_ASSERT( *InputBufferConsumed <= InputBufferLength );
590 
591  if ((SourceEntry != NULL) &&
592  ((*InputBufferConsumed >= InputBufferLength) ||
593  (*InputBufferConsumed + FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName ) > InputBufferLength))) {
594 
595  //
596  // Indicate to the caller that we consumed exactly the input buffer.
597  // Otherwise it may hold the possibly small remnant and come back
598  // in to this routine later with that remnant, causing us to overread
599  // the buffer.
600  //
601 
602  *InputBufferConsumed = InputBufferLength;
603  SourceEntry = NULL;
604  }
605  }
606 
607  } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) {
608 
609  Status = STATUS_INVALID_USER_BUFFER;
610  }
611 
612  //
613  // If we're succeeding, our list should be terminated.
614  //
615 
616  FLT_ASSERT( PrevDestEntry == NULL ||
617  !NT_SUCCESS( Status ) ||
618  PrevDestEntry->NextEntryOffset == 0);
619 
620 NcDirNotifyTranslateBuffersCleanup:
621 
622  if (MungedName.Buffer != NULL) {
623 
624  ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG );
625  MungedName.Buffer = NULL;
626  MungedName.MaximumLength = MungedName.Length = 0;
627 
628  }
629 
630  if (NameString.Buffer != NULL) {
631 
632  NcFreeUnicodeString( &NameString );
633  }
634 
635  return Status;
636 }
637 
638 NTSTATUS
640  _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext,
641  _In_ PFILE_OBJECT FileObject,
643  _In_ PFLT_CALLBACK_DATA Request,
644  _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext
645  )
646 /*++
647 
648 Routine Description:
649 
650  This function allocates a new request context with the specified
651  properties. It references objects passed into it.
652 
653 Arguments:
654 
655  HandleContext - Pointer to the handle context attached to the
656  user's handle.
657 
658  FileObject - The file that this request is being sent to. This
659  will be different to the user's file object for
660  mapping requests.
661 
662  RequestType - Specifies which class of subrequest this context
663  is for.
664 
665  Request - Pointer to Fltmgr's notion of this request.
666 
667  NotifyRequestContext - On output, points to the newly allocated
668  context. On failure, this value is
669  undefined.
670 
671 Return Value:
672 
673  Status of the operation.
674 
675 --*/
676 {
677  PNC_NOTIFY_REQUEST_CONTEXT RequestContext;
678 
679  PAGED_CODE();
680 
681  RequestContext = ExAllocatePoolWithTag( PagedPool,
682  sizeof(NC_NOTIFY_REQUEST_CONTEXT),
683  NC_TAG );
684 
685  if (RequestContext == NULL) {
686  return STATUS_INSUFFICIENT_RESOURCES;
687  }
688 
689  FltReferenceContext( HandleContext );
690  RequestContext->UserHandleContext = HandleContext;
691 
692  if (ARGUMENT_PRESENT( FileObject )) {
693 
694  ObReferenceObject( FileObject );
695  RequestContext->FileObjectToDereference = FileObject;
696  }
697 
698  RequestContext->RequestType = RequestType;
699  RequestContext->Request = Request;
700 
701  *NotifyRequestContext = RequestContext;
702 
703  return STATUS_SUCCESS;
704 }
705 
706 VOID
708  _In_ PNC_NOTIFY_REQUEST_CONTEXT NotifyRequestContext
709  )
710 /*++
711 
712 Routine Description:
713 
714  This function frees a request context previously allocated with
715  NcAllocateNotifyRequestContext. Since it dereferences objects
716  referenced at allocate time, it must be called without locks
717  held.
718 
719 Arguments:
720 
721  NotifyRequestContext - The request context to tear down.
722 
723 Return Value:
724 
725  None.
726 
727 --*/
728 {
729  PAGED_CODE();
730 
731  FLT_ASSERT( ExIsResourceAcquiredSharedLite( NotifyRequestContext->UserHandleContext->Lock ) == 0 );
732 
733  if (NotifyRequestContext->FileObjectToDereference) {
734  ObDereferenceObject( NotifyRequestContext->FileObjectToDereference );
735  }
736 
737  FltReleaseContext( NotifyRequestContext->UserHandleContext );
738 
739  ExFreePoolWithTag( NotifyRequestContext, NC_TAG );
740 
741 }
742 
743 NTSTATUS
745  _In_ PFLT_CALLBACK_DATA PrimaryRequest,
746  _In_ PFLT_INSTANCE Instance,
747  _In_ PFILE_OBJECT FileObject,
748  _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext,
750  _Out_ PFLT_CALLBACK_DATA * SubRequest,
751  _Out_ PNC_NOTIFY_REQUEST_CONTEXT * NotifyRequestContext
752  )
753 /*++
754 
755 Routine Description:
756 
757  This routine is used to create and initialize a new subrequest for
758  directory change notifications. We need to create a new subrequest
759  when requests are being shadowed or when merging from multiple locations.
760  For change notifications unrelated to the mapping or spanning both
761  mapping locations, we can send the user's request to the filesystem and
762  not create any subrequests.
763 
764 Arguments:
765 
766  PrimaryRequest - Pointer to the user's request. We use this to propagate
767  settings into the sub request.
768 
769  Instance - Pointer to the instance of this filter.
770 
771  FileObject - Pointer to the file object for the subrequest. This may be
772  the same as the user's file object, or may be a file object to the
773  parent of the real mapping.
774 
775  HandleContext - Pointer to the handle context attached to the
776  user's handle.
777 
778  RequestType - Specifies which class of subrequest this context
779  is for.
780 
781  SubRequest - Pointer to a location to contain the new request created by
782  this routine. This value is undefined on failure.
783 
784  NotifyRequestContext - On output, points to the newly allocated
785  context. On failure, this value is undefined.
786 
787 Return Value:
788 
789  The return value is the Status of the operation.
790 
791 --*/
792 {
793  PVOID MyBuffer;
794  ULONG BufferLength = PrimaryRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length;
795  NTSTATUS Status;
796  PFLT_CALLBACK_DATA NewRequest;
797 
798  PAGED_CODE();
799 
800  if (BufferLength > 0) {
801 
802  //
803  // Since this request isn't being sent to the filesystem and
804  // we'll complete it in arbitary context, we need to build a
805  // MDL now so we can copy to system address space rather than
806  // back to the originating usermode process.
807  //
808 
809  Status = FltLockUserBuffer( PrimaryRequest );
810 
811  if (!NT_SUCCESS( Status )) {
812 
813  return Status;
814  }
815 
816  //
817  // The #pragma is a notation to the static code analyzer to not worry
818  // that we're apparently leaking MyBuffer. It goes in to the subrequest
819  // we are building and will be cleaned up by NcCleanupSubNotifyRequest.
820  // That routine gets called on error in this routine, and from
821  // NcPostNotifyDirectoryReal, which is the completion routine that will
822  // be called once the subrequest we are building is finished executing.
823  //
824 
825 #pragma warning(suppress: 6014)
826  MyBuffer = ExAllocatePoolWithTag( PagedPool,
827  BufferLength,
828  NC_TAG );
829 
830  if (MyBuffer == NULL) {
831 
832  Status = STATUS_INSUFFICIENT_RESOURCES;
833  return Status;
834  }
835 
836  } else {
837 
838  MyBuffer = NULL;
839  }
840 
841  Status = FltAllocateCallbackData( Instance,
842  FileObject,
843  &NewRequest );
844 
845  if (!NT_SUCCESS( Status )) {
846 
847  if (MyBuffer != NULL) {
848  ExFreePoolWithTag( MyBuffer, NC_TAG );
849  }
850 
851  return Status;
852  }
853 
854  NewRequest->Iopb->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
855  NewRequest->Iopb->MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY;
856  NewRequest->Iopb->OperationFlags = PrimaryRequest->Iopb->OperationFlags;
857  NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length = BufferLength;
858  NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer = MyBuffer;
859  NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter =
860  PrimaryRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter;
861  NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare1 = 0;
862  NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare2 = 0;
863  NewRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress = NULL;
864 
865  if (BufferLength > 0) {
866 
867  NewRequest->Flags |= FLTFL_CALLBACK_DATA_SYSTEM_BUFFER;
868  }
869 
870  //
871  // Allocate the request context. We do this last because we
872  // cannot safely drop references if we fail after this point.
873  //
874 
875  Status = NcAllocateNotifyRequestContext( HandleContext,
876  FileObject,
877  RequestType,
878  NewRequest,
879  NotifyRequestContext );
880 
881  if (!NT_SUCCESS( Status )) {
882 
883  NcCleanupSubNotifyRequest( NewRequest );
884  return Status;
885  }
886 
887  *SubRequest = NewRequest;
888  return STATUS_SUCCESS;
889 }
890 
891 VOID
893  _In_ PFLT_CALLBACK_DATA SubRequest
894  )
895 /*++
896 
897 Routine Description:
898 
899  This routine is used to tear down a subrequest once we are done with it.
900 
901 Arguments:
902 
903  SubRequest - Pointer to the request to tear down.
904 
905 Return Value:
906 
907  None.
908 
909 --*/
910 {
911  PAGED_CODE();
912 
913  if (SubRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer != NULL) {
914 
915  ExFreePoolWithTag( SubRequest->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer,
916  NC_TAG );
917  }
918 
919  FltFreeCallbackData( SubRequest );
920 }
921 
922 NTSTATUS
924  _Inout_ PFLT_CALLBACK_DATA Data,
925  _Outptr_result_bytebuffer_maybenull_(*BufferSize) PVOID * DestBuffer,
926  _Out_ PULONG BufferSize
927  )
928 /*++
929 
930 Routine Description:
931 
932  This routine is used to obtain the virtual address that we should use
933  to access a particular request's buffer. Depending on the request,
934  this may be system buffered, system mapped but user exposed, or user
935  VA.
936 
937 Arguments:
938 
939  Data - The request whose VA we are trying to obtain/map.
940 
941  DestBuffer - Pointer to a pointer which will, on output, point to the
942  buffer associated with this request. On failure, this value is
943  undefined.
944 
945  BufferSize - Pointer to a location which will, on output, contain the
946  length of the user's buffer. On failure, this value is undefined.
947 
948 Return Value:
949 
950  The return value is the Status of the operation.
951 
952 --*/
953 {
954  NTSTATUS Status = STATUS_SUCCESS;
955  PAGED_CODE();
956 
957  *BufferSize = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length;
958 
959  *DestBuffer = NULL;
960 
961  //
962  // FltLockUserBuffer doesn't like being called with zero length buffers.
963  // If we have one of those, end now.
964  //
965 
966  if (Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length == 0) {
967 
968  Status = STATUS_SUCCESS;
969  goto NcGetDestinationNotifyBufferCleanup;
970  }
971 
972  //
973  // If we are in our post routine, we expect the buffer to have already
974  // been locked if FltGetNewSystemBufferAddress does not exist. If it
975  // does exist, we may have a MDL, or we may be obtaining the system
976  // buffer supplied by the filesystem.
977  //
978  // If we are issuing our own requests, we can also consume the user
979  // buffer directly, since it will always be system mapped (it's a pool
980  // allocation.)
981  //
982 
983  if (Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress) {
984 
985  Status = FltLockUserBuffer( Data );
986 
987  if (!NT_SUCCESS( Status )) {
988 
989  goto NcGetDestinationNotifyBufferCleanup;
990  }
991 
992  *DestBuffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress,
993  NormalPagePriority | MdlMappingNoExecute );
994  if (*DestBuffer == NULL) {
995 
996  //
997  // If this fails, we have the pages locked in RAM but don't
998  // have VA to map them to. STATUS_NO_MEMORY is usually used to
999  // indicate VA exhaustion.
1000  //
1001 
1002  Status = STATUS_NO_MEMORY;
1003  goto NcGetDestinationNotifyBufferCleanup;
1004  }
1005  } else {
1006 
1008  *DestBuffer = NcGetNewSystemBufferAddress( Data );
1009  } else {
1010  *DestBuffer = NULL;
1011  }
1012 
1013  //
1014  // If we have no MDL, and the filesystem hasn't allocated a system
1015  // buffer, there are two possibilities:
1016  //
1017  // 1. This is a request we created ourselves and was already system
1018  // buffered.
1019  //
1020  // 2. We are returning within the original thread context (ie.,
1021  // buffered results being returned to the caller instantly.)
1022  // In this case, we can use the user's VA.
1023  //
1024 
1025  if (*DestBuffer == NULL) {
1026 
1027  *DestBuffer = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer;
1028 
1030 
1031  //
1032  // On Win7 where we have FltGetNewSystemBufferAddress, we
1033  // can grab the system buffer from the Irp. Pre-Win7 we
1034  // must lock the user's buffer, thereby ending up in the
1035  // above code block. If we roll our own, the buffers we
1036  // use are in kernel space. The rule here is, no user VA
1037  // can be sent down without being locked first.
1038  //
1039 
1040  FLT_ASSERT( *DestBuffer > (PVOID)0x80000000 );
1041 
1042  } else {
1043 
1044  try {
1045 
1046  if (Data->RequestorMode != KernelMode) {
1047  ProbeForWrite( *DestBuffer, *BufferSize, sizeof( UCHAR ));
1048  }
1049 
1050  } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) {
1051 
1052  Status = STATUS_INVALID_USER_BUFFER;
1053  }
1054 
1055  }
1056  }
1057 
1058  }
1059 
1060 NcGetDestinationNotifyBufferCleanup:
1061 
1062  return Status;
1063 }
1064 
1065 
1066 FLT_PREOP_CALLBACK_STATUS
1068  _Inout_ PFLT_CALLBACK_DATA Data,
1069  _In_ PCFLT_RELATED_OBJECTS FltObjects,
1070  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
1071  )
1072 /*++
1073 
1074 Routine Description:
1075 
1076  Routine is invoked when the user wants to modify a directory for
1077  changes. The user can specify to monitor any directory, and can
1078  opt to monitor an entire subtree.
1079 
1080  We can approach this problem in one of four ways:
1081 
1082  1. If the directory being monitored has no relation to either
1083  mapping, we can send it straight to the filesystem.
1084 
1085  2. If the directory being monitored is an ancestor of the real
1086  mapping but not the user mapping, we must suppress entries
1087  from the mapping.
1088 
1089  3. If the directory being monitored is an ancestor of both
1090  mappings, we can change the contents of the buffer from real
1091  to user to reflect the change.
1092 
1093  4. If the directory being monitored is an ancestor of the user
1094  mapping but not the real mapping, we must send down two
1095  requests, one for the mapping parent and one for the user's
1096  handle, and combine the results back to the user.
1097 
1098 Arguments:
1099 
1100  Data - Pointer to the filter CallbackData that is passed to us.
1101 
1102  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure
1103  containing opaque handles to this filter, instance, its
1104  associated volume and file object.
1105 
1106  CompletionContext - The context for the completion routine for
1107  this operation. We set this to the handle context for
1108  directory change notifications.
1109 
1110 Return Value:
1111 
1112  The return value is the Status of the operation.
1113 
1114 --*/
1115 {
1116  FLT_PREOP_CALLBACK_STATUS ReturnValue;
1117  NTSTATUS Status;
1118 
1119  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
1120  PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL;
1121  PNC_DIR_NOT_CONTEXT NotCtx = NULL;
1122 
1123  PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL;
1124 
1125  NC_PATH_OVERLAP RealOverlap;
1126  NC_PATH_OVERLAP UserOverlap;
1127 
1128  BOOLEAN WatchTree = BooleanFlagOn( Data->Iopb->OperationFlags, SL_WATCH_TREE );
1129  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
1130  FO_OPENED_CASE_SENSITIVE );
1131  BOOLEAN UnlockContext = FALSE;
1132  ULONG SizeWeReturn = 0;
1133  PNC_NOTIFY_REQUEST_CONTEXT ShadowRequestContext = NULL;
1134  PNC_NOTIFY_REQUEST_CONTEXT MappingParentRequestContext = NULL;
1135  PFLT_CALLBACK_DATA NewShadowRequest = NULL;
1136  PFLT_CALLBACK_DATA NewMappingParentRequest = NULL;
1137 
1138  PAGED_CODE();
1139 
1140  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
1141 
1142  //
1143  // If the FileObject has been through cleanup, fail now. This check
1144  // is to catch where a file was cleaned up, then a notification was
1145  // called on it for the first time. We don't want to start
1146  // initializing our structures now if we don't have to.
1147  //
1148 
1149  if (FlagOn( FltObjects->FileObject->Flags, FO_CLEANUP_COMPLETE )) {
1150 
1151  ReturnValue = FLT_PREOP_COMPLETE;
1152  Status = STATUS_NOTIFY_CLEANUP;
1153  SizeWeReturn = 0;
1154 
1155  goto NcPreNotifyDirectoryCleanup;
1156  }
1157 
1158  //
1159  // Get our instance context.
1160  //
1161 
1162  Status = FltGetInstanceContext( FltObjects->Instance,
1163  &InstanceContext );
1164 
1165  if (!NT_SUCCESS( Status )) {
1166 
1167  ReturnValue = FLT_PREOP_COMPLETE;
1168  goto NcPreNotifyDirectoryCleanup;
1169  }
1170 
1171  //
1172  // Get the directory's name.
1173  //
1174 
1175  Status = NcGetFileNameInformation( Data,
1176  NULL,
1177  NULL,
1178  FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT,
1179  &FileNameInformation );
1180 
1181  if (!NT_SUCCESS( Status )) {
1182 
1183  ReturnValue = FLT_PREOP_COMPLETE;
1184  goto NcPreNotifyDirectoryCleanup;
1185  }
1186 
1187  Status = FltParseFileNameInformation( FileNameInformation );
1188 
1189  if (!NT_SUCCESS( Status )) {
1190 
1191  ReturnValue = FLT_PREOP_COMPLETE;
1192  goto NcPreNotifyDirectoryCleanup;
1193  }
1194 
1195  //
1196  // See if the directory is parent of either mapping.
1197  //
1198 
1199  NcComparePath( &FileNameInformation->Name,
1200  &InstanceContext->Mapping.UserMapping,
1201  NULL,
1202  IgnoreCase,
1203  TRUE,
1204  &UserOverlap );
1205 
1206  NcComparePath( &FileNameInformation->Name,
1207  &InstanceContext->Mapping.RealMapping,
1208  NULL,
1209  IgnoreCase,
1210  TRUE,
1211  &RealOverlap );
1212 
1213  //
1214  // If we are watching within the mapping, we have no
1215  // work to do. Strings returned are relative to the
1216  // handle used to query for this operation.
1217  //
1218 
1219  if (UserOverlap.InMapping || RealOverlap.InMapping) {
1220 
1221  Status = STATUS_SUCCESS;
1222  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
1223  goto NcPreNotifyDirectoryCleanup;
1224  }
1225 
1226  //
1227  // If this object has no relation to either mapping,
1228  // we have nothing to do.
1229  //
1230 
1231  if (!UserOverlap.Ancestor && !RealOverlap.Ancestor) {
1232 
1233  Status = STATUS_SUCCESS;
1234  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
1235  goto NcPreNotifyDirectoryCleanup;
1236  }
1237 
1238  //
1239  // If we are not monitoring recursively, and this is a
1240  // non-parent ancestor, we have no work to do.
1241  //
1242 
1243  if (!WatchTree &&
1244  (!UserOverlap.Parent && !RealOverlap.Parent)) {
1245 
1246  Status = STATUS_SUCCESS;
1247  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
1248  goto NcPreNotifyDirectoryCleanup;
1249  }
1250 
1251  //
1252  // Attach our handle context. We use this to record
1253  // state about which object we're monitoring, how we're
1254  // monitoring it, and our outstanding subrequests.
1255  //
1256 
1257  Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter,
1258  FltObjects->Instance,
1259  FltObjects->FileObject,
1260  &HandleContext );
1261 
1262  if (!NT_SUCCESS( Status )) {
1263 
1264  ReturnValue = FLT_PREOP_COMPLETE;
1265  goto NcPreNotifyDirectoryCleanup;
1266  }
1267 
1268  FLT_ASSERT( HandleContext != NULL );
1269 
1270  NotCtx = &HandleContext->DirectoryNotificationContext;
1271 
1272  //
1273  // Before looking at the context, we have to acquire the lock.
1274  //
1275 
1276  NcLockStreamHandleContext( HandleContext );
1277  UnlockContext = TRUE;
1278 
1279  //
1280  // If the user's handle has gone through cleanup, don't allow this
1281  // request. We're not synchronized with the FO_CLEANUP_COMPLETE
1282  // flag, but we are synchronized with the CleanupSeen flag, so we
1283  // need to do this check now after acquiring the lock.
1284  //
1285 
1286  if (NotCtx->CleanupSeen ||
1287  FlagOn( FltObjects->FileObject->Flags, FO_CLEANUP_COMPLETE )) {
1288 
1289  ReturnValue = FLT_PREOP_COMPLETE;
1290  Status = STATUS_NOTIFY_CLEANUP;
1291  SizeWeReturn = 0;
1292 
1293  goto NcPreNotifyDirectoryCleanup;
1294  }
1295 
1296  //
1297  // Save away the path used on this handle, if we haven't already.
1298  // This is done to avoid having to re-request it in post.
1299  //
1300  // Note that NameChanger will not allow renames of ancestor
1301  // components, which are also the only paths it intercepts
1302  // directory change notifications on. Accordingly, the path we
1303  // capture here should not change for the lifetime of the
1304  // notification.
1305  //
1306 
1307  if (NotCtx->UserRequestName.Buffer == NULL) {
1308 
1309  NotCtx->UserRequestName.Buffer = ExAllocatePoolWithTag( PagedPool,
1310  FileNameInformation->Name.Length,
1311  NC_TAG );
1312 
1313  if (NotCtx->UserRequestName.Buffer == NULL) {
1314 
1315  ReturnValue = FLT_PREOP_COMPLETE;
1316  Status = STATUS_INSUFFICIENT_RESOURCES;
1317  goto NcPreNotifyDirectoryCleanup;
1318 
1319  }
1320 
1321  RtlCopyMemory( NotCtx->UserRequestName.Buffer,
1322  FileNameInformation->Name.Buffer,
1323  FileNameInformation->Name.Length );
1324 
1325  NotCtx->UserRequestName.MaximumLength =
1326  NotCtx->UserRequestName.Length =
1327  FileNameInformation->Name.Length;
1328 
1330 
1331  NotCtx->IgnoreCase = IgnoreCase;
1332 
1333  FltReferenceContext( InstanceContext );
1334  NotCtx->InstanceContext = InstanceContext;
1335  NotCtx->CompletionFilter = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter;
1336  NotCtx->OperationFlags = Data->Iopb->OperationFlags;
1337 
1338  }
1339 
1340  //
1341  // If the user had previously cancelled a request on this handle,
1342  // clear that now. We want to process all results from this
1343  // point.
1344  //
1345 
1346  NotCtx->CancelSeen = FALSE;
1347 
1348  //
1349  // TODO: The notify package can record a number of Irps for the
1350  // same directory (with the same settings for all.) To preserve
1351  // this semantic, we need to save off whether we're watching the
1352  // entire tree or not in our handle context, then keep a list
1353  // of outstanding requests. These will be completed by the
1354  // filesystem on a first in, first out basis. Or perhaps, we
1355  // maintain the list and only send the filesystem a "representative"
1356  // query, but maintain our own list and re-send to the filesystem
1357  // if further user requests exist.
1358  //
1359  // Bah.
1360  //
1361 
1362  // Check if this ever occurs in practice
1363  FLT_ASSERT( NotCtx->UserRequest == NULL );
1364  if (NotCtx->UserRequest != NULL) {
1365 
1366  Status = STATUS_UNSUCCESSFUL;
1367  ReturnValue = FLT_PREOP_COMPLETE;
1368  goto NcPreNotifyDirectoryCleanup;
1369  }
1370 
1371  //
1372  // If we previously saw the filesystem indicate that the buffer was
1373  // not large enough to return all results, but we did not have a user
1374  // request to complete to that effect, let the user know now.
1375  //
1376 
1377  if (NotCtx->InsufficientBufferSeen) {
1378 
1379  NotCtx->InsufficientBufferSeen = FALSE;
1380  Status = STATUS_NOTIFY_ENUM_DIR;
1381  ReturnValue = FLT_PREOP_COMPLETE;
1382  goto NcPreNotifyDirectoryCleanup;
1383  }
1384 
1385  //
1386  // If we still have leftover changes from a previous call,
1387  // return them now. Note that we do not want to set this
1388  // request as being the user's request in this case.
1389  //
1390 
1391  if (NotCtx->BufferToFree != NULL) {
1392 
1393  PVOID Buffer;
1394  ULONG BufferSize;
1395 
1396  FLT_ASSERT( NotCtx->BufferLength != 0 );
1397 
1398  Status = NcGetDestinationNotifyBuffer( Data, &Buffer, &BufferSize );
1399 
1400  //
1401  // We were unable to lock/map the user's buffer, or the buffer
1402  // was invalid.
1403  //
1404 
1405  if (!NT_SUCCESS( Status )) {
1406 
1407  ReturnValue = FLT_PREOP_COMPLETE;
1408  goto NcPreNotifyDirectoryCleanup;
1409  }
1410 
1411  //
1412  // If the user's buffer isn't big enough, tell them to rescan.
1413  // Since we're telling them to rescan, tear down the buffer.
1414  //
1415 
1416  if (BufferSize < NotCtx->BufferLength) {
1417 
1418  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
1419  NotCtx->BufferToFree = NULL;
1420  NotCtx->BufferLength = 0;
1421 
1422  ReturnValue = FLT_PREOP_COMPLETE;
1423  Status = STATUS_NOTIFY_ENUM_DIR;
1424  goto NcPreNotifyDirectoryCleanup;
1425 
1426  } else {
1427 
1428  try {
1429 
1430  RtlCopyMemory( Buffer,
1431  NotCtx->BufferToFree,
1432  NotCtx->BufferLength );
1433 
1434  } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) {
1435 
1436  Status = STATUS_INVALID_USER_BUFFER;
1437  goto NcPreNotifyDirectoryCleanup;
1438  }
1439 
1440  SizeWeReturn = NotCtx->BufferLength;
1441 
1442  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
1443  NotCtx->BufferToFree = NULL;
1444  NotCtx->BufferLength = 0;
1445 
1446  ReturnValue = FLT_PREOP_COMPLETE;
1447  Status = STATUS_SUCCESS;
1448 
1449  goto NcPreNotifyDirectoryCleanup;
1450  }
1451  }
1452 
1453  NotCtx->UserRequest = Data;
1454 
1455  //
1456  // If we are watching the parent of the real mapping,
1457  // or an ancestor of it including subtree, but not
1458  // an ancestor of the user mapping, we'll need to "filter"
1459  // out notifications that a caller should not see.
1460  //
1461 
1462  if ((WatchTree && RealOverlap.Ancestor && !UserOverlap.Ancestor) ||
1463  (!WatchTree && RealOverlap.Parent && !UserOverlap.Parent)) {
1464 
1465  NotCtx->Mode = Filter;
1466 
1467  //
1468  // TODO: Currently the lifetime of requests in filter mode is
1469  // lock-step. If we get here when the Irp is already sent to
1470  // the filesystem, that implies that we have multiple async
1471  // notify irps sent to us, and we either support having
1472  // multiple notify irps sent to the filesystem, or we queue
1473  // them here.
1474  //
1475 
1476  FLT_ASSERT( NotCtx->ShadowRequest == NULL );
1477  if (NotCtx->ShadowRequest == NULL) {
1478 
1479  Status = NcBuildSubNotifyRequest( Data,
1480  FltObjects->Instance,
1481  FltObjects->FileObject,
1482  HandleContext,
1484  &NewShadowRequest,
1485  &ShadowRequestContext );
1486 
1487  if (!NT_SUCCESS( Status )) {
1488 
1489  ReturnValue = FLT_PREOP_COMPLETE;
1490  goto NcPreNotifyDirectoryCleanup;
1491  }
1492 
1493  }
1494 
1495  //
1496  // To synchronize this, we set the request in the context under
1497  // cover of the context lock. We then drop the lock to issue the
1498  // IO. The rule here is that IO should only be sent by its
1499  // creator or its completor.
1500  //
1501 
1502  if (NewShadowRequest) {
1503 
1504  FLT_ASSERT( ShadowRequestContext );
1505  FLT_ASSERT( UnlockContext );
1506 
1507  //
1508  // Set up the subrequest. This is done before we set up a
1509  // cancel callback to ensure that the cancel can do
1510  // something meaningful if it's invoked.
1511  //
1512 
1513  NotCtx->ShadowRequest = NewShadowRequest;
1514 
1515  //
1516  // Set a cancel routine on the user's request (which we intend
1517  // on hanging on to.) If the request is already cancelled,
1518  // cancel our new request.
1519  //
1520 
1522 
1523  if (!NT_SUCCESS( Status )) {
1524 
1525  NotCtx->ShadowRequest = NULL;
1526 
1527  ReturnValue = FLT_PREOP_COMPLETE;
1528  goto NcPreNotifyDirectoryCleanup;
1529  }
1530 
1531  FLT_ASSERT( HandleContext != NULL );
1532  NcUnlockStreamHandleContext( HandleContext );
1533  UnlockContext = FALSE;
1534 
1535  //
1536  // As soon as we send this, Data/NotCtx->UserRequest is
1537  // no longer guaranteed to be valid. Fltmgr will call
1538  // our completion routine even on failure. Regardless
1539  // of what happens here, we do not need to worry about
1540  // cleaning things up.
1541  //
1542 
1543  Status = FltPerformAsynchronousIo( NotCtx->ShadowRequest,
1545  ShadowRequestContext );
1546 
1547  }
1548 
1549  //
1550  // Don't free the request or its context. These belong to the
1551  // request now.
1552  //
1553 
1554  ShadowRequestContext = NULL;
1555  NewShadowRequest = NULL;
1556 
1557  //
1558  // We return pending here in all cases. Either the filesystem
1559  // pended the request (and we should too), or the filesystem
1560  // completed it inline, in which case our completion routine has
1561  // already completed our 'pending' request, so we'd better pend it.
1562  //
1563  // If our request failed, Fltmgr will call our completion routine
1564  // which will also propagate the failure to the user request and
1565  // complete it.
1566  //
1567 
1568  ReturnValue = FLT_PREOP_PENDING;
1569 
1570  } else
1571 
1572  //
1573  // If we are watching the tree of a mutual ancestor,
1574  // or just the directory of a mutal parent, or within
1575  // the mapping itself, we'll need to transform names.
1576  //
1577 
1578  if ((WatchTree && RealOverlap.Ancestor && UserOverlap.Ancestor) ||
1579  (RealOverlap.Parent && UserOverlap.Parent)) {
1580 
1581  //
1582  // Filesystems may switch a directory change notify request
1583  // to system buffered. On Win7, we can obtain the resulting
1584  // system buffer from FltGetNewSystemBufferAddress. Prior
1585  // to this we can't obtain it, so we must lock the user
1586  // buffer now so the filesystem can obtain kernel VA and will
1587  // not attempt to system buffer the request.
1588  //
1589 
1591 
1592  Status = FltLockUserBuffer( Data );
1593  if (!NT_SUCCESS( Status )) {
1594 
1595  ReturnValue = FLT_PREOP_COMPLETE;
1596  goto NcPreNotifyDirectoryCleanup;
1597  }
1598 
1599  if (MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress,
1600  NormalPagePriority | MdlMappingNoExecute ) == NULL) {
1601 
1602  Status = STATUS_NO_MEMORY;
1603  ReturnValue = FLT_PREOP_COMPLETE;
1604  goto NcPreNotifyDirectoryCleanup;
1605  }
1606  }
1607 
1608  //
1609  // Allocate a context that describes this subrequest.
1610  //
1611 
1612  Status = NcAllocateNotifyRequestContext( HandleContext,
1613  FltObjects->FileObject,
1615  Data,
1616  &ShadowRequestContext );
1617 
1618  if (!NT_SUCCESS( Status )) {
1619 
1620  ReturnValue = FLT_PREOP_COMPLETE;
1621  goto NcPreNotifyDirectoryCleanup;
1622  }
1623 
1624  *CompletionContext = ShadowRequestContext;
1625  ShadowRequestContext = NULL;
1626 
1627  //
1628  // We have to do something with this call, so tell
1629  // fltmgr to call us back.
1630  //
1631 
1632  ReturnValue = FLT_PREOP_SUCCESS_WITH_CALLBACK;
1633 
1634  NotCtx->Mode = Munge;
1635 
1636  } else
1637 
1638  //
1639  // If we are watching the tree of an ancestor of the
1640  // user mapping or just the directory of the parent
1641  // of the user mapping, which is not an ancestor of
1642  // the real mapping, we need to merge two different
1643  // notifications into one.
1644  //
1645 
1646  if ((WatchTree && UserOverlap.Ancestor && !RealOverlap.Ancestor) ||
1647  (!WatchTree && UserOverlap.Parent && !RealOverlap.Parent)) {
1648 
1649  //
1650  // If we already have a handle to the mapping parent, use it.
1651  // If we don't have one, drop our lock and proceed to open it.
1652  //
1653 
1654  if (NotCtx->RealParentHandle == NULL) {
1655 
1656  OBJECT_ATTRIBUTES MappingParentAttributes;
1657  HANDLE MappingParentHandle = NULL;
1658  PFILE_OBJECT MappingParentFileObject = NULL;
1659  PWSTR MappingParentName;
1660  IO_STATUS_BLOCK MappingParentStatusBlock;
1661  PFLT_FILE_NAME_INFORMATION FileInfoInternalHandle = NULL;
1662  PFLT_GENERIC_WORKITEM MappingParentWorkItem = NULL;
1663 
1664  //
1665  // Since we haven't opened a handle yet, we assume no requests
1666  // can be outstanding which could race with us. Even if the
1667  // directory being used has been renamed so we're going into
1668  // this path after being through one of the other paths, we
1669  // should be in lock-step until we enter Merge mode, so no
1670  // requests should exist.
1671  //
1672 
1673  FLT_ASSERT( NotCtx->ShadowRequest == NULL &&
1674  NotCtx->MappingRequest == NULL );
1675 
1676  FLT_ASSERT( UnlockContext );
1677  NcUnlockStreamHandleContext( HandleContext );
1678  UnlockContext = FALSE;
1679 
1680  //
1681  // Open the parent of the mapping. We'll need to return results
1682  // from both the user's handle and our own. Note that we open
1683  // the parent specifically so that a caller can monitor changes
1684  // to the mapping itself.
1685  //
1686 
1687  InitializeObjectAttributes( &MappingParentAttributes,
1688  &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath,
1689  OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0),
1690  NULL,
1691  NULL);
1692 
1693  Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter
1694  FltObjects->Instance, // InstanceOffsets
1695  &MappingParentHandle, // Returned Handle
1696  &MappingParentFileObject, // Returned FileObject
1697  FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access
1698  &MappingParentAttributes, // object attributes
1699  &MappingParentStatusBlock, // Returned IOStatusBlock
1700  0, // Allocation Size
1701  FILE_ATTRIBUTE_NORMAL, // File Attributes
1702  0, // Share Access
1703  FILE_OPEN, // Create Disposition
1704  FILE_DIRECTORY_FILE, // Create Options
1705  NULL, // Ea Buffer
1706  0, // EA Length
1707  IO_IGNORE_SHARE_ACCESS_CHECK, // Flags
1708  FltObjects->FileObject ); // Transaction info
1709 
1710  if (!NT_SUCCESS( Status )) {
1711 
1712  //
1713  // This filter enforces the existence of mapping parents
1714  // at attach time, and will prevent them being deleted or
1715  // renamed.
1716  //
1717 
1718  FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND &&
1719  Status != STATUS_OBJECT_NAME_NOT_FOUND );
1720 
1721  ReturnValue = FLT_PREOP_COMPLETE;
1722  goto NcPreNotifyDirectoryCleanup;
1723 
1724  }
1725 
1726  Status = NcGetFileNameInformation( NULL,
1727  MappingParentFileObject,
1728  FltObjects->Instance,
1729  FLT_FILE_NAME_OPENED |
1730  FLT_FILE_NAME_QUERY_DEFAULT |
1731  FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
1732  &FileInfoInternalHandle );
1733  if (!NT_SUCCESS( Status )) {
1734 
1735  FltClose( MappingParentHandle );
1736  ObDereferenceObject( MappingParentFileObject );
1737 
1738  ReturnValue = FLT_PREOP_COMPLETE;
1739  goto NcPreNotifyDirectoryCleanup;
1740  }
1741 
1742  Status = FltParseFileNameInformation( FileInfoInternalHandle );
1743  if (!NT_SUCCESS( Status )) {
1744 
1745  FltClose( MappingParentHandle );
1746  ObDereferenceObject( MappingParentFileObject );
1747  FltReleaseFileNameInformation( FileInfoInternalHandle );
1748 
1749  ReturnValue = FLT_PREOP_COMPLETE;
1750  goto NcPreNotifyDirectoryCleanup;
1751  }
1752 
1753  MappingParentName = ExAllocatePoolWithTag( PagedPool,
1754  FileInfoInternalHandle->Name.Length,
1755  NC_TAG );
1756 
1757  if (MappingParentName == NULL) {
1758 
1759  Status = STATUS_INSUFFICIENT_RESOURCES;
1760 
1761  FltClose( MappingParentHandle );
1762  ObDereferenceObject( MappingParentFileObject );
1763  FltReleaseFileNameInformation( FileInfoInternalHandle );
1764 
1765  ReturnValue = FLT_PREOP_COMPLETE;
1766  goto NcPreNotifyDirectoryCleanup;
1767  }
1768 
1769  MappingParentWorkItem = FltAllocateGenericWorkItem();
1770  if (MappingParentWorkItem == NULL) {
1771 
1772  Status = STATUS_INSUFFICIENT_RESOURCES;
1773 
1774  FltClose( MappingParentHandle );
1775  ObDereferenceObject( MappingParentFileObject );
1776  FltReleaseFileNameInformation( FileInfoInternalHandle );
1777  ExFreePoolWithTag( MappingParentName, NC_TAG );
1778 
1779  ReturnValue = FLT_PREOP_COMPLETE;
1780  goto NcPreNotifyDirectoryCleanup;
1781  }
1782 
1783 
1784  NcLockStreamHandleContext( HandleContext );
1785  UnlockContext = TRUE;
1786 
1787  //
1788  // We could be racing with another thread attempting to open the
1789  // mapping. If, after having reacquired the lock we still see
1790  // no mapping handle, use ours. If another thread beat us to it,
1791  // tear down ours and use theirs.
1792  //
1793 
1794  if (NotCtx->RealParentHandle == NULL &&
1795  !NotCtx->CleanupSeen) {
1796 
1797  //
1798  // Store our object into the context.
1799  //
1800 
1801  FLT_ASSERT( NotCtx->RealParentFileObject == NULL &&
1802  NotCtx->RealParentCloseWorkItem == NULL &&
1803  NotCtx->MappingParentName.Buffer == NULL );
1804 
1805  NotCtx->RealParentHandle = MappingParentHandle;
1806  NotCtx->RealParentFileObject = MappingParentFileObject;
1807  NotCtx->RealParentCloseWorkItem = MappingParentWorkItem;
1808 
1809  NotCtx->MappingParentName.Buffer = MappingParentName;
1810  NotCtx->MappingParentName.MaximumLength = FileInfoInternalHandle->Name.Length;
1811 
1812  RtlCopyMemory( NotCtx->MappingParentName.Buffer,
1813  FileInfoInternalHandle->Name.Buffer,
1814  FileInfoInternalHandle->Name.Length );
1815 
1816  NotCtx->MappingParentName.Length = FileInfoInternalHandle->Name.Length;
1817 
1819 
1820  FltReleaseFileNameInformation( FileInfoInternalHandle );
1821 
1822  } else {
1823 
1824  //
1825  // Another thread beat us to the punch, or our services are
1826  // no longer required. Tear down our state and reload from
1827  // theirs.
1828  //
1829  // Really, we can't race here (currently) because:
1830  //
1831  // 1. We do not support multiple outstanding requests on one
1832  // handle (if attempted, we already failed.)
1833  //
1834  // 2. This must be the first request on this handle, or else
1835  // our mapping parent handle already existed.
1836  //
1837  // 3. We have not yet set up cancellation on this request.
1838  //
1839  // The only way to be here is if a cleanup request has
1840  // occurred and we're aborting.
1841  //
1842 
1843  FLT_ASSERT( NotCtx->CleanupSeen );
1844 
1845  NcUnlockStreamHandleContext( HandleContext );
1846  UnlockContext = FALSE;
1847 
1848  FltClose( MappingParentHandle );
1849  ObDereferenceObject( MappingParentFileObject );
1850  ExFreePoolWithTag( MappingParentName, NC_TAG );
1851  FltReleaseFileNameInformation( FileInfoInternalHandle );
1852  FltFreeGenericWorkItem( MappingParentWorkItem );
1853 
1854  NcLockStreamHandleContext( HandleContext );
1855  UnlockContext = TRUE;
1856 
1857  //
1858  // Another thread beat us to creating the file object, but
1859  // it was subsequently torn down while we dropped our lock.
1860  // This can happen if the user handle is closed, in which
1861  // case we expect CleanupSeen to be set and we can safely
1862  // leave. Return pending because the cleanup completed our
1863  // request.
1864  //
1865 
1866  if (NotCtx->RealParentHandle == NULL) {
1867 
1868  FLT_ASSERT( NotCtx->CleanupSeen );
1869 
1870  ReturnValue = FLT_PREOP_PENDING;
1871  goto NcPreNotifyDirectoryCleanup;
1872  }
1873  }
1874 
1875  MappingParentHandle = NULL;
1876  MappingParentFileObject = NULL;
1877  MappingParentName = NULL;
1878 
1879  //
1880  // This relationship may have changed while we dropped the
1881  // lock, but this can't happen in practice today:
1882  //
1883  // 1. We do not support multiple outstanding requests on one
1884  // handle (if attempted, we already failed.)
1885  //
1886  // 2. This must be the first request on this handle, or else
1887  // our mapping parent handle already existed.
1888  //
1889  // 3. We have not yet set up cancellation on this request.
1890  //
1891  // 4. If the user handle were closed, we failed out above.
1892  //
1893  // If these change and we need to revalidate state, we may
1894  // need to clear cancel state, return insufficient buffer,
1895  // or return buffered data here.
1896  //
1897 
1898  FLT_ASSERT( NotCtx->UserRequest == Data );
1899 
1900  FLT_ASSERT( NotCtx->ShadowRequest == NULL &&
1901  NotCtx->MappingRequest == NULL &&
1902  !NotCtx->CleanupSeen &&
1903  !NotCtx->CancelSeen &&
1904  !NotCtx->InsufficientBufferSeen &&
1905  NotCtx->BufferToFree == NULL &&
1906  NotCtx->BufferLength == 0 );
1907 
1908  }
1909 
1910  FLT_ASSERT( UnlockContext );
1911  FLT_ASSERT( NotCtx->RealParentHandle != NULL &&
1912  NotCtx->RealParentFileObject != NULL &&
1913  NotCtx->RealParentCloseWorkItem != NULL );
1914 
1915  NotCtx->Mode = Merge;
1916 
1917  //
1918  // Now create our child requests. We need to watch for changes on
1919  // both the user's handle and on the mapping parent handle. While
1920  // this process is ongoing, we're trying to drop/reacquire the lock
1921  // as few times as possible, so we set up both requests at once,
1922  // then issue them together.
1923  //
1924  // If this is the second (or subsequent) time notifications are
1925  // being requested, we may already have subrequests issued to the
1926  // filesystem. In that case, we leave the existing requests in
1927  // place. Any request not already in place we create ourselves
1928  // now.
1929  //
1930 
1931  if (NotCtx->ShadowRequest == NULL) {
1932 
1933  Status = NcBuildSubNotifyRequest( Data,
1934  FltObjects->Instance,
1935  FltObjects->FileObject,
1936  HandleContext,
1938  &NewShadowRequest,
1939  &ShadowRequestContext );
1940 
1941  if (!NT_SUCCESS( Status )) {
1942 
1943  ReturnValue = FLT_PREOP_COMPLETE;
1944  goto NcPreNotifyDirectoryCleanup;
1945  }
1946 
1947  }
1948 
1949  if (NotCtx->MappingRequest == NULL) {
1950 
1951  Status = NcBuildSubNotifyRequest( Data,
1952  FltObjects->Instance,
1953  NotCtx->RealParentFileObject,
1954  HandleContext,
1956  &NewMappingParentRequest,
1957  &MappingParentRequestContext );
1958 
1959  if (!NT_SUCCESS( Status )) {
1960 
1961  ReturnValue = FLT_PREOP_COMPLETE;
1962  goto NcPreNotifyDirectoryCleanup;
1963  }
1964  }
1965 
1966  //
1967  // To synchronize this, we set the request in the context under cover
1968  // of the context lock. We then drop the lock to issue the IO.
1969  // The rule here is that IO should only be sent by its creator or its
1970  // completor.
1971  //
1972 
1973  if (NewShadowRequest || NewMappingParentRequest) {
1974 
1975  FLT_ASSERT( UnlockContext );
1976 
1977  if (NewShadowRequest) {
1978  NotCtx->ShadowRequest = NewShadowRequest;
1979  }
1980 
1981  if (NewMappingParentRequest) {
1982  NotCtx->MappingRequest = NewMappingParentRequest;
1983  }
1984 
1985  //
1986  // Set a cancel routine on the user's request (which we intend on
1987  // hanging on to.)
1988  //
1989 
1991 
1992  if (!NT_SUCCESS( Status )) {
1993 
1994  ReturnValue = FLT_PREOP_COMPLETE;
1995  goto NcPreNotifyDirectoryCleanup;
1996  }
1997 
1998  FLT_ASSERT( HandleContext != NULL );
1999  NcUnlockStreamHandleContext( HandleContext );
2000  UnlockContext = FALSE;
2001 
2002  //
2003  // Note that as soon as we drop the lock, the user's request is
2004  // no longer guaranteed to be valid. Any previously outstanding
2005  // IO may complete and take it away from us.
2006  //
2007 
2008  if (NewShadowRequest) {
2009 
2010  Status = FltPerformAsynchronousIo( NotCtx->ShadowRequest,
2012  ShadowRequestContext );
2013 
2014  ShadowRequestContext = NULL;
2015  NewShadowRequest = NULL;
2016 
2017 
2018  //
2019  // If we failed and have a second request, grab the lock
2020  // again to enable our cleanup code to tear it down.
2021  //
2022 
2023  if (!NT_SUCCESS( Status ) && NewMappingParentRequest) {
2024 
2025  NcLockStreamHandleContext( HandleContext );
2026  UnlockContext = TRUE;
2027 
2028  ReturnValue = FLT_PREOP_PENDING;
2029  goto NcPreNotifyDirectoryCleanup;
2030 
2031  }
2032  }
2033 
2034  if (NewMappingParentRequest) {
2035 
2036  Status = FltPerformAsynchronousIo( NotCtx->MappingRequest,
2038  MappingParentRequestContext );
2039 
2040  MappingParentRequestContext = NULL;
2041  NewMappingParentRequest = NULL;
2042  }
2043  }
2044 
2045  ReturnValue = FLT_PREOP_PENDING;
2046 
2047  }
2048 
2049  FLT_ASSERT( NotCtx->Mode != Uninitialized );
2050 
2051  FLT_ASSERT( NotCtx->InstanceContext != NULL );
2052 
2053 
2054 NcPreNotifyDirectoryCleanup:
2055 
2056  if (ReturnValue == FLT_PREOP_COMPLETE) {
2057 
2058  //
2059  // We need to write back results of query.
2060  //
2061 
2062  Data->IoStatus.Status = Status;
2063 
2064  if (NT_SUCCESS( Status )) {
2065 
2066  //success
2067  Data->IoStatus.Information = SizeWeReturn;
2068 
2069  } else {
2070 
2071  //failure
2072  Data->IoStatus.Information = 0;
2073 
2074  }
2075 
2076  //
2077  // Since we're completing the request, remove it from the notify
2078  // context structure. Note that since we're completing it, it
2079  // must still be valid and owned by us, so we're absolutely
2080  // entitled (and required!) to remove it.
2081  //
2082 
2083  if (HandleContext != NULL) {
2084 
2085  if (!UnlockContext) {
2086  NcLockStreamHandleContext( HandleContext );
2087  UnlockContext = TRUE;
2088  }
2089 
2090  if (NotCtx->UserRequest == Data) {
2091  NotCtx->UserRequest = NULL;
2092  }
2093  }
2094  }
2095 
2096  if (NewShadowRequest) {
2097 
2098  //
2099  // We should only be cleaning this up if we hold the lock.
2100  //
2101 
2102  FLT_ASSERT( UnlockContext );
2103 
2104  //
2105  // Either this request is still set in the context (and we'll
2106  // take care of that), or it's been cancelled and hence
2107  // removed already. In either case, we still need to tear it
2108  // down.
2109  //
2110 
2111  FLT_ASSERT( NotCtx->ShadowRequest == NULL ||
2112  NotCtx->ShadowRequest == NewShadowRequest );
2113 
2114  NcCleanupSubNotifyRequest( NewShadowRequest );
2115  NotCtx->ShadowRequest = NewShadowRequest = NULL;
2116  }
2117 
2118  if (NewMappingParentRequest) {
2119 
2120  //
2121  // We should only be cleaning this up if we hold the lock.
2122  //
2123 
2124  FLT_ASSERT( UnlockContext );
2125 
2126  //
2127  // Either this request is still set in the context (and we'll
2128  // take care of that), or it's been cancelled and hence
2129  // removed already. In either case, we still need to tear it
2130  // down.
2131  //
2132 
2133  FLT_ASSERT( NotCtx->MappingRequest == NULL ||
2134  NotCtx->MappingRequest == NewMappingParentRequest );
2135 
2136  NcCleanupSubNotifyRequest( NewMappingParentRequest );
2137  NotCtx->MappingRequest = NewMappingParentRequest = NULL;
2138  }
2139 
2140  if (InstanceContext != NULL) {
2141 
2142  FltReleaseContext( InstanceContext );
2143  }
2144 
2145  if (UnlockContext) {
2146 
2147  FLT_ASSERT( HandleContext != NULL );
2148  NcUnlockStreamHandleContext( HandleContext );
2149  }
2150 
2151  //
2152  // Now that we've dropped the lock and can issue IO, tear down any
2153  // request contexts from failed requests.
2154  //
2155 
2156  if (ShadowRequestContext) {
2157  NcFreeNotifyRequestContext( ShadowRequestContext );
2158  }
2159 
2160  if (MappingParentRequestContext) {
2161  NcFreeNotifyRequestContext( MappingParentRequestContext );
2162  }
2163 
2164  if (HandleContext != NULL) {
2165 
2166  FltReleaseContext( HandleContext );
2167  }
2168 
2169  if (FileNameInformation != NULL) {
2170 
2171  FltReleaseFileNameInformation( FileNameInformation );
2172  }
2173 
2174  return ReturnValue;
2175 }
2176 
2177 
2178 VOID
2180  _Inout_ PFLT_CALLBACK_DATA Data,
2181  _In_ PVOID CompletionContext
2182  )
2183 /*++
2184 
2185 Routine Description:
2186 
2187  Routine is invoked after the filesystem has returned a change
2188  notification, or if we are processing a cancellation for an
2189  outstanding change notification.
2190 
2191  This routine is guaranteed to be called <= APC level. It is
2192  still not really safe to issue IO from here, since that may
2193  require us to be at passive level. We also have no guarantees
2194  that TopLevelIrp == NULL at this point (and in many cases, we
2195  wouldn't expect it to be.) Accordingly, this function should
2196  not call back into the filesystem directly itself.
2197 
2198 Arguments:
2199 
2200  Data - Pointer to the request. This could be the user's request,
2201  or it could be a subrequest we created for ourselves.
2202 
2203  CompletionContext - The context for the completion routine for
2204  this operation. We set this to the handle context for
2205  directory change notifications.
2206 
2207 Return Value:
2208 
2209  None. If we are completing a request, any errors will be
2210  associated with that request.
2211 
2212 --*/
2213 {
2214  NTSTATUS Status;
2215 
2216  PFILE_NOTIFY_INFORMATION SourceBuffer = NULL;
2217  PFILE_NOTIFY_INFORMATION DestBuffer;
2218 
2219  PNC_INSTANCE_CONTEXT InstanceContext;
2220  PNC_NOTIFY_REQUEST_CONTEXT RequestContext = (PNC_NOTIFY_REQUEST_CONTEXT)CompletionContext;
2221  PNC_STREAM_HANDLE_CONTEXT HandleContext = RequestContext->UserHandleContext;
2222  PNC_DIR_NOT_CONTEXT NotCtx = NULL;
2223 
2224  ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information;
2225  ULONG BufferSize;
2226  ULONG InputConsumed;
2227  ULONG SizeWeReturn;
2228 
2229  BOOLEAN UnlockContext = FALSE;
2230  BOOLEAN CompleteUserRequest = TRUE;
2231  BOOLEAN ReissuedRequest = FALSE;
2232 
2233  PAGED_CODE();
2234 
2235  NotCtx = &HandleContext->DirectoryNotificationContext;
2236 
2237  //
2238  // Before looking at the context, we have to acquire the lock.
2239  //
2240 
2241  NcLockStreamHandleContext( HandleContext );
2242  UnlockContext = TRUE;
2243 
2244  //
2245  // Perform debug-only sanity checking and set up our state so we
2246  // can quickly tell which kind of request we're dealing with.
2247  //
2248 
2249  FLT_ASSERT( NotCtx->Mode == Filter || NotCtx->Mode == Munge || NotCtx->Mode == Merge );
2250  FLT_ASSERT( RequestContext->Request == Data );
2251 
2252  //
2253  // If the user's handle has gone through cleanup, and we're completing
2254  // it, make sure it fails correctly. Make sure we don't propagate any
2255  // success or meaningful information beyond this point.
2256  //
2257 
2258  if (NotCtx->CleanupSeen) {
2259  if (NotCtx->UserRequest == NULL) {
2260  CompleteUserRequest = FALSE;
2261  } else {
2262  Status = STATUS_NOTIFY_CLEANUP;
2263  SizeWeReturn = 0;
2264  }
2265  goto NcPostNotifyDirectoryRealCleanup;
2266  }
2267 
2268  //
2269  // If the user has cancelled their request, make sure any completing
2270  // subrequests are failed with STATUS_CANCELLED. This is primarily to
2271  // ensure that we don't reissue requests post-cancel (which will never
2272  // be cancelled.)
2273  //
2274 
2275  if (NotCtx->CancelSeen) {
2276 
2277  Data->IoStatus.Status = STATUS_CANCELLED;
2278  }
2279 
2280  //
2281  // Flow any failures back to the user's request, if we have one.
2282  //
2283 
2284  if (!NT_SUCCESS( Data->IoStatus.Status ) ||
2285  Data->IoStatus.Status == STATUS_NOTIFY_CLEANUP ||
2286  Data->IoStatus.Status == STATUS_NOTIFY_ENUM_DIR ) {
2287 
2288  Status = Data->IoStatus.Status;
2289  SizeWeReturn = 0;
2290 
2291  if (NotCtx->UserRequest == NULL) {
2292 
2293  CompleteUserRequest = FALSE;
2294 
2295  if (Data->IoStatus.Status == STATUS_NOTIFY_ENUM_DIR) {
2296 
2297  //
2298  // If the filesystem is trying to tell the app to rescan,
2299  // but we don't have the app's request right now, make a
2300  // note to tell the app that it must rescan as soon as it
2301  // asks us again.
2302  //
2303 
2304  NotCtx->InsufficientBufferSeen = TRUE;
2305  }
2306  }
2307  goto NcPostNotifyDirectoryRealCleanup;
2308  }
2309 
2310  FLT_ASSERT( NotCtx->Mode != Merge ||
2311  (NotCtx->RealParentFileObject != NULL &&
2312  NotCtx->UserRequestName.Buffer != NULL ));
2313 
2314  FLT_ASSERT( Data == NotCtx->UserRequest ||
2315  Data == NotCtx->ShadowRequest ||
2316  Data == NotCtx->MappingRequest );
2317 
2318  InstanceContext = NotCtx->InstanceContext;
2319 
2320  Status = NcGetDestinationNotifyBuffer( Data, &DestBuffer, &BufferSize );
2321  if (!NT_SUCCESS( Status )) {
2322 
2323  //
2324  // This can fail due no pages/VA to lock the user's buffer, or a
2325  // 'user buffer' that points to kernel space. By the time we get
2326  // here, we expect that our pages are either already locked or we
2327  // do not need to lock them; and we expect that requests we
2328  // generate internally are based on pool and should not require VA
2329  // to map, nor be usermode pointers into kernel space. Really, the
2330  // only time this should fail is if we have no VA to map the user's
2331  // request.
2332  //
2333  // If this assumption is wrong, we need to be smarter about whether
2334  // we want to complete the user's request in this path or not.
2335  //
2336 
2337  FLT_ASSERT( Data == NotCtx->UserRequest );
2338  goto NcPostNotifyDirectoryRealCleanup;
2339  }
2340 
2341  FLT_ASSERT( SizeActuallyReturned <= BufferSize );
2342 
2343  //
2344  // If we have a zero-length buffer, the filesystem should not have
2345  // returned STATUS_SUCCESS. We still try to tolerate this
2346  // condition regardless.
2347  //
2348 
2349  FLT_ASSERT( BufferSize > 0 );
2350 
2351  //
2352  // If the filesystem returned something, go ahead and try to munge it.
2353  //
2354 
2355  if (SizeActuallyReturned > (ULONG)FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName )) {
2356 
2357  //
2358  // Allocate a new buffer and copy the contents. Note that this is
2359  // particularly important with this call, since it's not always
2360  // system buffered; the contents are free to change underneath us.
2361  // This allocation protects us against that, but we still must be
2362  // paranoid touching the buffer, since we cannot trust that it has
2363  // any integrity at this point.
2364  //
2365 
2366  SourceBuffer = ExAllocatePoolWithTag( PagedPool,
2367  SizeActuallyReturned,
2368  NC_TAG );
2369 
2370  if (SourceBuffer == NULL) {
2371 
2372  Status = STATUS_INSUFFICIENT_RESOURCES;
2373  goto NcPostNotifyDirectoryRealCleanup;
2374  }
2375 
2376  try {
2377 
2378  //
2379  // The #pragma is a notation to the static code analyzer that the
2380  // buffer sizes involved have been computed correctly so no buffer
2381  // overrun is possible here.
2382  //
2383 #pragma warning(suppress:6385)
2384  RtlCopyMemory( SourceBuffer, DestBuffer, SizeActuallyReturned );
2385 
2386  } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) {
2387 
2388  Status = STATUS_INVALID_USER_BUFFER;
2389  goto NcPostNotifyDirectoryRealCleanup;
2390  }
2391 
2392  //
2393  // Now that we have a copy of the filesystem data, we need to
2394  // transform it and return it to the user. So now we point our
2395  // destination buffers at the original request if we own it, if it
2396  // wasn't already.
2397  //
2398  // When merging from the mapping and non-mapping paths, we may have
2399  // completed the user's request from one path and then receive
2400  // notification on the second. In this case, we buffer the
2401  // notification so the caller can receieve it again next call. We
2402  // do not expect to need more than one buffer. If this overflows,
2403  // just tell the caller their buffer was too small.
2404  //
2405 
2406  if (RequestContext->RequestType == NotifyShadowRequest ||
2407  RequestContext->RequestType == NotifyMappingRequest) {
2408 
2409  if (NotCtx->UserRequest != NULL) {
2410 
2411  Status = NcGetDestinationNotifyBuffer( NotCtx->UserRequest,
2412  &DestBuffer,
2413  &BufferSize );
2414 
2415  if (!NT_SUCCESS( Status )) {
2416  goto NcPostNotifyDirectoryRealCleanup;
2417  }
2418 
2419  } else {
2420 
2421  FLT_ASSERT( NotCtx->Mode == Merge );
2422 
2423  //
2424  // We have at most two outstanding child requests, and if
2425  // one completes we'll complete the master request,
2426  // buffering with the second. We therefore only need one
2427  // buffer - most of the time.
2428  //
2429  // There is a case where one buffer is insufficient: if the
2430  // user issues a cancel while both child requests are
2431  // completing, we may have a cancelled user request and two
2432  // incoming pieces of data. In this case, it's safe to
2433  // throw one away since the act of cancelling implies the
2434  // user doesn't expect to see everything. However in that
2435  // case we should have failed out above, and should never
2436  // get here.
2437  //
2438 
2439  FLT_ASSERT( NotCtx->BufferToFree == NULL );
2440  if (NotCtx->BufferToFree != NULL) {
2441 
2442  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
2443  NotCtx->BufferToFree = NULL;
2444  NotCtx->BufferLength = 0;
2445  }
2446 
2447  //
2448  // There is no user request, don't complete it.
2449  //
2450 
2451  CompleteUserRequest = FALSE;
2452 
2453  if (BufferSize > 0) {
2454 
2455  //
2456  // Save away the output to our buffer. Note that we
2457  // process the data now because we won't know which call
2458  // generated the data later.
2459  //
2460 
2461  NotCtx->BufferToFree = ExAllocatePoolWithTag( PagedPool,
2462  BufferSize,
2463  NC_TAG );
2464 
2465  if (NotCtx->BufferToFree == NULL) {
2466 
2467  Status = STATUS_INSUFFICIENT_RESOURCES;
2468  goto NcPostNotifyDirectoryRealCleanup;
2469  }
2470 
2471  }
2472 
2473  DestBuffer = NotCtx->BufferToFree;
2474  FLT_ASSERT( NotCtx->BufferLength == 0 );
2475  }
2476  }
2477 
2478  //
2479  // If we have a zero length buffer, return to the user that something
2480  // changed and do not attempt to return anything more.
2481  //
2482 
2483  if (BufferSize > 0) {
2484 
2485  Status = NcDirNotifyTranslateBuffers( InstanceContext,
2486  NotCtx->IgnoreCase,
2487  &NotCtx->UserRequestName,
2488  (RequestContext->RequestType == NotifyMappingRequest)?
2489  &NotCtx->MappingParentName:
2490  &NotCtx->UserRequestName,
2491  SourceBuffer,
2492  DestBuffer,
2493  SizeActuallyReturned,
2494  BufferSize,
2495  &InputConsumed,
2496  &SizeWeReturn,
2497  (BOOLEAN)(NotCtx->Mode == Munge || NotCtx->Mode == Merge),
2498  (BOOLEAN)(RequestContext->RequestType == NotifyMappingRequest) );
2499 
2500  } else {
2501 
2502  FLT_ASSERT( NT_SUCCESS( Status ));
2503  }
2504 
2505 
2506  if (!NT_SUCCESS( Status )) {
2507 
2508  //
2509  // If we failed, don't leave the garbage buffer to be picked
2510  // up again. Tear it down now.
2511  //
2512 
2513  if (DestBuffer == NotCtx->BufferToFree) {
2514  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
2515  NotCtx->BufferToFree = NULL;
2516  NotCtx->BufferLength = 0;
2517  }
2518  goto NcPostNotifyDirectoryRealCleanup;
2519  }
2520 
2521  //
2522  // We couldn't fit the translated information into the caller's
2523  // buffer. For directory change notifications, there is a special
2524  // status to use in this situation, which tells the caller to
2525  // rescan from scratch. It's a heavy hammer, but might teach the
2526  // caller to use a bigger buffer.
2527  //
2528  // Unfortunately this is lost in translation between NT & Win32,
2529  // so most applications end up guessing.
2530  //
2531 
2532  if ((BufferSize == 0) ||
2533  (InputConsumed < SizeActuallyReturned)) {
2534 
2535  if (!CompleteUserRequest) {
2536 
2537  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
2538  NotCtx->BufferToFree = NULL;
2539  NotCtx->BufferLength = 0;
2540  NotCtx->InsufficientBufferSeen = TRUE;
2541  }
2542 
2543  SizeWeReturn = 0;
2544  Status = STATUS_NOTIFY_ENUM_DIR;
2545  goto NcPostNotifyDirectoryRealCleanup;
2546 
2547  } else {
2548 
2549  Status = STATUS_SUCCESS;
2550 
2551  if (DestBuffer == NotCtx->BufferToFree) {
2552  NotCtx->BufferLength = SizeWeReturn;
2553  }
2554  }
2555 
2556  //
2557  // Our worst nightmare has come true. The filesystem completed the
2558  // request but we filtered out all the contents. We certainly
2559  // don't want to tell the caller nothing whatsoever happened, so we
2560  // try to reissue this request.
2561  //
2562 
2563  if (SizeWeReturn == 0) {
2564 
2565  PMDL OldMdl = Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress;
2566  PFLT_GENERIC_WORKITEM WorkItem;
2567 
2568  FLT_ASSERT( (NotCtx->Mode == Filter || NotCtx->Mode == Merge ) &&
2569  (RequestContext->RequestType == NotifyShadowRequest || RequestContext->RequestType == NotifyMappingRequest ) );
2570 
2571  if (DestBuffer == NotCtx->BufferToFree) {
2572 
2573  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
2574  NotCtx->BufferToFree = NULL;
2575  NotCtx->BufferLength = 0;
2576  }
2577 
2578  Status = NcGetDestinationNotifyBuffer( Data,
2579  &DestBuffer,
2580  &BufferSize );
2581  if (!NT_SUCCESS( Status )) {
2582  goto NcPostNotifyDirectoryRealCleanup;
2583  }
2584 
2585  WorkItem = FltAllocateGenericWorkItem();
2586  if (WorkItem == NULL) {
2587  Status = STATUS_INSUFFICIENT_RESOURCES;
2588  goto NcPostNotifyDirectoryRealCleanup;
2589  }
2590 
2591  FLT_ASSERT( RequestContext->Request == Data );
2592 
2593  //
2594  // Clear and reinitialize the request using the Mdl and buffer
2595  // that it was previously using. Note that we know it will be
2596  // system buffered if it has a buffer, because the only
2597  // requests that can get here are requests we created.
2598  //
2599 
2600  FltReuseCallbackData( Data );
2601 
2602  Data->Iopb->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
2603  Data->Iopb->MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY;
2604  Data->Iopb->OperationFlags = NotCtx->OperationFlags;
2605  Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Length =
2606  BufferSize;
2607  Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.DirectoryBuffer =
2608  DestBuffer;
2609  Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.CompletionFilter =
2610  NotCtx->CompletionFilter;
2611  Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare1 = 0;
2612  Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.Spare2 = 0;
2613  Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress =
2614  OldMdl;
2615 
2616  if (BufferSize > 0) {
2617 
2618  Data->Flags |= FLTFL_CALLBACK_DATA_SYSTEM_BUFFER;
2619  }
2620 
2621  //
2622  // Post issuing new IO to passive level, where we are sure TopLevelIrp
2623  // is actually NULL.
2624  //
2625 
2626  Status = FltQueueGenericWorkItem( WorkItem,
2629  DelayedWorkQueue,
2630  RequestContext );
2631 
2632  if (NT_SUCCESS( Status )) {
2633  ReissuedRequest = TRUE;
2634  CompleteUserRequest = FALSE;
2635  } else {
2636  goto NcPostNotifyDirectoryRealCleanup;
2637  }
2638 
2639  }
2640 
2641  //
2642  // If we successfully parsed and returned data either to the user's
2643  // call or saved it to our buffer, we will not reissue the
2644  // associated call. If the user calls us again, we'll repeat any
2645  // completed call(s). Doing this allows the filter to delegate
2646  // buffering of operations to the filesystem, except for the above
2647  // case (both sides of a merge complete in parallel before the user
2648  // calls again.)
2649  //
2650 
2651 
2652  } else {
2653 
2654  //
2655  // If the filesystem returned something less than one single
2656  // entry, return nothing.
2657  //
2658 
2659  SizeWeReturn = 0;
2660  if (NotCtx->UserRequest == NULL) {
2661  CompleteUserRequest = FALSE;
2662  }
2663  }
2664 
2665 NcPostNotifyDirectoryRealCleanup:
2666 
2667  FLT_ASSERT( CompleteUserRequest || RequestContext->RequestType != NotifyUserRequest );
2668 
2669  //
2670  // We've just processed something from one of the child Irps.
2671  // Tear it down, NULL it out. We'll rebuild it if we're asked
2672  // for another change notification.
2673  //
2674 
2675  if (!ReissuedRequest) {
2676 
2677  if (RequestContext->RequestType == NotifyShadowRequest ||
2678  RequestContext->RequestType == NotifyMappingRequest ) {
2679 
2680  if (Data == NotCtx->ShadowRequest) {
2681  NotCtx->ShadowRequest = NULL;
2682  }
2683 
2684  if (Data == NotCtx->MappingRequest) {
2685  NotCtx->MappingRequest = NULL;
2686  }
2687 
2688  //
2689  // This routine will free the FLT_CALLBACK_DATA.
2690  //
2691 
2692  NcCleanupSubNotifyRequest( Data );
2693  }
2694  }
2695 
2696  //
2697  // We need to write back results of query.
2698  //
2699 
2700  if (CompleteUserRequest) {
2701 
2702  FLT_ASSERT( UnlockContext );
2703 
2704  FLT_ASSERT( NotCtx->UserRequest != NULL );
2705  FLT_ASSERT( RequestContext->RequestType != NotifyUserRequest ||
2706  Data == NotCtx->UserRequest );
2707 
2708  NotCtx->UserRequest->IoStatus.Status = Status;
2709 
2710  if (NT_SUCCESS( Status )) {
2711 
2712  //success
2713  NotCtx->UserRequest->IoStatus.Information = SizeWeReturn;
2714 
2715  } else {
2716 
2717  //failure
2718  NotCtx->UserRequest->IoStatus.Information = 0;
2719  }
2720 
2721  if (RequestContext->RequestType != NotifyUserRequest) {
2722 
2723  PFLT_CALLBACK_DATA RequestToComplete = NotCtx->UserRequest;
2724  NTSTATUS CancelStatus;
2725 
2726  //
2727  // Clear the cancel routine. If cancel has already been
2728  // invoked, don't complete now (leave the cancel routine to
2729  // complete the request, and give the user nothing.) If
2730  // cancel has not yet been invoked, after we clear the
2731  // routine it cannot be invoked, so we are free to complete
2732  // the request ourselves unconditionally.
2733  //
2734 
2735  CancelStatus = FltClearCancelCompletion( RequestToComplete );
2736 
2737  if (CancelStatus != STATUS_CANCELLED) {
2738 
2739  NotCtx->UserRequest = NULL;
2740 
2741  NcUnlockStreamHandleContext( HandleContext );
2742  UnlockContext = FALSE;
2743 
2744  FltCompletePendedPreOperation( RequestToComplete,
2745  FLT_PREOP_COMPLETE,
2746  NULL );
2747  }
2748 
2749  } else {
2750 
2751  NotCtx->UserRequest = NULL;
2752  }
2753  }
2754 
2755  if (SourceBuffer != NULL) {
2756 
2757  ExFreePoolWithTag( SourceBuffer, NC_TAG );
2758  }
2759 
2760  if (UnlockContext) {
2761 
2762  FLT_ASSERT( HandleContext != NULL );
2763  NcUnlockStreamHandleContext( HandleContext );
2764  }
2765 
2766  //
2767  // Now that we've dropped the lock, drop our references.
2768  //
2769 
2770  if (!ReissuedRequest) {
2771  NcFreeNotifyRequestContext( RequestContext );
2772  }
2773 
2774 }
2775 
2776 FLT_POSTOP_CALLBACK_STATUS
2778  _Inout_ PFLT_CALLBACK_DATA Data,
2779  _In_ PCFLT_RELATED_OBJECTS FltObjects,
2780  _In_ PVOID CompletionContext,
2781  _In_ FLT_POST_OPERATION_FLAGS Flags
2782  )
2783 /*++
2784 
2785 Routine Description:
2786 
2787  Routine is invoked when a directory change notification request
2788  completes. The request must be the user's request. This
2789  function is called when <= APC_LEVEL.
2790 
2791 Arguments:
2792 
2793  Data - Pointer to the filter CallbackData that is passed to us.
2794 
2795  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
2796  opaque handles to this filter, instance, its associated volume and
2797  file object.
2798 
2799  CompletionContext - The context for the completion routine for this
2800  operation. For directory change notifications, this is a pointer
2801  to our handle context.
2802 
2803  Flags - Flags for this operation.
2804 
2805 Return Value:
2806 
2807  The return value is the Status of the operation.
2808 
2809 --*/
2810 {
2811 
2812  PAGED_CODE();
2813 
2814  NcPostNotifyDirectoryReal( Data, CompletionContext );
2815 
2816  UNREFERENCED_PARAMETER( Flags );
2817  UNREFERENCED_PARAMETER( FltObjects );
2818 
2819  return FLT_POSTOP_FINISHED_PROCESSING;
2820 }
2821 
2822 
2823 FLT_POSTOP_CALLBACK_STATUS
2825  _Inout_ PFLT_CALLBACK_DATA Data,
2826  _In_ PCFLT_RELATED_OBJECTS FltObjects,
2827  _In_ PVOID CompletionContext,
2828  _In_ FLT_POST_OPERATION_FLAGS Flags
2829  )
2830 /*++
2831 
2832 Routine Description:
2833 
2834  Routine is invoked when a directory change notification request
2835  completes. The request must be the user's request. This routine
2836  may be called at DISPATCH_LEVEL and must be nonpaged.
2837 
2838 Arguments:
2839 
2840  Data - Pointer to the filter CallbackData that is passed to us.
2841 
2842  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
2843  opaque handles to this filter, instance, its associated volume and
2844  file object.
2845 
2846  CompletionContext - The context for the completion routine for this
2847  operation. For directory change notifications, this is a pointer
2848  to our handle context.
2849 
2850  Flags - Flags for this operation.
2851 
2852 Return Value:
2853 
2854  The return value is the Status of the operation.
2855 
2856 --*/
2857 {
2858 
2859  FLT_POSTOP_CALLBACK_STATUS Status;
2860  BOOLEAN Success;
2861 
2862  if (FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING )) {
2863 
2864  //
2865  // We can only be here on a user initiated operation that we sent
2866  // to the filesystem.
2867  //
2868  // TODO: Really what we want to do here is return
2869  // STATUS_NOTIFY_ENUM_DIR to the caller, but we no longer own the
2870  // request. Should we bite the bullet and shadow all munge requests
2871  // so that this can work?
2872  //
2873 
2874  NcFreeNotifyRequestContext( CompletionContext );
2875 
2876  Status = FLT_POSTOP_FINISHED_PROCESSING;
2877 
2878  } else {
2879 
2880  //
2881  // For simplicity, and to avoid needing everything to be nonpaged,
2882  // we perform completion at APC_LEVEL or below.
2883  //
2884 
2885  Success = FltDoCompletionProcessingWhenSafe( Data,
2886  FltObjects,
2887  CompletionContext,
2888  Flags,
2890  &Status );
2891 
2892  if (!Success) {
2893 
2894  //
2895  // It would be nice to know _why_ this fails, but since we
2896  // don't, neither will anybody else.
2897  //
2898 
2899  Data->IoStatus.Status = STATUS_UNSUCCESSFUL;
2900  Data->IoStatus.Information = 0;
2901  Status = FLT_POSTOP_FINISHED_PROCESSING;
2902  }
2903  }
2904 
2905  return Status;
2906 }
2907 
2908 VOID
2910  _In_ PFLT_GENERIC_WORKITEM WorkItem,
2911  _In_ PFLT_FILTER Filter,
2912  _In_ PVOID RequestPtr
2913  )
2914 /*++
2915 
2916 Routine Description:
2917 
2918  This function reissues an subrequest that is part of an
2919  oustanding directory change notification request. If it is
2920  unable to reissue the request, this function is responsible
2921  for all cleanup of the request.
2922 
2923 Arguments:
2924 
2925  WorkItem - Pointer to the workitem which triggered the call to this
2926  function.
2927 
2928  Filter - Pointer to our filter object.
2929 
2930  RequestPtr - Our context, specifically the request we wish to reissue.
2931 
2932 Return Value:
2933 
2934  None.
2935 
2936 --*/
2937 {
2938  PNC_NOTIFY_REQUEST_CONTEXT RequestContext = (PNC_NOTIFY_REQUEST_CONTEXT)RequestPtr;
2939 
2940  PAGED_CODE();
2941 
2942  UNREFERENCED_PARAMETER( Filter );
2943 
2944  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
2945 
2946  FLT_ASSERT( RequestContext->RequestType == NotifyShadowRequest ||
2947  RequestContext->RequestType == NotifyMappingRequest );
2948 
2949  //
2950  // Since we're being called asynchronously, it's possible
2951  // that this request has already been cancelled, or that
2952  // cleanup has already occurred. In the event of a cancel,
2953  // this request should be marked as canceled and the
2954  // filesystem will complete it immediately. In the event
2955  // of cleanup, we are sending the request to a cleaned up
2956  // handle and the filesystem will complete it immediately.
2957  //
2958  // On failure, Fltmgr will call our completion routine,
2959  // so we don't need to clean up here.
2960  //
2961 
2962  (VOID) FltPerformAsynchronousIo( RequestContext->Request,
2964  RequestContext );
2965 
2966  FltFreeGenericWorkItem( WorkItem );
2967 }
2968 
2969 VOID
2971  _In_ PFLT_GENERIC_WORKITEM WorkItem,
2972  _In_ PFLT_FILTER Filter,
2973  _In_ PVOID HandlePtr
2974  )
2975 /*++
2976 
2977 Routine Description:
2978 
2979  This function performs a handle close operation. Since handle
2980  close operations must occur at passive, we queue a workitem to
2981  close the handle when we cannot do so inline. This function
2982  actually performs the close.
2983 
2984 Arguments:
2985 
2986  WorkItem - Pointer to the workitem which triggered the call to this
2987  function.
2988 
2989  Filter - Pointer to our filter object.
2990 
2991  HandlePtr - Our context, specifically the handle we wish to close.
2992 
2993 Return Value:
2994 
2995  None.
2996 
2997 --*/
2998 {
2999  HANDLE Handle = (HANDLE)HandlePtr;
3000  NTSTATUS DummyStatus;
3001 
3002  PAGED_CODE();
3003 
3004  UNREFERENCED_PARAMETER( Filter );
3005 
3006  DummyStatus = FltClose( Handle );
3007  FLT_ASSERT( NT_SUCCESS( DummyStatus ));
3008 
3009  FltFreeGenericWorkItem( WorkItem );
3010 }
3011 
3012 _Pre_satisfies_(*LockHeld)
3013 _Requires_lock_held_(_Global_critical_region_)
3014 _Requires_lock_held_(*HandleContext->Lock)
3015 _When_(*LockHeld == FALSE, _Releases_lock_(_Global_critical_region_))
3016 _When_(*LockHeld == FALSE, _Releases_lock_(*HandleContext->Lock))
3017 VOID
3018 NcNotifyAbort (
3019  _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext,
3020  _In_ NTSTATUS Status,
3021  _Inout_ PBOOLEAN LockHeld
3022  )
3023 /*++
3024 
3025 Routine Description:
3026 
3027  This function aborts a change notify operation. It is shared code
3028  called from both cancel and handle cleanup. It assumes that it is
3029  called with the context lock already held, and may drop the lock
3030  if it needs to complete or cancel requests.
3031 
3032 Arguments:
3033 
3034  HandleContext - The context describing the request we wish to abort.
3035 
3036  Status - Status code to abort with. This should be STATUS_CANCELLED
3037  for cancel requests or STATUS_NOTIFY_CLEANUP for cleanup requests.
3038 
3039  LockHeld - Pointer to a boolean value. On input, this should contain
3040  a value indicating whether the handle context lock is acquired
3041  (and it should be.) On output, this will be set to TRUE if the
3042  lock is acquired or FALSE if not.
3043 
3044 Return Value:
3045 
3046  None.
3047 
3048 --*/
3049 {
3050  PNC_DIR_NOT_CONTEXT NotCtx = &HandleContext->DirectoryNotificationContext;
3051 
3052  FLT_ASSERT( *LockHeld );
3053 
3054  PAGED_CODE();
3055 
3056  //
3057  // If we are hanging on to the user's request, complete it now. The status
3058  // code for this is very specific. Note that in the Munge case the request
3059  // is owned by the filesystem, so we leave the filesystem to deal with this
3060  // when it gets the cleanup request.
3061  //
3062  // Cancel any outstanding requests we have issued (if any.)
3063  //
3064 
3065  if ((NotCtx->Mode == Filter) ||
3066  (NotCtx->Mode == Merge)) {
3067 
3068  PFLT_CALLBACK_DATA RequestToComplete = NotCtx->UserRequest;
3069  PFLT_CALLBACK_DATA RequestToCancel1 = NotCtx->ShadowRequest;
3070  PFLT_CALLBACK_DATA RequestToCancel2 = NotCtx->MappingRequest;
3071  HANDLE HandleToClose = NotCtx->RealParentHandle;
3072  PFILE_OBJECT FileObjectToDereference = NotCtx->RealParentFileObject;
3073  PFLT_GENERIC_WORKITEM WorkItem = NotCtx->RealParentCloseWorkItem;
3074 
3075  NotCtx->ShadowRequest = NULL;
3076  NotCtx->MappingRequest = NULL;
3077  NotCtx->RealParentHandle = NULL;
3078  NotCtx->RealParentFileObject = NULL;
3079  NotCtx->RealParentCloseWorkItem = NULL;
3080 
3081  //
3082  // Attempt to clear the cancel callback on the user's request. If
3083  // this fails, it may indicate that we are cancelling the irp ourselves,
3084  // or may indicate that we're in cleanup and racing with a cancel.
3085  // When racing with cancel, cancel always wins.
3086  //
3087 
3088  if (RequestToComplete) {
3089  NTSTATUS CancelStatus;
3090 
3091  CancelStatus = FltClearCancelCompletion( RequestToComplete );
3092 
3093  if (CancelStatus == STATUS_CANCELLED && Status != STATUS_CANCELLED) {
3094  RequestToComplete = NULL;
3095  } else {
3096  NotCtx->UserRequest = NULL;
3097  }
3098  }
3099 
3100  NcUnlockStreamHandleContext( HandleContext );
3101  *LockHeld = FALSE;
3102 
3103  if (RequestToComplete) {
3104  RequestToComplete->IoStatus.Status = Status;
3105  RequestToComplete->IoStatus.Information = 0;
3106 
3107  FltCompletePendedPreOperation( RequestToComplete, FLT_PREOP_COMPLETE, NULL );
3108  }
3109 
3110 
3111  //
3112  // Cancel any outstanding IOs, ignoring status.
3113  //
3114  // It is possible that we can't cancel because:
3115  //
3116  // 1. The request hasn't reached the filesystem so there's no cancel
3117  // routine. This is not a problem, since the below call will
3118  // mark the request as cancelled so it will complete immediately.
3119  //
3120  // 2. The request is already being completed. It's no big deal if
3121  // we let it complete, just so long as it finishes soon. Note
3122  // that we've already set CleanupSeen/CancelSeen by this point,
3123  // so unless we're cancelling and get a new user request, the
3124  // outcome will be the same as if our cancel succeeded.
3125  //
3126 
3127  if (RequestToCancel1) {
3128 
3129  (VOID)FltCancelIo( RequestToCancel1 );
3130  }
3131 
3132  if (RequestToCancel2) {
3133 
3134  (VOID)FltCancelIo( RequestToCancel2 );
3135  }
3136 
3137  if (HandleToClose) {
3138 
3139  NTSTATUS WorkItemStatus;
3140 
3141  FLT_ASSERT( WorkItem != NULL );
3142 
3143  WorkItemStatus = FltQueueGenericWorkItem( WorkItem,
3146  DelayedWorkQueue,
3147  HandleToClose );
3148 
3149  //
3150  // Queuing the work item will fail if the filter is unloading. All
3151  // we're doing is a handle close, which will get taken care of when
3152  // the process we're in eventually goes away. However we've still
3153  // got an allocated work item to take care of.
3154  //
3155 
3156  if (!NT_SUCCESS( WorkItemStatus )) {
3157 
3158  FltFreeGenericWorkItem( WorkItem );
3159  }
3160  }
3161 
3162  if (FileObjectToDereference) {
3163  ObDereferenceObject( FileObjectToDereference );
3164  }
3165  }
3166 }
3167 
3168 VOID
3170  _In_ PFLT_CALLBACK_DATA Data
3171  )
3172 /*++
3173 
3174 Routine Description:
3175 
3176  This function is called to cancel a change notify operation.
3177 
3178 Arguments:
3179 
3180  Data - The request to cancel. This should be caller-initiated (or we
3181  shouldn't have set a cancel routine on it.)
3182 
3183 Return Value:
3184 
3185  None.
3186 
3187 --*/
3188 {
3189  PNC_STREAM_HANDLE_CONTEXT HandleContext;
3190  PNC_DIR_NOT_CONTEXT NotCtx;
3191  NTSTATUS Status;
3192  BOOLEAN UnlockContext;
3193 
3194  PAGED_CODE();
3195 
3196  //
3197  // Try to figure out the context associated with this request.
3198  // It would be really nice if we had a FltObjects structure for
3199  // this. Are filters really supposed to do this?
3200  //
3201 
3202  Status = FltGetStreamHandleContext( Data->Iopb->TargetInstance,
3203  Data->Iopb->TargetFileObject,
3204  &HandleContext );
3205 
3206  FLT_ASSERT( NT_SUCCESS( Status ));
3207  if (!NT_SUCCESS( Status )) {
3208  return;
3209  }
3210 
3211  FLT_ASSERT( HandleContext != NULL );
3212 
3213  NotCtx = &HandleContext->DirectoryNotificationContext;
3214 
3215  //
3216  // Before looking at the context, we have to acquire the lock.
3217  //
3218 
3219  NcLockStreamHandleContext( HandleContext );
3220  UnlockContext = TRUE;
3221 
3222  //
3223  // We should only ever be called on the user's request.
3224  // It follows that this is a Merge or Filter operation.
3225  //
3226 
3227  FLT_ASSERT( Data == NotCtx->UserRequest );
3228  FLT_ASSERT( NotCtx->Mode == Filter || NotCtx->Mode == Merge );
3229 
3230  //
3231  // Indicate to completing requests that we are cancelling.
3232  // While we are cancelling, if child requests are racing
3233  // with us and completing legitimately, we force them to
3234  // drain out. This flag will be cleared next time the
3235  // user asks for a change notification on this handle.
3236  //
3237 
3238  NotCtx->CancelSeen = TRUE;
3239 
3240  //
3241  // If the user is cancelling, it follows that they cannot
3242  // expect a complete picture of data. Tear down any held
3243  // over buffer that we may happen to have, since it may be
3244  // large and we're not optimizing for recurring work after
3245  // cancel.
3246  //
3247  // We cannot tear down any file names at this point, since
3248  // we're racing with IO completion which may depend on
3249  // those names remaining intact.
3250  //
3251 
3252  if (NotCtx->BufferToFree != NULL) {
3253 
3254  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
3255  NotCtx->BufferToFree = NULL;
3256  NotCtx->BufferLength = 0;
3257  }
3258 
3259  //
3260  // This call may drop our locks. If it does, we are no
3261  // longer synchronized against incoming requests and
3262  // must not tear anything down any further.
3263  //
3264 
3265  NcNotifyAbort( HandleContext, STATUS_CANCELLED, &UnlockContext );
3266 
3267  if (UnlockContext) {
3268  NcUnlockStreamHandleContext( HandleContext );
3269  UnlockContext = FALSE;
3270  }
3271 
3272  FltReleaseContext( HandleContext );
3273 }
3274 
3275 NTSTATUS
3277  _Out_ PNC_DIR_NOT_CONTEXT Context
3278  )
3279 /*++
3280 
3281 Routine Description:
3282 
3283  This function is called to initialize the directory change notification
3284  portion of a stream handle context.
3285 
3286 Arguments:
3287 
3288  Context - Pointer to the directory change notification portion of the
3289  stream handle context.
3290 
3291 Return Value:
3292 
3293  Returns the status of the operation (currently only STATUS_SUCCESS.)
3294 
3295 --*/
3296 {
3297  NTSTATUS Status = STATUS_SUCCESS;
3298 
3299  PAGED_CODE();
3300 
3301  Context->Mode = Uninitialized;
3302  Context->CleanupSeen = FALSE;
3303  Context->InsufficientBufferSeen = FALSE;
3304  Context->UserRequestName.Buffer = NULL;
3305  Context->UserRequestName.Length = Context->UserRequestName.MaximumLength = 0;
3306 
3307  Context->MappingParentName.Buffer = NULL;
3308  Context->MappingParentName.Length = Context->MappingParentName.MaximumLength = 0;
3309 
3310  Context->UserRequest = NULL;
3311  Context->ShadowRequest = NULL;
3312  Context->MappingRequest = NULL;
3313 
3314  Context->InstanceContext = NULL;
3315  Context->RealParentHandle = NULL;
3316  Context->RealParentFileObject = NULL;
3317 
3318  Context->BufferToFree = NULL;
3319  Context->BufferLength = 0;
3320 
3321  return Status;
3322 }
3323 
3324 VOID
3326  _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext
3327  )
3328 /*++
3329 
3330 Routine Description:
3331 
3332  This function is called to process IRP_MJ_CLEANUP for a directory change
3333  notification request. Directory change notifications are cancelled
3334  with a special status code at handle cleanup time. We take this
3335  opportunity to tear down anything we reasonably can, since a file may
3336  be in limbo between cleanup and close for a long time.
3337 
3338 Arguments:
3339 
3340  HandleContext - Pointer to the stream handle context.
3341 
3342 Return Value:
3343 
3344  None.
3345 
3346 --*/
3347 {
3348  PNC_DIR_NOT_CONTEXT NotCtx = &HandleContext->DirectoryNotificationContext;
3349  BOOLEAN UnlockContext;
3350 
3351  PAGED_CODE();
3352 
3353  NcLockStreamHandleContext( HandleContext );
3354  UnlockContext = TRUE;
3355 
3356  //
3357  // Set a flag to indicate that outstanding requests should complete immediately
3358  // without propagating status to the user or buffering results. This also
3359  // prevents new work starting after this point.
3360  //
3361 
3362  FLT_ASSERT( !NotCtx->CleanupSeen );
3363  NotCtx->CleanupSeen = TRUE;
3364 
3365  //
3366  // Complete our requests. Note that this routine may drop our lock.
3367  //
3368 
3369  NcNotifyAbort( HandleContext, STATUS_NOTIFY_CLEANUP, &UnlockContext );
3370 
3371  //
3372  // Lock the structure again if necessary after performing any IO so we can
3373  // continue teardown. Note that the user's request should not come back
3374  // at this point in the case of cleanup.
3375  //
3376 
3377  if (!UnlockContext) {
3378  NcLockStreamHandleContext( HandleContext );
3379  UnlockContext = TRUE;
3380  }
3381 
3382  //
3383  // Once a handle is closed, it should stay closed.
3384  //
3385 
3386  FLT_ASSERT( NotCtx->CleanupSeen );
3387 
3388  FLT_ASSERT( NotCtx->UserRequest == NULL ||
3389  NotCtx->Mode == Munge);
3390 
3391  //
3392  // Although requests may remain, if we've been through cleanup our post
3393  // routine will ensure they can't do squat.
3394  //
3395 
3396  if (NotCtx->UserRequestName.Buffer != NULL) {
3398  }
3399 
3400  if (NotCtx->MappingParentName.Buffer != NULL) {
3402  }
3403 
3404  if (NotCtx->BufferToFree != NULL) {
3405 
3406  ExFreePoolWithTag( NotCtx->BufferToFree, NC_TAG );
3407  NotCtx->BufferToFree = NULL;
3408  NotCtx->BufferLength = 0;
3409  }
3410 
3411  if (NotCtx->InstanceContext != NULL) {
3412  FltReleaseContext( NotCtx->InstanceContext );
3413  NotCtx->InstanceContext = NULL;
3414  }
3415 
3416  NotCtx->Mode = Uninitialized;
3417 
3418  NcUnlockStreamHandleContext( HandleContext );
3419  UnlockContext = FALSE;
3420 }
3421 
3422 VOID
3424  _In_ PNC_DIR_NOT_CONTEXT Context
3425  )
3426 /*++
3427 
3428 Routine Description:
3429 
3430  This function is called to tear down the directory change notification
3431  portion of a stream handle context.
3432 
3433 Arguments:
3434 
3435  Context - Pointer to the directory change notification portion of the
3436  stream handle context.
3437 
3438 Return Value:
3439 
3440  None.
3441 
3442 --*/
3443 {
3444 
3445  PAGED_CODE();
3446 
3447  //
3448  // TODO: Determine when/how this is called. What IRQL are we really at
3449  // here? Are we top level? What should we do if we're not?
3450  //
3451  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
3452  FLT_ASSERT( !KeAreAllApcsDisabled() );
3453 
3454 
3455  //
3456  // We need to be protected against any new work arriving on this handle.
3457  // Typically we expect cleanup to have already been seen by this point,
3458  // which will have torn down most state for this handle. This is not
3459  // necessarily true when the filter is being detached. Since we can't
3460  // tear down a context with active references, we expect that mapping/
3461  // shadow requests are currently NULL, and should not come back - either
3462  // because CleanupSeen is set, or FltMgr is draining us.
3463  //
3464 
3465  FLT_ASSERT( Context->MappingRequest == NULL &&
3466  Context->ShadowRequest == NULL );
3467 
3468  //
3469  // Firstly, close the handles. These can recurse back into the
3470  // filesystem and generate notifications themselves, or cancel
3471  // outstanding notifications, etc. Note that we do not hold the lock
3472  // around this operation.
3473  //
3474 
3475  if (Context->RealParentHandle != NULL) {
3476 
3477  FltClose( Context->RealParentHandle );
3478 
3479  }
3480 
3481  //
3482  // In theory, we should no longer have outstanding notifications at
3483  // this point. The user has closed their handle (completing any
3484  // shadow request) and we have closed ours (completing any mapping
3485  // request.) Now we are safe to teardown, and we don't require a
3486  // lock to do so.
3487  //
3488 
3489  if (Context->RealParentFileObject != NULL) {
3490 
3491  ObDereferenceObject( Context->RealParentFileObject );
3492  }
3493 
3494  Context->RealParentFileObject = NULL;
3495  Context->RealParentHandle = NULL;
3496 
3497  if (Context->RealParentCloseWorkItem != NULL) {
3498  FltFreeGenericWorkItem( Context->RealParentCloseWorkItem );
3499  Context->RealParentCloseWorkItem = NULL;
3500  }
3501 
3502  if (Context->UserRequestName.Buffer != NULL) {
3503  NcFreeUnicodeString( &Context->UserRequestName );
3504  }
3505 
3506  if (Context->MappingParentName.Buffer != NULL) {
3507  NcFreeUnicodeString( &Context->MappingParentName );
3508  }
3509 
3510  if (Context->BufferToFree != NULL) {
3511 
3512  ExFreePoolWithTag( Context->BufferToFree, NC_TAG );
3513  Context->BufferToFree = NULL;
3514  Context->BufferLength = 0;
3515  }
3516 
3517  Context->Mode = Uninitialized;
3518 
3519  if (Context->InstanceContext != NULL) {
3520  FltReleaseContext( Context->InstanceContext );
3521  Context->InstanceContext = NULL;
3522  }
3523 }
3524 
3525 
3526 
NC_NOTIFY_REQUEST_TYPE RequestType
Definition: ncdirnotify.c:40
NTSTATUS NcSetCancelCompletion(_In_ PFLT_CALLBACK_DATA Data, _In_ PFLT_COMPLETE_CANCELED_CALLBACK CanceledCallback)
Definition: nchelper.c:270
#define EMPTY_UNICODE_STRING
Definition: nc.h:33
_NC_NOTIFY_REQUEST_TYPE
Definition: ncdirnotify.c:31
BOOLEAN CleanupSeen
Definition: nc.h:323
PNC_INSTANCE_CONTEXT InstanceContext
Definition: nc.h:369
NTSTATUS NcStreamHandleContextAllocAndAttach(_In_ PFLT_FILTER Filter, _In_ PFLT_INSTANCE Instance, _In_ PFILE_OBJECT FileObject, _Out_ PNC_STREAM_HANDLE_CONTEXT *Context)
Definition: nccontext.c:126
PFLT_GENERIC_WORKITEM RealParentCloseWorkItem
Definition: nc.h:383
#define NcRemoveTrailingSlashIfPresent(US)
NTSTATUS NcAllocateNotifyRequestContext(_In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, _In_ PFILE_OBJECT FileObject, _In_ NC_NOTIFY_REQUEST_TYPE RequestType, _In_ PFLT_CALLBACK_DATA Request, _Out_ PNC_NOTIFY_REQUEST_CONTEXT *NotifyRequestContext)
Definition: ncdirnotify.c:639
UNICODE_STRING MappingParentName
Definition: nc.h:342
PFILE_OBJECT FileObjectToDereference
Definition: ncdirnotify.c:39
NTSTATUS NcGetDestinationNotifyBuffer(_Inout_ PFLT_CALLBACK_DATA Data, _Outptr_result_bytebuffer_maybenull_(*BufferSize) PVOID *DestBuffer, _Out_ PULONG BufferSize)
Definition: ncdirnotify.c:923
NC_MAPPING_ENTRY RealMapping
Definition: nc.h:186
NC_GET_NEW_SYSTEM_BUFFER_ADDRESS NcGetNewSystemBufferAddress
Definition: nccompat.c:81
struct _NC_NOTIFY_REQUEST_CONTEXT NC_NOTIFY_REQUEST_CONTEXT
BOOLEAN IgnoreCase
Definition: nc.h:324
VOID NcCleanupSubNotifyRequest(_In_ PFLT_CALLBACK_DATA SubRequest)
Definition: ncdirnotify.c:892
RtlCopyMemory(OutputStringBuffer, TempMappingBuffer->Data, OutputString->MaximumLength)
FLT_PREOP_CALLBACK_STATUS NcPreNotifyDirectory(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
Definition: ncdirnotify.c:1067
#define Add2Ptr(P, I)
Definition: minispy.h:238
_In_opt_ PFILE_OBJECT _In_opt_ PFLT_INSTANCE Instance
Definition: nc.h:493
#define IRP_MN_NOTIFY_CHANGE_DIRECTORY
Definition: mspyLog.h:319
BOOLEAN InsufficientBufferSeen
Definition: nc.h:325
NC_MAPPING_PATH LongNamePath
Definition: nc.h:173
PFILE_NOTIFY_INFORMATION BufferToFree
Definition: nc.h:385
_When_(Data==NULL, _Pre_satisfies_(FileObject !=NULL &&Instance !=NULL)) _When_(FileObject
HANDLE RealParentHandle
Definition: nc.h:381
#define NC_SEPARATOR
Definition: nc.h:32
FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject))
FLT_POSTOP_CALLBACK_STATUS NcPostNotifyDirectory(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags)
Definition: ncdirnotify.c:2824
#define NcFreeUnicodeString(UCS)
Definition: nc.h:40
return TRUE
#define NcLockStreamHandleContext(C)
Definition: nc.h:444
PFLT_CALLBACK_DATA UserRequest
Definition: nc.h:365
BOOLEAN CancelSeen
Definition: nc.h:322
VOID NcPostNotifyDirectoryReal(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PVOID CompletionContext)
Definition: ncdirnotify.c:2179
_Pre_satisfies_(Data !=NULL)) NTSTATUS NcGetFileNameInformation(_In_opt_ PFLT_CALLBACK_DATA Data
NC_MAPPING Mapping
Definition: nc.h:233
enum _NC_DIR_NOT_CONTEXT::@12 Mode
NTSTATUS NcStreamHandleContextNotCreate(_Out_ PNC_DIR_NOT_CONTEXT Context)
Definition: ncdirnotify.c:3276
NC_MAPPING_ENTRY UserMapping
Definition: nc.h:187
_In_opt_ PFILE_OBJECT _In_opt_ PFLT_INSTANCE _In_ FLT_FILE_NAME_OPTIONS _Outptr_ PFLT_FILE_NAME_INFORMATION * FileNameInformation
Definition: nc.h:493
VOID NcStreamHandleContextNotCleanup(_In_ PNC_STREAM_HANDLE_CONTEXT HandleContext)
Definition: ncdirnotify.c:3325
VOID NcReissueNotifyRequestWorkerRoutine(_In_ PFLT_GENERIC_WORKITEM WorkItem, _In_ PFLT_FILTER Filter, _In_ PVOID RequestPtr)
Definition: ncdirnotify.c:2909
_In_ BOOLEAN _Out_ PFILE_BASIC_INFORMATION Buffer
#define FlagOn(_F, _SF)
Definition: minispy.h:247
_In_ PLARGE_INTEGER _In_ ULONG _In_ ULONG _In_reads_bytes_(Length)
int Ancestor
Definition: nc.h:217
UNREFERENCED_PARAMETER(FileObject)
NC_DIR_NOT_CONTEXT DirectoryNotificationContext
Definition: nc.h:438
PNC_STREAM_HANDLE_CONTEXT UserHandleContext
Definition: ncdirnotify.c:38
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
int InMapping
Definition: nc.h:220
PFLT_CALLBACK_DATA MappingRequest
Definition: nc.h:367
UNICODE_STRING ParentPath
Definition: nc.h:158
BOOLEAN NcComparePath(_In_ PCUNICODE_STRING Name, _In_ PNC_MAPPING_ENTRY Mapping, _Out_opt_ PUNICODE_STRING Remainder, _In_ BOOLEAN IgnoreCase, _In_ BOOLEAN ContainsDevice, _Out_ PNC_PATH_OVERLAP Overlap)
Definition: ncpath.c:13
VOID NcNotifyCancelCallback(_In_ PFLT_CALLBACK_DATA Data)
Definition: ncdirnotify.c:3169
PAGED_CODE()
#define IRP_MJ_DIRECTORY_CONTROL
Definition: mspyLog.h:296
IoStatus Status
LONG NcExceptionFilter(_In_ PEXCEPTION_POINTERS ExceptionPointer, _In_ BOOLEAN AccessingUserBuffer)
Definition: nchelper.c:322
PFLT_CALLBACK_DATA Request
Definition: ncdirnotify.c:41
PFILE_OBJECT RealParentFileObject
Definition: nc.h:382
FORCEINLINE VOID _Releases_lock_(_Global_critical_region_) _Requires_lock_held_(_Global_critical_region_) AvReleaseResource(_Inout_ _Requires_lock_held_(*Resource) _Releases_lock_(*Resource) PERESOURCE Resource)
NC_GLOBAL_DATA NcGlobalData
Definition: nc.c:335
#define NcUnlockStreamHandleContext(C)
Definition: nc.h:447
NTSTATUS NcDirNotifyTranslateBuffers(_In_ PNC_INSTANCE_CONTEXT InstanceContext, _In_ BOOLEAN IgnoreCase, _In_ PUNICODE_STRING UserRequestName, _In_ PUNICODE_STRING OpenedName, _In_reads_bytes_(InputBufferLength) PFILE_NOTIFY_INFORMATION InputSystemBuffer, _Out_writes_bytes_to_(OutputBufferLength, *OutputBufferWritten) PFILE_NOTIFY_INFORMATION OutputUserBuffer, _In_ ULONG InputBufferLength, _In_ ULONG OutputBufferLength, _Out_ PULONG InputBufferConsumed, _Out_ PULONG OutputBufferWritten, _In_ BOOLEAN ReturnRealMappingPaths, _In_ BOOLEAN ReturnInMappingOnly)
VOID NcFreeNotifyRequestContext(_In_ PNC_NOTIFY_REQUEST_CONTEXT NotifyRequestContext)
Definition: ncdirnotify.c:707
#define AlignToSize(_length, _alignment)
Definition: nc.h:35
ULONG CompletionFilter
Definition: nc.h:328
NTSTATUS NcBuildSubNotifyRequest(_In_ PFLT_CALLBACK_DATA PrimaryRequest, _In_ PFLT_INSTANCE Instance, _In_ PFILE_OBJECT FileObject, _In_ PNC_STREAM_HANDLE_CONTEXT HandleContext, _In_ NC_NOTIFY_REQUEST_TYPE RequestType, _Out_ PFLT_CALLBACK_DATA *SubRequest, _Out_ PNC_NOTIFY_REQUEST_CONTEXT *NotifyRequestContext)
Definition: ncdirnotify.c:744
NTSTATUS NcCreateFileHelper(_In_ PFLT_FILTER Filter, _In_opt_ PFLT_INSTANCE Instance, _Out_ PHANDLE FileHandle, _Outptr_opt_ PFILE_OBJECT *FileObject, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_opt_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, _In_ ULONG EaLength, _In_ ULONG Flags, _In_opt_ PFILE_OBJECT ParentFileObject)
Definition: nchelper.c:195
NcLoadRegistryStringCleanup NC_TAG
Definition: ncinit.c:170
UCHAR OperationFlags
Definition: nc.h:327
#define NC_GENERATE_NAME_TAG
Definition: nc.h:17
enum _NC_NOTIFY_REQUEST_TYPE NC_NOTIFY_REQUEST_TYPE
ULONG BufferLength
Definition: nc.h:386
int Parent
Definition: nc.h:218
_Pre_satisfies_ LockHeld _Requires_lock_held_(_Global_critical_region_)
Definition: ncdirnotify.c:114
PFLT_FILTER FilterHandle
Definition: nc.h:475
UNICODE_STRING UserRequestName
Definition: nc.h:341
PFLT_CALLBACK_DATA ShadowRequest
Definition: nc.h:366
struct _NC_NOTIFY_REQUEST_CONTEXT * PNC_NOTIFY_REQUEST_CONTEXT
_In_opt_ PFILE_OBJECT FileObject
Definition: nc.h:493
VOID NcStreamHandleContextNotClose(_In_ PNC_DIR_NOT_CONTEXT Context)
Definition: ncdirnotify.c:3423
FLT_POSTOP_CALLBACK_STATUS NcPostNotifyDirectorySafe(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags)
Definition: ncdirnotify.c:2777
VOID NcCloseHandleWorkerRoutine(_In_ PFLT_GENERIC_WORKITEM WorkItem, _In_ PFLT_FILTER Filter, _In_ PVOID HandlePtr)
Definition: ncdirnotify.c:2970

Social Network


Services Overview

Architect, implement and test file system filter drivers for a wide range of functionality. We can offer several levels of assistance to meet your specific.

Contact Us

You are welcome to contact us for salse or partnership.

Sales: sales@easefilter.com
Support: support@easefilter.com
Info: info@easefilter.com