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

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