WDK Mini Filter Example
ncfileinfo.c
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) 2008 - 2009 Microsoft Corporation
4 
5 Module Name:
6 
7  ncfileinfo.c
8 
9 Abstract:
10 
11  Contains routines to process user-initiated query file and set file
12  information requests.
13 
14 Environment:
15 
16  Kernel mode
17 
18 --*/
19 
20 #include "nc.h"
21 
22 #ifdef ALLOC_PRAGMA
23 #pragma alloc_text(PAGE, NcPreQueryAlternateName)
24 #pragma alloc_text(PAGE, NcPostQueryHardLinks)
25 #pragma alloc_text(PAGE, NcPostQueryName)
26 #pragma alloc_text(PAGE, NcPreSetDisposition)
27 #pragma alloc_text(PAGE, NcPreSetLinkInformation)
28 #pragma alloc_text(PAGE, NcPreSetShortName)
29 #pragma alloc_text(PAGE, NcPreRename)
30 #endif
31 
32 FLT_POSTOP_CALLBACK_STATUS
34  _Inout_ PFLT_CALLBACK_DATA Data,
35  _In_ PCFLT_RELATED_OBJECTS FltObjects,
36  _In_opt_ PVOID CompletionContext,
37  _In_ FLT_POST_OPERATION_FLAGS Flags
38  )
39 /*++
40 
41 Routine Description:
42 
43  This routine is called when the user wants to request a name for a
44  previously opened handle. Since we munged the name to be to the real
45  mapping in pre-create, we must munge it back to the user visible view
46  in response to name requests, even by opened name.
47 
48  Note that this function processes three information classes:
49 
50  FileNameInformation
51  FileNormalizedNameInformation
52  FileAllInformation
53 
54 Arguments:
55 
56  Data - Pointer to the filter CallbackData that is passed to us.
57 
58  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure
59  containing opaque handles to this filter, instance, its
60  associated volume and file object.
61 
62  CompletionContext - The context for the completion routine for
63  this operation. We set this to the handle context for
64  directory change notifications.
65 
66 Return Value:
67 
68  The return value is the Status of the operation.
69 
70 --*/
71 {
72  NTSTATUS Status = STATUS_SUCCESS;
73  NC_PATH_OVERLAP Overlap;
74  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
75  FILE_INFORMATION_CLASS InfoClass = Data->Iopb->Parameters.QueryFileInformation.FileInformationClass;
76  PVOID UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
77  ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length;
78  ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information;
79  ULONG LengthNeeded = 0;
80  ULONG UserStructureSize = 0;
81  ULONG RequiredNameSize = 0;
82  ULONG NameLengthAvailable = 0;
83  PFILE_NAME_INFORMATION NameInfo;
84  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
85  FO_OPENED_CASE_SENSITIVE );
86  UNICODE_STRING Remainder;
87  UNICODE_STRING RemainderCopy = EMPTY_UNICODE_STRING;
88  UNICODE_STRING ReturnedName;
89 
90  UNREFERENCED_PARAMETER( CompletionContext );
91  UNREFERENCED_PARAMETER( Flags );
92 
93  PAGED_CODE();
94 
95  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
96 
97  //
98  // If the operation failed already, we have no processing to do unless the
99  // failure is a buffer overflow.
100  //
101 
102  if (!NT_SUCCESS( Data->IoStatus.Status ) &&
103  (Data->IoStatus.Status != STATUS_BUFFER_OVERFLOW)) {
104 
105  Status = Data->IoStatus.Status;
106  goto NcPostQueryNameInformationCleanup;
107  }
108 
109  //
110  // Get our instance context.
111  //
112 
113  Status = FltGetInstanceContext( FltObjects->Instance,
114  &InstanceContext);
115 
116  if (!NT_SUCCESS( Status )) {
117 
118  goto NcPostQueryNameInformationCleanup;
119  }
120 
121  //
122  // Find the name in the user buffer.
123  //
124 
125  if (InfoClass == FileAllInformation) {
126 
127  NameInfo = & ((PFILE_ALL_INFORMATION) UserBuffer)->NameInformation;
128 
129  } else {
130 
131  FLT_ASSERT( InfoClass == FileNameInformation ||
132  InfoClass == FileNormalizedNameInformation );
133 
134  NameInfo = UserBuffer;
135  }
136 
137  //
138  // If the name is too long for a UNICODE_STRING, we can't process it.
139  // This should never really happen, since UNICODE_STRINGs are used
140  // all across the NT IO model.
141  //
142 
143  if (NameInfo->FileNameLength >= MAXUSHORT) {
144 
145  Status = STATUS_OBJECT_PATH_INVALID;
146  goto NcPostQueryNameInformationCleanup;
147  }
148 
149  //
150  // Now that we have an instance context and NameInfo buffer, see if the file
151  // system failed with a buffer overflow.
152  //
153 
154  if (Data->IoStatus.Status == STATUS_BUFFER_OVERFLOW) {
155 
156  //
157  // We need to bias the FileNameLength field by the difference between
158  // the real and user mapping lengths if the user mapping is longer. This
159  // is so that if the caller re-issues the name query we won't fail with
160  // a buffer overflow in the filter even if the file system succeeded.
161  //
162 
163  if (InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length >
164  InstanceContext->Mapping.RealMapping.LongNamePath.VolumelessName.Length) {
165 
166  NameInfo->FileNameLength += InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length -
167  InstanceContext->Mapping.RealMapping.LongNamePath.VolumelessName.Length;
168  }
169 
170  Status = Data->IoStatus.Status;
171  LengthNeeded = SizeActuallyReturned;
172 
173  goto NcPostQueryNameInformationCleanup;
174  }
175 
176  ReturnedName.Buffer = NameInfo->FileName;
177  ReturnedName.MaximumLength =
178  ReturnedName.Length = (USHORT)NameInfo->FileNameLength;
179 
180  //
181  // Check if the name being returned is within the real mapping.
182  // If not, we have no translation to perform.
183  //
184 
185  NcComparePath( &ReturnedName,
186  &InstanceContext->Mapping.RealMapping,
187  &Remainder,
188  IgnoreCase,
189  FALSE,
190  &Overlap );
191 
192  if (!Overlap.InMapping && !Overlap.Match) {
193 
194  Status = Data->IoStatus.Status;
195  LengthNeeded = SizeActuallyReturned;
196 
197  goto NcPostQueryNameInformationCleanup;
198  }
199 
200  //
201  // Make sure that the user buffer is long enough.
202  //
203 
204  UserStructureSize = FIELD_OFFSET( FILE_NAME_INFORMATION, FileName );
205 
206  if (InfoClass == FileAllInformation) {
207 
208  UserStructureSize += FIELD_OFFSET( FILE_ALL_INFORMATION, NameInformation );
209  }
210 
211  RequiredNameSize = InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length;
212 
213  //
214  // Add back the trailing portion of the name. Note that Remainder
215  // is only defined if InMapping is TRUE.
216  //
217 
218  if (Overlap.InMapping && !Overlap.Match) {
219 
220  RequiredNameSize += Remainder.Length + sizeof(WCHAR);
221  }
222 
223  LengthNeeded = UserStructureSize + RequiredNameSize;
224 
225  //
226  // Whether the user has provided enough buffer or not, the
227  // FILE_NAME_INFORMATION.FileNameLength field has to contain the total length
228  // of the name we want to return.
229  //
230 
231  NameInfo->FileNameLength = RequiredNameSize;
232 
233  //
234  // If the user's buffer is not big enough to handle the name we need to return,
235  // we will copy in as much as we can and return STATUS_BUFFER_OVERFLOW. The
236  // user expects that the needed name length will be reported in the
237  // FILE_NAME_INFORMATION.FileNameLength field.
238  //
239 
240  if (UserBufferLength < LengthNeeded) {
241 
242  NameLengthAvailable = UserBufferLength - UserStructureSize;
243 
244  //
245  // Truncate the LengthNeeded value since it will be returned in the
246  // IoStatus block to tell I/O Manager how much to copy back to the
247  // user's buffer.
248  //
249 
250  LengthNeeded = UserBufferLength;
251 
252  Status = STATUS_BUFFER_OVERFLOW;
253 
254  //
255  // We have enough space. Let's assume we'll succeed to copy the name.
256  //
257 
258  } else {
259 
260  NameLengthAvailable = NameInfo->FileNameLength;
261 
262  Status = STATUS_SUCCESS;
263  }
264 
265  if (Overlap.InMapping && !Overlap.Match) {
266 
267  //
268  // Copy the remainder of the returned name from the user. This is
269  // done so that we can rewrite the user's buffer. Note that if we
270  // are not a match, we expect some remainder.
271  //
272 
273  FLT_ASSERT( Remainder.Length > 0 );
274 
275  RemainderCopy.Buffer = ExAllocatePoolWithTag( PagedPool,
276  Remainder.Length,
277  NC_TAG );
278 
279  if (RemainderCopy.Buffer == NULL) {
280 
281  Status = STATUS_INSUFFICIENT_RESOURCES;
282  goto NcPostQueryNameInformationCleanup;
283  }
284 
285  RtlCopyMemory( RemainderCopy.Buffer,
286  Remainder.Buffer,
287  Remainder.Length );
288 
289  RemainderCopy.MaximumLength =
290  RemainderCopy.Length = Remainder.Length;
291  }
292 
293  //
294  // Firstly, copy back the name to our mapping.
295  //
296 
297  RtlCopyMemory( &NameInfo->FileName,
298  InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Buffer,
299  min(InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length,
300  NameLengthAvailable) );
301 
302  if (NameLengthAvailable > InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length) {
303 
304  NameLengthAvailable -= InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length;
305 
306  } else {
307 
308  NameLengthAvailable = 0;
309  }
310 
311  //
312  // If the object being queried is within the mapping, copy back the
313  // remainder of that name.
314  //
315 
316  if ((NameLengthAvailable > 0) &&
317  Overlap.InMapping && !Overlap.Match) {
318 
319  NameInfo->FileName[InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length / sizeof(WCHAR)] = '\\';
320 
321  RtlCopyMemory( Add2Ptr( &NameInfo->FileName,
322  InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length + sizeof(WCHAR) ),
323  RemainderCopy.Buffer,
324  min(RemainderCopy.Length, NameLengthAvailable) );
325  }
326 
327  //
328  // We have finished the query, complete operation.
329  //
330 
331 NcPostQueryNameInformationCleanup:
332 
333  Data->IoStatus.Status = Status;
334 
335  //
336  // Note that STATUS_BUFFER_OVERFLOW is not a success code, but for name queries
337  // it indicates that the caller needs to allocate a bigger buffer. The needed
338  // size for the name is stored in the FILE_NAME_INFORMATION.FileNameLength field.
339  // Therefore the IoStatus.Information field must not be 0 for a buffer overflow,
340  // it must contain the size of the data that was actually copied.
341  //
342 
343  if (NT_SUCCESS( Status ) ||
344  (Status == STATUS_BUFFER_OVERFLOW)) {
345 
346  Data->IoStatus.Information = LengthNeeded;
347 
348  } else {
349 
350  Data->IoStatus.Information = 0;
351  }
352 
353  if (RemainderCopy.Buffer != NULL) {
354 
355  ExFreePoolWithTag( RemainderCopy.Buffer, NC_TAG );
356  }
357 
358  if (InstanceContext != NULL) {
359 
360  FltReleaseContext( InstanceContext );
361  }
362 
363  return FLT_POSTOP_FINISHED_PROCESSING;
364 }
365 
366 FLT_PREOP_CALLBACK_STATUS
368  _Inout_ PFLT_CALLBACK_DATA Data,
369  _In_ PCFLT_RELATED_OBJECTS FltObjects,
370  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
371  )
372 /*++
373 
374 Routine Description:
375 
376  This routine is called when the user wants to find the alternate name
377  for a previously opened handle. An alternate name means the short
378  half of a long/short name pair.
379 
380 Arguments:
381 
382  Data - Pointer to the filter CallbackData that is passed to us.
383 
384  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure
385  containing opaque handles to this filter, instance, its
386  associated volume and file object.
387 
388  CompletionContext - The context for the completion routine for
389  this operation. We set this to the handle context for
390  directory change notifications.
391 
392 Return Value:
393 
394  The return value is the Status of the operation.
395 
396 --*/
397 {
398  NTSTATUS Status;
399  FLT_PREOP_CALLBACK_STATUS ReturnValue;
400  NC_PATH_OVERLAP Overlap;
401  PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
402  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
403  PVOID UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
404  ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length;
405  ULONG LengthNeeded = 0;
406  PFILE_NAME_INFORMATION NameInfo;
407  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
408  FO_OPENED_CASE_SENSITIVE );
409  PUNICODE_STRING FinalComponentToReturn;
410 
411  UNREFERENCED_PARAMETER( CompletionContext );
412 
413  PAGED_CODE();
414 
415  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
416 
417  //
418  // Get our instance context.
419  //
420 
421  Status = FltGetInstanceContext( FltObjects->Instance,
422  &InstanceContext);
423 
424  if (!NT_SUCCESS( Status )) {
425 
426  ReturnValue = FLT_PREOP_COMPLETE;
427  goto NcPreQueryAlternateNameInformationCleanup;
428  }
429 
430  //
431  // Get the file's name.
432  //
433 
434  Status = NcGetFileNameInformation( Data,
435  NULL,
436  NULL,
437  FLT_FILE_NAME_OPENED |
438  FLT_FILE_NAME_QUERY_DEFAULT |
439  FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
440  &FileInfo );
441 
442  if (!NT_SUCCESS( Status )) {
443 
444  ReturnValue = FLT_PREOP_COMPLETE;
445  goto NcPreQueryAlternateNameInformationCleanup;
446  }
447 
448  Status = FltParseFileNameInformation( FileInfo );
449 
450  if (!NT_SUCCESS( Status )) {
451 
452  ReturnValue = FLT_PREOP_COMPLETE;
453  goto NcPreQueryAlternateNameInformationCleanup;
454  }
455 
456  //
457  // We only need to handle the case where a shortname is being
458  // generated on the mapping itself. These names are final
459  // component path only, so any files within the mapping will
460  // still be correct even if we don't munge them.
461  //
462 
463  NcComparePath( &FileInfo->Name,
464  &InstanceContext->Mapping.UserMapping,
465  NULL,
466  IgnoreCase,
467  TRUE,
468  &Overlap );
469 
470  if (!Overlap.Match) {
471 
472  NcComparePath( &FileInfo->Name,
473  &InstanceContext->Mapping.RealMapping,
474  NULL,
475  IgnoreCase,
476  TRUE,
477  &Overlap );
478 
479  FLT_ASSERT( !Overlap.Match );
480 
481  if (!Overlap.Match) {
482 
483  //
484  // This file is not the mapping, so we can just let this
485  // request go down normally.
486  //
487 
488  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
489  goto NcPreQueryAlternateNameInformationCleanup;
490  }
491  }
492 
493  //
494  // Return the short name.
495  //
496  // Note that this behavior differs from the filesystem in two respects:
497  //
498  // 1. We are not attempting to detect (and fail for) an open-by-ID
499  // handle. Since these are link agnostic, returning data is
500  // meaningless.
501  //
502  // 2. We may support having multiple alternate names for multiple
503  // links on the file, if the mapping was created as a file then
504  // a hardlink was created with a mapping name. This file thus
505  // contains two shortnames, which NTFS does not support. In
506  // theory APIs should be clean to this (and a future filesystem
507  // may wish to support it.)
508  //
509 
510  FinalComponentToReturn = &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName;
511 
512  //
513  // Make sure that the user buffer is long enough.
514  //
515 
516  LengthNeeded = FIELD_OFFSET( FILE_NAME_INFORMATION, FileName );
517 
518  LengthNeeded += FinalComponentToReturn->Length;
519 
520  if (UserBufferLength < LengthNeeded) {
521 
522  Status = STATUS_BUFFER_OVERFLOW;
523  ReturnValue = FLT_PREOP_COMPLETE;
524  goto NcPreQueryAlternateNameInformationCleanup;
525  }
526 
527  NameInfo = UserBuffer;
528 
529  //
530  // Copy back the name to our mapping.
531  //
532 
533  RtlCopyMemory( &NameInfo->FileName,
534  FinalComponentToReturn->Buffer,
535  FinalComponentToReturn->Length );
536 
537  NameInfo->FileNameLength = FinalComponentToReturn->Length;
538 
539  //
540  // We have finished the query, complete operation.
541  //
542 
543  Status = STATUS_SUCCESS;
544  ReturnValue = FLT_PREOP_COMPLETE;
545 
546 NcPreQueryAlternateNameInformationCleanup:
547 
548  if (ReturnValue == FLT_PREOP_COMPLETE) {
549 
550  Data->IoStatus.Status = Status;
551 
552  //
553  // Note that STATUS_BUFFER_OVERFLOW is not a success code, and
554  // will result in zero bytes being copied back to the caller.
555  //
556 
557  if (NT_SUCCESS( Status )) {
558 
559  Data->IoStatus.Information = LengthNeeded;
560  } else {
561 
562  Data->IoStatus.Information = 0;
563  }
564  }
565 
566  if (FileInfo != NULL) {
567 
568  FltReleaseFileNameInformation( FileInfo );
569  }
570 
571  if (InstanceContext != NULL) {
572 
573  FltReleaseContext( InstanceContext );
574  }
575 
576  return ReturnValue;
577 }
578 
579 
580 FLT_POSTOP_CALLBACK_STATUS
582  _Inout_ PFLT_CALLBACK_DATA Data,
583  _In_ PCFLT_RELATED_OBJECTS FltObjects,
584  _In_opt_ PVOID CompletionContext,
585  _In_ FLT_POST_OPERATION_FLAGS Flags
586  )
587 /*++
588 
589 Routine Description:
590 
591  This routine is called when the user wants to enumerate all hard links
592  for a previously opened handle.
593 
594 Arguments:
595 
596  Data - Pointer to the filter CallbackData that is passed to us.
597 
598  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure
599  containing opaque handles to this filter, instance, its
600  associated volume and file object.
601 
602  CompletionContext - The context for the completion routine for
603  this operation. We set this to the handle context for
604  directory change notifications.
605 
606 Return Value:
607 
608  The return value is the Status of the operation.
609 
610 --*/
611 {
612  NTSTATUS Status;
613  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
614  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
615  FO_OPENED_CASE_SENSITIVE );
616 
617  //
618  // Pointer to the buffer returned to us, which we will also return to
619  // our caller; length of the buffer; size of the buffer filled in
620  // by the filesystem; size of the buffer that we filled in
621  //
622 
623  PFILE_LINKS_INFORMATION UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
624  ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length;
625  ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information;
626  ULONG BytesWritten = 0;
627 
628  //
629  // Length of the current entry that we're processing; a UNICODE_STRING
630  // for the final component of the name that we're processing; and
631  // a flag indicating whether this entry is being modified
632  //
633 
634  ULONG EntrySize;
635  UNICODE_STRING EntryName;
636  BOOLEAN MungeEntry;
637 
638  //
639  // Copy of the buffer returned from the filesystem, our iterators as
640  // we process this buffer, and a pointer to the previous destination
641  // entry (if one exists) so we can zero the offset to next entry
642  // field on completion
643  //
644 
645  PFILE_LINKS_INFORMATION OriginalBuffer = NULL;
646  PFILE_LINK_ENTRY_INFORMATION SourceEntry;
647  PFILE_LINK_ENTRY_INFORMATION DestEntry;
648  PFILE_LINK_ENTRY_INFORMATION PrevDestEntry = NULL;
649 
650 
651  //
652  // Variables that we use to obtain IDs to the mapping parents.
653  //
654 
655  OBJECT_ATTRIBUTES MappingParentAttributes;
656  HANDLE MappingParentHandle = NULL;
657  PFILE_OBJECT MappingParentFileObject = NULL;
658  IO_STATUS_BLOCK MappingParentStatusBlock;
659 
660  //
661  // File IDs for the mapping parents
662  //
663 
664  LONGLONG RealMappingParentId;
665  LONGLONG UserMappingParentId;
666 
667  UNREFERENCED_PARAMETER( CompletionContext );
668  UNREFERENCED_PARAMETER( Flags );
669 
670  PAGED_CODE();
671 
672  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
673 
674  //
675  // If the buffer was invalid, or if the call failed, leave now. Note
676  // that this may be STATUS_BUFFER_OVERFLOW and the number of bytes the
677  // caller needs will be reported inaccurately. To handle this we'd
678  // really need to issue our own call to get the full buffer, then
679  // transform it to find the "correct" length the user will need.
680  //
681  // Rather than do this, we return the caller a value for bytes required
682  // which may not be accurate. When they call us again, we will
683  // have data to transform, and can then fail the call again specifying
684  // a new value for bytes required that is accurate.
685  //
686 
687  if (SizeActuallyReturned <= (ULONG)FIELD_OFFSET( FILE_LINKS_INFORMATION, Entry ) ||
688  !NT_SUCCESS( Data->IoStatus.Status )) {
689 
690  BytesWritten = SizeActuallyReturned;
691 
692  Status = Data->IoStatus.Status;
693  goto NcPostQueryHardLinkInformationCleanup;
694  }
695 
696  //
697  // Get our instance context.
698  //
699 
700  Status = FltGetInstanceContext( FltObjects->Instance,
701  &InstanceContext);
702 
703  if (!NT_SUCCESS( Status )) {
704 
705  goto NcPostQueryHardLinkInformationCleanup;
706  }
707 
708  //
709  // Open the mapping parents and query IDs.
710  //
711 
712  InitializeObjectAttributes( &MappingParentAttributes,
713  &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath,
714  OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0),
715  NULL,
716  NULL);
717 
718  Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter
719  Data->Iopb->TargetInstance, // Instance
720  &MappingParentHandle, // Returned Handle
721  &MappingParentFileObject, // Returned FileObject
722  FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access
723  &MappingParentAttributes, // object attributes
724  &MappingParentStatusBlock, // Returned IOStatusBlock
725  0, // Allocation Size
726  FILE_ATTRIBUTE_NORMAL, // File Attributes
727  0, // Share Access
728  FILE_OPEN, // Create Disposition
729  FILE_DIRECTORY_FILE, // Create Options
730  NULL, // Ea Buffer
731  0, // EA Length
732  IO_IGNORE_SHARE_ACCESS_CHECK, // Flags
733  Data->Iopb->TargetFileObject ); // Transaction info.
734 
735  if (!NT_SUCCESS( Status )) {
736 
737  FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND &&
738  Status != STATUS_OBJECT_NAME_NOT_FOUND );
739 
740  goto NcPostQueryHardLinkInformationCleanup;
741  }
742 
743  Status = FltQueryInformationFile( Data->Iopb->TargetInstance,
744  MappingParentFileObject,
745  &RealMappingParentId,
746  sizeof(RealMappingParentId),
747  FileInternalInformation,
748  NULL );
749 
750  if (!NT_SUCCESS( Status )) {
751 
752  goto NcPostQueryHardLinkInformationCleanup;
753  }
754 
755  FltClose( MappingParentHandle );
756  ObDereferenceObject( MappingParentFileObject );
757 
758  MappingParentHandle = NULL;
759  MappingParentFileObject = NULL;
760 
761  InitializeObjectAttributes( &MappingParentAttributes,
762  &InstanceContext->Mapping.UserMapping.LongNamePath.ParentPath,
763  OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0),
764  NULL,
765  NULL);
766 
767  Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter
768  Data->Iopb->TargetInstance, // Instance
769  &MappingParentHandle, // Returned Handle
770  &MappingParentFileObject, // Returned FileObject
771  FILE_READ_ATTRIBUTES|FILE_TRAVERSE, // Desired Access
772  &MappingParentAttributes, // object attributes
773  &MappingParentStatusBlock, // Returned IOStatusBlock
774  0, // Allocation Size
775  FILE_ATTRIBUTE_NORMAL, // File Attributes
776  0, // Share Access
777  FILE_OPEN, // Create Disposition
778  FILE_DIRECTORY_FILE, // Create Options
779  NULL, // Ea Buffer
780  0, // EA Length
781  IO_IGNORE_SHARE_ACCESS_CHECK, // Flags
782  Data->Iopb->TargetFileObject ); // Transaction info.
783 
784  if (!NT_SUCCESS( Status )) {
785 
786  FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND &&
787  Status != STATUS_OBJECT_NAME_NOT_FOUND );
788 
789  goto NcPostQueryHardLinkInformationCleanup;
790  }
791 
792  Status = FltQueryInformationFile( Data->Iopb->TargetInstance,
793  MappingParentFileObject,
794  &UserMappingParentId,
795  sizeof(UserMappingParentId),
796  FileInternalInformation,
797  NULL );
798 
799  if (!NT_SUCCESS( Status )) {
800 
801  goto NcPostQueryHardLinkInformationCleanup;
802  }
803 
804  FltClose( MappingParentHandle );
805  ObDereferenceObject( MappingParentFileObject );
806 
807  MappingParentHandle = NULL;
808  MappingParentFileObject = NULL;
809 
810  //
811  // Take a copy of the results of the call from the filesystem.
812  //
813 
814  OriginalBuffer = ExAllocatePoolWithTag( PagedPool,
815  SizeActuallyReturned,
816  NC_TAG );
817 
818  if (OriginalBuffer == NULL) {
819 
820  Status = STATUS_INSUFFICIENT_RESOURCES;
821  goto NcPostQueryHardLinkInformationCleanup;
822  }
823 
824  RtlCopyMemory( OriginalBuffer, UserBuffer, SizeActuallyReturned );
825 
826  //
827  // Set up our iterators to walk through the returned links.
828  //
829 
830  DestEntry = &UserBuffer->Entry;
831 
832  if (UserBuffer->EntriesReturned >= 1) {
833  SourceEntry = &OriginalBuffer->Entry;
834  } else {
835  SourceEntry = NULL;
836  }
837  UserBuffer->EntriesReturned = 0;
838  BytesWritten = FIELD_OFFSET( FILE_LINKS_INFORMATION, Entry );
839  UserBuffer->BytesNeeded = BytesWritten;
840 
841  while( SourceEntry ) {
842 
843  //
844  // Assume we don't need to munge the link. If the parent IDs,
845  // final component lengths and final component strings correspond
846  // to the real mapping, we will need to transform it.
847  //
848 
849  MungeEntry = FALSE;
850 
851  if (SourceEntry->ParentFileId == RealMappingParentId) {
852 
853  if (SourceEntry->FileNameLength * sizeof(WCHAR) >= MAXUSHORT) {
854 
855  Status = STATUS_OBJECT_PATH_INVALID;
856  goto NcPostQueryHardLinkInformationCleanup;
857  }
858 
859  EntryName.Buffer = SourceEntry->FileName;
860  EntryName.Length = EntryName.MaximumLength = (USHORT)SourceEntry->FileNameLength * sizeof(WCHAR);
861 
862  if (EntryName.Length == InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName.Length &&
863  RtlCompareUnicodeString( &EntryName, &InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName, IgnoreCase ) == 0) {
864 
865  MungeEntry = TRUE;
866  }
867 
868  //
869  // TODO: Preserve shortness in output
870  //
871 
872  if (EntryName.Length == InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName.Length &&
873  RtlCompareUnicodeString( &EntryName, &InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName, IgnoreCase ) == 0) {
874 
875  MungeEntry = TRUE;
876  }
877  }
878 
879  //
880  // Calculate the length of the entry that we want to write.
881  //
882 
883  if (MungeEntry) {
884 
885  EntrySize = FIELD_OFFSET( FILE_LINK_ENTRY_INFORMATION, FileName ) +
886  InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length;
887 
888  EntrySize = AlignToSize( EntrySize, 8 );
889 
890  } else {
891 
892  EntrySize = FIELD_OFFSET( FILE_LINK_ENTRY_INFORMATION, FileName ) +
893  SourceEntry->FileNameLength * sizeof(WCHAR);
894 
895  EntrySize = AlignToSize( EntrySize, 8 );
896  }
897 
898  //
899  // Record how much space the caller would need to return all entries.
900  //
901 
902  UserBuffer->BytesNeeded += EntrySize;
903 
904  //
905  // If we have space, copy this entry into the user's buffer and
906  // advance our destination iterator.
907  //
908 
909  if (BytesWritten + EntrySize <= UserBufferLength) {
910 
911  if (MungeEntry) {
912 
913  DestEntry->NextEntryOffset = EntrySize;
914  DestEntry->ParentFileId = UserMappingParentId;
915  DestEntry->FileNameLength = InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length / sizeof(WCHAR);
916  RtlCopyMemory( DestEntry->FileName,
917  InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Buffer,
918  InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length );
919  } else {
920 
921  RtlCopyMemory( DestEntry, SourceEntry, EntrySize );
922  }
923  PrevDestEntry = DestEntry;
924  DestEntry = Add2Ptr( DestEntry, EntrySize );
925  UserBuffer->EntriesReturned++;
926  BytesWritten += EntrySize;
927  }
928 
929  //
930  // If we still have links that we have not yet consumed, move
931  // to those.
932  //
933 
934  if (SourceEntry->NextEntryOffset != 0) {
935  SourceEntry = Add2Ptr( SourceEntry, SourceEntry->NextEntryOffset );
936  } else {
937  SourceEntry = NULL;
938  }
939  }
940 
941  //
942  // If we already copied one or more links, make sure our list is
943  // correctly terminated.
944  //
945 
946  if (PrevDestEntry != NULL) {
947  PrevDestEntry->NextEntryOffset = 0;
948  }
949 
950  //
951  // If we copied all results, return STATUS_SUCCESS. If we saw entries
952  // that we did not copy, return STATUS_BUFFER_OVERFLOW.
953  //
954 
955  if (BytesWritten == UserBuffer->BytesNeeded) {
956  Status = STATUS_SUCCESS;
957  } else {
958  Status = STATUS_BUFFER_OVERFLOW;
959  }
960 
961 NcPostQueryHardLinkInformationCleanup:
962 
963  Data->IoStatus.Status = Status;
964 
965  if (NT_SUCCESS( Status ) || Status == STATUS_BUFFER_OVERFLOW) {
966 
967  Data->IoStatus.Information = BytesWritten;
968  } else {
969 
970  Data->IoStatus.Information = 0;
971  }
972 
973  if (MappingParentHandle != NULL) {
974 
975  FltClose( MappingParentHandle );
976  MappingParentHandle = NULL;
977  }
978 
979  if (MappingParentFileObject != NULL) {
980 
981  ObDereferenceObject( MappingParentFileObject );
982  MappingParentFileObject = NULL;
983  }
984 
985  if (OriginalBuffer != NULL) {
986 
987  ExFreePoolWithTag( OriginalBuffer, NC_TAG );
988  }
989 
990  if (InstanceContext != NULL) {
991 
992  FltReleaseContext( InstanceContext );
993  }
994 
995  return FLT_POSTOP_FINISHED_PROCESSING;
996 }
997 
998 FLT_PREOP_CALLBACK_STATUS
1000  _Inout_ PFLT_CALLBACK_DATA Data,
1001  _In_ PCFLT_RELATED_OBJECTS FltObjects,
1002  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
1003  )
1004 /*++
1005 
1006 Routine Description:
1007 
1008  This routine is called when the user wants to change the short name
1009  for a previously opened handle.
1010 
1011 Arguments:
1012 
1013  Data - Pointer to the filter CallbackData that is passed to us.
1014 
1015  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure
1016  containing opaque handles to this filter, instance, its
1017  associated volume and file object.
1018 
1019  CompletionContext - The context for the completion routine for
1020  this operation. We set this to the handle context for
1021  directory change notifications.
1022 
1023 Return Value:
1024 
1025  The return value is the Status of the operation.
1026 
1027 --*/
1028 {
1029  FLT_PREOP_CALLBACK_STATUS ReturnValue;
1030  NTSTATUS Status;
1031  PFILE_NAME_INFORMATION NameInfo =
1032  Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
1033  PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
1034  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
1035  NC_PATH_OVERLAP RealOverlap;
1036  NC_PATH_OVERLAP UserOverlap;
1037  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
1038  FO_OPENED_CASE_SENSITIVE );
1039 
1040  PAGED_CODE();
1041 
1042  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
1043 
1044  UNREFERENCED_PARAMETER( CompletionContext );
1045 
1046  Status = FltGetInstanceContext( FltObjects->Instance,
1047  &InstanceContext);
1048 
1049  if (!NT_SUCCESS( Status )) {
1050 
1051  ReturnValue = FLT_PREOP_COMPLETE;
1052  goto NcPreSetShortNameCleanup;
1053  }
1054 
1055  //
1056  // Let's skip doing any of this work for file systems that we know don't
1057  // have short names.
1058  //
1059 
1060  if ((InstanceContext->VolumeFilesystemType == FLT_FSTYPE_EXFAT) ||
1061  (InstanceContext->VolumeFilesystemType == FLT_FSTYPE_REFS)) {
1062 
1063  ReturnValue = FLT_PREOP_COMPLETE;
1064  goto NcPreSetShortNameCleanup;
1065  }
1066 
1067  //
1068  // Get the file's name.
1069  //
1070 
1071  Status = NcGetFileNameInformation( Data,
1072  NULL,
1073  NULL,
1074  FLT_FILE_NAME_OPENED |
1075  FLT_FILE_NAME_QUERY_DEFAULT |
1076  FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
1077  &FileInfo );
1078 
1079  if (!NT_SUCCESS( Status )) {
1080 
1081  ReturnValue = FLT_PREOP_COMPLETE;
1082  goto NcPreSetShortNameCleanup;
1083  }
1084 
1085  Status = FltParseFileNameInformation( FileInfo );
1086 
1087  if (!NT_SUCCESS( Status )) {
1088 
1089  ReturnValue = FLT_PREOP_COMPLETE;
1090  goto NcPreSetShortNameCleanup;
1091  }
1092 
1093  //
1094  // Calculate Overlap and Remainder
1095  //
1096 
1097  NcComparePath( &FileInfo->Name,
1098  &InstanceContext->Mapping.UserMapping,
1099  NULL,
1100  IgnoreCase,
1101  TRUE,
1102  &UserOverlap );
1103 
1104  NcComparePath( &FileInfo->Name,
1105  &InstanceContext->Mapping.RealMapping,
1106  NULL,
1107  IgnoreCase,
1108  TRUE,
1109  &RealOverlap );
1110 
1111  //
1112  // Currently the file names that we use are read only, so changing a
1113  // shortname on the mapping or any of its ancestors cannot be
1114  // supported.
1115  //
1116  // TODO: Should we support this?
1117  //
1118 
1119  if (RealOverlap.Match ||
1120  UserOverlap.Match ||
1121  RealOverlap.Ancestor ||
1122  UserOverlap.Ancestor ||
1123  NameInfo->FileNameLength > MAXUSHORT) {
1124 
1125  Status = STATUS_ACCESS_DENIED;
1126  ReturnValue = FLT_PREOP_COMPLETE;
1127  goto NcPreSetShortNameCleanup;
1128  }
1129 
1130  //
1131  // If the user is attempting to set a name which is used by the real
1132  // file, we let the request go to the file system (which will fail
1133  // it.) For names used by the user mapping, we need to detect and
1134  // fail those.
1135  //
1136 
1137  if (UserOverlap.Peer) {
1138 
1139  UNICODE_STRING TargetComponent;
1140 
1141  TargetComponent.Buffer = NameInfo->FileName;
1142  TargetComponent.Length = TargetComponent.MaximumLength = (USHORT)NameInfo->FileNameLength;
1143 
1144  if( RtlCompareUnicodeString( &TargetComponent,
1146  IgnoreCase) == 0 ||
1147  RtlCompareUnicodeString( &TargetComponent,
1149  IgnoreCase) == 0 ) {
1150 
1151  Status = STATUS_ACCESS_DENIED;
1152  ReturnValue = FLT_PREOP_COMPLETE;
1153  goto NcPreSetShortNameCleanup;
1154  }
1155 
1156  }
1157 
1158  //
1159  // If the user is not setting a short name on our mapping, an ancestor
1160  // of our mapping or targetting our mapping, let the request go to the
1161  // file system.
1162  //
1163 
1164  Status = STATUS_SUCCESS;
1165  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
1166 
1167 NcPreSetShortNameCleanup:
1168 
1169  if (ReturnValue == FLT_PREOP_COMPLETE) {
1170 
1171  Data->IoStatus.Status = Status;
1172  }
1173 
1174  if (FileInfo != NULL) {
1175 
1176  FltReleaseFileNameInformation( FileInfo );
1177  }
1178 
1179  if (InstanceContext != NULL) {
1180 
1181  FltReleaseContext( InstanceContext );
1182  }
1183 
1184  return ReturnValue;
1185 }
1186 
1187 
1188 FLT_PREOP_CALLBACK_STATUS
1190  _Inout_ PFLT_CALLBACK_DATA Data,
1191  _In_ PCFLT_RELATED_OBJECTS FltObjects,
1192  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
1193  )
1194 /*++
1195 
1196 Routine Description:
1197 
1198  Fltmgr callback which manages setting the delete disposition on a file.
1199  We must disallow setting the delete disposition on an ancestor of either
1200  mapping because otherwise we would have to maintain the mapping's
1201  short/long name pairings.
1202 
1203 Arguments:
1204 
1205  Data - Pointer to the filter CallbackData that is passed to us.
1206 
1207  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
1208  opaque handles to this filter, instance, its associated volume and
1209  file object.
1210 
1211  CompletionContext - The context for the completion routine for this
1212  operation.
1213 
1214 Return Value:
1215 
1216  The return value is the Status of the operation.
1217 
1218 --*/
1219 {
1220  NTSTATUS Status;
1221  FLT_PREOP_CALLBACK_STATUS ReturnValue;
1222  PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
1223  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
1224  PNC_MAPPING Mapping;
1225  NC_PATH_OVERLAP RealOverlap;
1226  NC_PATH_OVERLAP UserOverlap;
1227  FILE_INFORMATION_CLASS fileInformationClass;
1228  BOOLEAN IsDeleteFile;
1229  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
1230  FO_OPENED_CASE_SENSITIVE );
1231 
1232  PAGED_CODE();
1233 
1234  UNREFERENCED_PARAMETER( CompletionContext );
1235 
1236  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
1237 
1238  fileInformationClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
1239 
1240  //
1241  // See if they are setting the delete disposition to false.
1242  // If they are we can passthrough. We don't care if people
1243  // want to mark the mapping as "don't delete".
1244  //
1245 
1246  IsDeleteFile = (fileInformationClass == FileDispositionInformationEx) ?
1247  BooleanFlagOn( ((PFILE_DISPOSITION_INFORMATION_EX)Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->Flags, FILE_DISPOSITION_DELETE ) :
1248  ((PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->DeleteFile;
1249 
1250  if (IsDeleteFile == FALSE) {
1251 
1252  Status = STATUS_SUCCESS;
1253  goto NcPreSetDispositionCleanup;
1254  }
1255 
1256  //
1257  // The user is trying to delete a file.
1258  // We have to make sure that the file is not an ancestor of either mapping.
1259  //
1260 
1261  //
1262  // Get the file's name.
1263  //
1264 
1265  Status = NcGetFileNameInformation( Data,
1266  NULL,
1267  NULL,
1268  FLT_FILE_NAME_OPENED |
1269  FLT_FILE_NAME_QUERY_DEFAULT |
1270  FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
1271  &FileInfo);
1272 
1273  if (!NT_SUCCESS( Status )) {
1274 
1275  goto NcPreSetDispositionCleanup;
1276  }
1277 
1278  Status = FltParseFileNameInformation( FileInfo );
1279 
1280  if (!NT_SUCCESS( Status )) {
1281 
1282  goto NcPreSetDispositionCleanup;
1283  }
1284 
1285  //
1286  // Get the mapping
1287  //
1288 
1289  Status = FltGetInstanceContext( FltObjects->Instance,
1290  &InstanceContext );
1291 
1292  if (!NT_SUCCESS( Status )) {
1293 
1294  goto NcPreSetDispositionCleanup;
1295  }
1296 
1297  Mapping = &InstanceContext->Mapping;
1298 
1299  //
1300  // Check to see of this will delete an ancestor of the real mapping.
1301  //
1302 
1303  NcComparePath( &FileInfo->Name,
1304  &Mapping->RealMapping,
1305  NULL,
1306  IgnoreCase,
1307  TRUE,
1308  &RealOverlap );
1309 
1310  if (RealOverlap.Ancestor) {
1311 
1312  //
1313  // The file is an ancestor of the real mapping, so disallow setting
1314  // disposition.
1315  //
1316 
1317  Status = STATUS_ACCESS_DENIED;
1318  goto NcPreSetDispositionCleanup;
1319  }
1320 
1321  //
1322  // Check the user mapping.
1323  //
1324 
1325  NcComparePath( &FileInfo->Name,
1326  &Mapping->UserMapping,
1327  NULL,
1328  IgnoreCase,
1329  TRUE,
1330  &UserOverlap );
1331 
1332  if (UserOverlap.Ancestor) {
1333 
1334  //
1335  // The file is an ancestor of the user mapping, so disallow setting
1336  // disposition.
1337  //
1338 
1339  Status = STATUS_ACCESS_DENIED;
1340  goto NcPreSetDispositionCleanup;
1341  }
1342 
1343  //
1344  // The file is ok to mark for delete.
1345  //
1346 
1347  Status = STATUS_SUCCESS;
1348  goto NcPreSetDispositionCleanup;
1349 
1350 NcPreSetDispositionCleanup:
1351 
1352  if (!NT_SUCCESS( Status )) {
1353 
1354  ReturnValue = FLT_PREOP_COMPLETE;
1355  Data->IoStatus.Status = Status;
1356 
1357  } else {
1358 
1359  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
1360  }
1361 
1362  if (FileInfo) {
1363 
1364  FltReleaseFileNameInformation( FileInfo );
1365  }
1366 
1367  if (InstanceContext) {
1368 
1369  FltReleaseContext( InstanceContext );
1370  }
1371 
1372  return ReturnValue;
1373 }
1374 
1375 
1376 FLT_PREOP_CALLBACK_STATUS
1378  _Inout_ PFLT_CALLBACK_DATA Data,
1379  _In_ PCFLT_RELATED_OBJECTS FltObjects,
1380  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
1381  )
1382 /*++
1383 
1384 Routine Description:
1385 
1386  Fltmgr callback which manages link creation on a file.
1387  We need to make sure that new links down the user mapping
1388  are redirected to the real mapping.
1389 
1390 Arguments:
1391 
1392  Data - Pointer to the filter CallbackData that is passed to us.
1393 
1394  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
1395  opaque handles to this filter, instance, its associated volume and
1396  file object.
1397 
1398  CompletionContext - The context for the completion routine for this
1399  operation.
1400 
1401 Return Value:
1402 
1403  The return value is the Status of the operation.
1404 
1405 --*/
1406 {
1407  FLT_PREOP_CALLBACK_STATUS ReturnValue;
1408  NTSTATUS Status;
1409  PFILE_LINK_INFORMATION LinkInfo =
1410  Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
1411  PFILE_LINK_INFORMATION MungedLinkInfo = NULL;
1412  ULONG MungedLinkInfoSize;
1413  PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
1414  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
1415  NC_PATH_OVERLAP RealOverlap;
1416  NC_PATH_OVERLAP UserOverlap;
1417  UNICODE_STRING UserRemainder;
1418  UNICODE_STRING MungedName = EMPTY_UNICODE_STRING;
1419  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
1420  FO_OPENED_CASE_SENSITIVE );
1421 
1422  PAGED_CODE();
1423 
1424  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
1425 
1426  UNREFERENCED_PARAMETER( CompletionContext );
1427 
1428  Status = FltGetInstanceContext( FltObjects->Instance,
1429  &InstanceContext);
1430 
1431  if (!NT_SUCCESS( Status )) {
1432 
1433  ReturnValue = FLT_PREOP_COMPLETE;
1434  goto NcPreSetLinkInformationCleanup;
1435  }
1436 
1437  Status = FltGetDestinationFileNameInformation( FltObjects->Instance,
1438  FltObjects->FileObject,
1439  LinkInfo->RootDirectory,
1440  LinkInfo->FileName,
1441  LinkInfo->FileNameLength,
1442  FLT_FILE_NAME_OPENED |
1443  FLT_FILE_NAME_QUERY_DEFAULT |
1444  FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
1445  &FileInfo);
1446 
1447  if (!NT_SUCCESS( Status )) {
1448 
1449  ReturnValue = FLT_PREOP_COMPLETE;
1450  goto NcPreSetLinkInformationCleanup;
1451  }
1452 
1453  Status = FltParseFileNameInformation( FileInfo );
1454 
1455  if (!NT_SUCCESS( Status )) {
1456 
1457  ReturnValue = FLT_PREOP_COMPLETE;
1458  goto NcPreSetLinkInformationCleanup;
1459  }
1460 
1461  //
1462  // Calculate Overlap and Remainder
1463  //
1464 
1465  NcComparePath( &FileInfo->Name,
1466  &InstanceContext->Mapping.UserMapping,
1467  &UserRemainder,
1468  IgnoreCase,
1469  TRUE,
1470  &UserOverlap );
1471 
1472  NcComparePath( &FileInfo->Name,
1473  &InstanceContext->Mapping.RealMapping,
1474  NULL,
1475  IgnoreCase,
1476  TRUE,
1477  &RealOverlap );
1478 
1479  //
1480  // We cannot allow the user to link inside the real mapping, since it
1481  // is hidden.
1482  //
1483 
1484  if (RealOverlap.Match) {
1485 
1486  Status = STATUS_ACCESS_DENIED;
1487  ReturnValue = FLT_PREOP_COMPLETE;
1488  goto NcPreSetLinkInformationCleanup;
1489 
1490  } else if (RealOverlap.InMapping) {
1491 
1492  //
1493  // We should never get here. Getting here requires an
1494  // OPEN_TARGET_DIRECTORY open which should already have failed.
1495  //
1496 
1497  FLT_ASSERT( !RealOverlap.InMapping );
1498 
1499  Status = STATUS_OBJECT_PATH_NOT_FOUND;
1500  ReturnValue = FLT_PREOP_COMPLETE;
1501  goto NcPreSetLinkInformationCleanup;
1502 
1503  } else if ((RealOverlap.Ancestor || UserOverlap.Ancestor) &&
1504  LinkInfo->ReplaceIfExists) {
1505 
1506  //
1507  // The user is attempting to overwrite a parent of the mapping.
1508  // Fail this operation.
1509  //
1510 
1511  Status = STATUS_ACCESS_DENIED;
1512  ReturnValue = FLT_PREOP_COMPLETE;
1513  goto NcPreSetLinkInformationCleanup;
1514  }
1515 
1516  //
1517  // If the destination path is outside the mapping then we can pass it
1518  // through without a problem.
1519  //
1520 
1521  if (!UserOverlap.InMapping) {
1522 
1523  //
1524  // The destination outside the mapping. We can ignore this IO.
1525  //
1526 
1527  Status = STATUS_SUCCESS;
1528  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
1529  goto NcPreSetLinkInformationCleanup;
1530  }
1531 
1532  //
1533  // The destination is inside the mapping. This means we have to issue
1534  // our own request and forward the results to the user.
1535  //
1536 
1537  //
1538  // We need to build a new path to link on.
1539  //
1540 
1541  Status = NcConstructPath( &InstanceContext->Mapping.RealMapping,
1542  &UserRemainder,
1543  TRUE,
1544  &MungedName );
1545 
1546  if (!NT_SUCCESS( Status )) {
1547 
1548  ReturnValue = FLT_PREOP_COMPLETE;
1549  goto NcPreSetLinkInformationCleanup;
1550  }
1551 
1552  //
1553  // Create our own link structure.
1554  //
1555 
1556  MungedLinkInfoSize = sizeof(FILE_LINK_INFORMATION) + MungedName.Length - sizeof(WCHAR);
1557  MungedLinkInfo = ExAllocatePoolWithTag( PagedPool,
1558  MungedLinkInfoSize,
1560 
1561  if (MungedLinkInfo == NULL) {
1562 
1563  Status = STATUS_INSUFFICIENT_RESOURCES;
1564  ReturnValue = FLT_PREOP_COMPLETE;
1565  goto NcPreSetLinkInformationCleanup;
1566  }
1567 
1568  MungedLinkInfo->ReplaceIfExists = LinkInfo->ReplaceIfExists;
1569  MungedLinkInfo->RootDirectory = NULL;
1570  MungedLinkInfo->FileNameLength = MungedName.Length;
1571 
1572  RtlCopyMemory( &MungedLinkInfo->FileName, MungedName.Buffer, MungedName.Length );
1573 
1574  //
1575  // Issue our own request.
1576  //
1577 
1578  Status = FltSetInformationFile( FltObjects->Instance,
1579  FltObjects->FileObject,
1580  MungedLinkInfo,
1581  MungedLinkInfoSize,
1582  FileLinkInformation );
1583 
1584  //
1585  // Because we issued the IO, we will pass complete this ourselves.
1586  //
1587 
1588  ReturnValue = FLT_PREOP_COMPLETE;
1589 
1590 NcPreSetLinkInformationCleanup:
1591 
1592  if (ReturnValue == FLT_PREOP_COMPLETE) {
1593 
1594  Data->IoStatus.Status = Status;
1595  }
1596 
1597  if (MungedLinkInfo != NULL) {
1598 
1599  ExFreePoolWithTag( MungedLinkInfo, NC_SET_LINK_BUFFER_TAG );
1600  }
1601 
1602  if (FileInfo != NULL) {
1603 
1604  FltReleaseFileNameInformation( FileInfo );
1605  }
1606 
1607  if (InstanceContext != NULL) {
1608 
1609  FltReleaseContext( InstanceContext );
1610  }
1611 
1612  if (MungedName.Buffer != NULL) {
1613 
1614  ExFreePoolWithTag( MungedName.Buffer, NC_TAG );
1615  }
1616 
1617  return ReturnValue;
1618 }
1619 
1620 FLT_PREOP_CALLBACK_STATUS
1622  _Inout_ PFLT_CALLBACK_DATA Data,
1623  _In_ PCFLT_RELATED_OBJECTS FltObjects,
1624  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
1625  )
1626 /*++
1627 
1628 Routine Description:
1629 
1630  Fltmgr callback which manages renaming files. We must disallow renaming
1631  on an ancestor of either mapping because otherwise we would have to
1632  maintain the mapping's short/long name pairings.
1633 
1634 Arguments:
1635 
1636  Data - Pointer to the filter CallbackData that is passed to us.
1637 
1638  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
1639  opaque handles to this filter, instance, its associated volume and
1640  file object.
1641 
1642  CompletionContext - The context for the completion routine for this
1643  operation.
1644 
1645 Return Value:
1646 
1647  The return value is the Status of the operation.
1648 
1649 --*/
1650 {
1651  //
1652  // Return Values
1653  //
1654 
1655  NTSTATUS Status;
1656  FLT_PREOP_CALLBACK_STATUS ReturnValue;
1657 
1658  //
1659  // Contexts
1660  //
1661 
1662  PNC_INSTANCE_CONTEXT InstanceContext = NULL;
1663 
1664  //
1665  // Data
1666  //
1667 
1668  PFILE_RENAME_INFORMATION RenameInfo =
1669  Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
1670 
1671  //
1672  // FileInformation
1673  //
1674 
1675  PFLT_FILE_NAME_INFORMATION TargetInfo = NULL;
1676  PFLT_FILE_NAME_INFORMATION SrcInfo = NULL;
1677 
1678  //
1679  // Target Real Overlap
1680  //
1681 
1682  NC_PATH_OVERLAP TargetRealOverlap;
1683  UNICODE_STRING TargetRealRemainder;
1684 
1685  //
1686  // Target User Overlap
1687  //
1688 
1689  NC_PATH_OVERLAP TargetUserOverlap;
1690  UNICODE_STRING TargetUserRemainder;
1691 
1692  //
1693  // Src Real Overlap
1694  //
1695 
1696  NC_PATH_OVERLAP SrcRealOverlap;
1697  NC_PATH_OVERLAP SrcUserOverlap;
1698 
1699  //
1700  // Munge Data
1701  //
1702 
1703  UNICODE_STRING MungedTargetName = EMPTY_UNICODE_STRING;
1704  PFILE_RENAME_INFORMATION MungedRenameInfo = NULL;
1705  ULONG MungedRenameLength;
1706  BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
1707  FO_OPENED_CASE_SENSITIVE );
1708 
1709  FILE_INFORMATION_CLASS fileInformationClass;
1710  BOOLEAN ReplaceIfExists;
1711 
1712  fileInformationClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
1713  ReplaceIfExists = (fileInformationClass == FileRenameInformationEx) ?
1714  BooleanFlagOn( RenameInfo->Flags, FILE_RENAME_REPLACE_IF_EXISTS ) :
1715  RenameInfo->ReplaceIfExists;
1716 
1717 
1718  PAGED_CODE();
1719 
1720  UNREFERENCED_PARAMETER( CompletionContext );
1721 
1722  FLT_ASSERT( IoGetTopLevelIrp() == NULL );
1723 
1724  FLT_ASSERT( (fileInformationClass == FileRenameInformation) ||
1725  (fileInformationClass == FileRenameInformationEx) );
1726 
1727  //
1728  // Get Instance Context
1729  //
1730 
1731  Status = FltGetInstanceContext( FltObjects->Instance,
1732  &InstanceContext );
1733 
1734  if (!NT_SUCCESS( Status )) {
1735 
1736  ReturnValue = FLT_PREOP_COMPLETE;
1737  goto NcPreRenameCleanup;
1738  }
1739 
1740  //
1741  // Find out the src file's name.
1742  //
1743 
1744  Status = NcGetFileNameInformation( Data,
1745  NULL,
1746  NULL,
1747  FLT_FILE_NAME_OPENED |
1748  FLT_FILE_NAME_QUERY_DEFAULT |
1749  FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
1750  &SrcInfo);
1751 
1752  if (!NT_SUCCESS( Status )) {
1753 
1754  ReturnValue = FLT_PREOP_COMPLETE;
1755  goto NcPreRenameCleanup;
1756  }
1757 
1758  Status = FltParseFileNameInformation( SrcInfo );
1759 
1760  if (!NT_SUCCESS( Status )) {
1761 
1762  ReturnValue = FLT_PREOP_COMPLETE;
1763  goto NcPreRenameCleanup;
1764  }
1765 
1766  //
1767  // Find the src's overlap with the real and user mappings.
1768  //
1769 
1770  NcComparePath( &SrcInfo->Name,
1771  &InstanceContext->Mapping.RealMapping,
1772  NULL,
1773  IgnoreCase,
1774  TRUE,
1775  &SrcRealOverlap );
1776 
1777  NcComparePath( &SrcInfo->Name,
1778  &InstanceContext->Mapping.UserMapping,
1779  NULL,
1780  IgnoreCase,
1781  TRUE,
1782  &SrcUserOverlap );
1783 
1784  //
1785  // If the src is an ancestor of either the user or real mappings we can
1786  // fail the request.
1787  //
1788 
1789  if (SrcUserOverlap.Ancestor || SrcRealOverlap.Ancestor) {
1790 
1791  ReturnValue = FLT_PREOP_COMPLETE;
1792  Status = STATUS_ACCESS_DENIED;
1793  goto NcPreRenameCleanup;
1794  }
1795 
1796  //
1797  // Find out the target file's name.
1798  //
1799 
1800  Status = FltGetDestinationFileNameInformation( FltObjects->Instance,
1801  FltObjects->FileObject,
1802  RenameInfo->RootDirectory,
1803  RenameInfo->FileName,
1804  RenameInfo->FileNameLength,
1805  FLT_FILE_NAME_OPENED |
1806  FLT_FILE_NAME_QUERY_DEFAULT |
1807  FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
1808  &TargetInfo);
1809 
1810  if (!NT_SUCCESS( Status )) {
1811 
1812  ReturnValue = FLT_PREOP_COMPLETE;
1813  goto NcPreRenameCleanup;
1814  }
1815 
1816  Status = FltParseFileNameInformation( TargetInfo );
1817 
1818  if( !NT_SUCCESS( Status ) ) {
1819 
1820  FLT_ASSERT( NT_SUCCESS( Status ) );
1821 
1822  ReturnValue = FLT_PREOP_COMPLETE;
1823  goto NcPreRenameCleanup;
1824  }
1825 
1826  //
1827  // Find the target's overlap with the real and user mappings.
1828  //
1829 
1830  NcComparePath( &TargetInfo->Name,
1831  &InstanceContext->Mapping.RealMapping,
1832  &TargetRealRemainder,
1833  IgnoreCase,
1834  TRUE,
1835  &TargetRealOverlap );
1836 
1837  NcComparePath( &TargetInfo->Name,
1838  &InstanceContext->Mapping.UserMapping,
1839  &TargetUserRemainder,
1840  IgnoreCase,
1841  TRUE,
1842  &TargetUserOverlap );
1843 
1844  //
1845  // If the target is in the real mapping, then disallow the rename. If
1846  // the target is to an ancestor of the mappings, this could change IDs
1847  // and is therefore also disallowed.
1848  //
1849 
1850  if (TargetRealOverlap.InMapping) {
1851 
1852  Status = STATUS_ACCESS_DENIED;
1853  ReturnValue = FLT_PREOP_COMPLETE;
1854  goto NcPreRenameCleanup;
1855 
1856  } else if ((TargetRealOverlap.Ancestor || TargetUserOverlap.Ancestor) &&
1857  ReplaceIfExists) {
1858 
1859  Status = STATUS_ACCESS_DENIED;
1860  ReturnValue = FLT_PREOP_COMPLETE;
1861  goto NcPreRenameCleanup;
1862  }
1863 
1864 
1865  //
1866  // If the target is in the user mapping, then we need to munge the
1867  // name and send the request down. If this is a stream rename we
1868  // do not perform the mapping since only the stream name is changing.
1869  //
1870 
1871  if (TargetUserOverlap.InMapping &&
1872  (RenameInfo->FileName[0] != ':')) {
1873 
1874  Status = NcConstructPath( &InstanceContext->Mapping.RealMapping,
1875  &TargetUserRemainder,
1876  TRUE,
1877  &MungedTargetName );
1878 
1879  if (!NT_SUCCESS( Status )) {
1880 
1881  ReturnValue = FLT_PREOP_COMPLETE;
1882  goto NcPreRenameCleanup;
1883  }
1884 
1885 
1886  //
1887  // Because the target is in the user mapping, we have to issue
1888  // our own rename down the real mapping.
1889  //
1890 
1891  //
1892  // Allocate rename information structure.
1893  //
1894 
1895  MungedRenameLength = sizeof(FILE_RENAME_INFORMATION) -
1896  sizeof(WCHAR) +
1897  MungedTargetName.Length;
1898 
1899  MungedRenameInfo = ExAllocatePoolWithTag( PagedPool,
1900  MungedRenameLength,
1902 
1903  if (MungedRenameInfo == NULL) {
1904 
1905  Status = STATUS_INSUFFICIENT_RESOURCES;
1906  ReturnValue = FLT_PREOP_COMPLETE;
1907  goto NcPreRenameCleanup;
1908  }
1909 
1910  //
1911  // Copy user rename parameters.
1912  //
1913 
1914  MungedRenameInfo->Flags = RenameInfo->Flags;
1915  MungedRenameInfo->RootDirectory = NULL;
1916  MungedRenameInfo->FileNameLength = MungedTargetName.Length;
1917  RtlCopyMemory( &MungedRenameInfo->FileName,
1918  MungedTargetName.Buffer,
1919  MungedTargetName.Length );
1920 
1921  //
1922  // Send the request. Note that we cannot just place the new buffer
1923  // in the CallbackData; filesystems use the name from a previous
1924  // OPEN_TARGET_DIRECTORY open, so changing the buffer here would
1925  // result in unexpected (and undefined!) behavior.
1926  //
1927 
1928  Status = FltSetInformationFile( FltObjects->Instance,
1929  FltObjects->FileObject,
1930  MungedRenameInfo,
1931  MungedRenameLength,
1932  fileInformationClass );
1933 
1934  //
1935  // Complete the IO.
1936  //
1937 
1938  ReturnValue = FLT_PREOP_COMPLETE;
1939  goto NcPreRenameCleanup;
1940 
1941  } else {
1942 
1943  //
1944  // The target was outside the mapping. The rename does not have
1945  // to be munged. Pass through.
1946  //
1947 
1948  ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
1949  goto NcPreRenameCleanup;
1950 
1951  }
1952 
1953 NcPreRenameCleanup:
1954 
1955  if (ReturnValue == FLT_PREOP_COMPLETE) {
1956 
1957  Data->IoStatus.Status = Status;
1958  }
1959 
1960  if (InstanceContext != NULL) {
1961 
1962  FltReleaseContext( InstanceContext );
1963  }
1964 
1965  if (TargetInfo != NULL) {
1966 
1967  FltReleaseFileNameInformation( TargetInfo );
1968  }
1969 
1970  if (SrcInfo != NULL) {
1971 
1972  FltReleaseFileNameInformation( SrcInfo );
1973  }
1974 
1975  if (MungedTargetName.Buffer != NULL) {
1976 
1977  ExFreePoolWithTag( MungedTargetName.Buffer, NC_GENERATE_NAME_TAG );
1978  }
1979 
1980  if (MungedRenameInfo != NULL) {
1981 
1982  ExFreePoolWithTag( MungedRenameInfo, NC_RENAME_BUFFER_TAG );
1983  }
1984 
1985  return ReturnValue;
1986 }
1987 
1988 
#define EMPTY_UNICODE_STRING
Definition: nc.h:33
UNICODE_STRING FinalComponentName
Definition: nc.h:159
FLT_PREOP_CALLBACK_STATUS NcPreRename(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
Definition: ncfileinfo.c:1621
NC_MAPPING_ENTRY RealMapping
Definition: nc.h:186
RtlCopyMemory(OutputStringBuffer, TempMappingBuffer->Data, OutputString->MaximumLength)
FLT_PREOP_CALLBACK_STATUS NcPreSetShortName(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
Definition: ncfileinfo.c:999
#define Add2Ptr(P, I)
Definition: minispy.h:238
#define NC_RENAME_BUFFER_TAG
Definition: nc.h:23
NC_MAPPING_PATH ShortNamePath
Definition: nc.h:176
NC_MAPPING_PATH LongNamePath
Definition: nc.h:173
FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject))
return TRUE
#define NC_SET_LINK_BUFFER_TAG
Definition: nc.h:22
UNICODE_STRING VolumelessName
Definition: nc.h:160
NC_MAPPING Mapping
Definition: nc.h:233
FLT_FILESYSTEM_TYPE VolumeFilesystemType
Definition: nc.h:236
NC_MAPPING_ENTRY UserMapping
Definition: nc.h:187
_In_opt_ PFILE_OBJECT _In_opt_ PFLT_INSTANCE _In_ FLT_FILE_NAME_OPTIONS _Outptr_ PFLT_FILE_NAME_INFORMATION * FileNameInformation
Definition: nc.h:493
int Ancestor
Definition: nc.h:217
FLT_POSTOP_CALLBACK_STATUS NcPostQueryHardLinks(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags)
Definition: ncfileinfo.c:581
UNREFERENCED_PARAMETER(FileObject)
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
int InMapping
Definition: nc.h:220
FLT_POSTOP_CALLBACK_STATUS NcPostQueryName(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags)
Definition: ncfileinfo.c:33
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()
IoStatus Status
NC_GLOBAL_DATA NcGlobalData
Definition: nc.c:335
FLT_PREOP_CALLBACK_STATUS NcPreSetDisposition(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
Definition: ncfileinfo.c:1189
#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
int Peer
Definition: nc.h:221
NcLoadRegistryStringCleanup NC_TAG
Definition: ncinit.c:170
#define NC_GENERATE_NAME_TAG
Definition: nc.h:17
PFLT_FILTER FilterHandle
Definition: nc.h:475
FLT_PREOP_CALLBACK_STATUS NcPreSetLinkInformation(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
Definition: ncfileinfo.c:1377
FLT_PREOP_CALLBACK_STATUS NcPreQueryAlternateName(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
Definition: ncfileinfo.c:367

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