WDK Mini Filter Example
ncpath.c
Go to the documentation of this file.
1 #include "nc.h"
2 
3 #ifdef ALLOC_PRAGMA
4 #pragma alloc_text(PAGE, NcComparePath)
5 #pragma alloc_text(PAGE, NcConstructPath)
6 #pragma alloc_text(PAGE, NcParseFinalComponent)
7 #endif
8 
9 #define NcIsCharComponentTerminator( C ) \
10  ((C) == L'\\' || (C) == L':')
11 
12 BOOLEAN
14  _In_ PCUNICODE_STRING Name,
15  _In_ PNC_MAPPING_ENTRY Mapping,
16  _Out_opt_ PUNICODE_STRING Remainder,
17  _In_ BOOLEAN IgnoreCase,
18  _In_ BOOLEAN ContainsDevice,
19  _Out_ PNC_PATH_OVERLAP Overlap
20  )
21 /*++
22 
23 Routine Description:
24 
25  Compares a name against a mapping and returns the Overlap.
26 
27 Arguments:
28 
29  Name - The name we are checking.
30 
31  Mapping - The mapping path we are comparing against.
32 
33  Remainder - If the string matches the mapping, then Remainder will be
34  the portion of the name after the mapping to the end of the
35  string.
36 
37  IgnoreCase - Case sensitivity.
38 
39  ContainsDevice - TRUE if the input name is expected to contain a fully
40  qualified path including the device name. FALSE if the
41  path is relative to the root of the volume (excludes
42  device.)
43 
44  Overlap - Pointer to an NC_PATH_OVERLAP structure. Structure members will
45  be set when this routine exits.
46 
47 Return Value:
48 
49  TRUE if the string is in the mapping; Remainder is written to if present.
50  FALSE if the string is not in the mapping; Remainder is not written to.
51 
52  Note that this is not a success code; this routine always succeeds. Further
53  information about the mapping is in the Overlap parameter.
54 
55 --*/
56 {
57  PUNICODE_STRING LongName;
58  PUNICODE_STRING ShortName;
59 
60  PWSTR NameBuff;
61  PWSTR LongBuff;
62  PWSTR ShortBuff;
63 
64  WCHAR NameBuffCur;
65  WCHAR LongBuffCur;
66  WCHAR ShortBuffCur;
67 
68  USHORT NameIndex; // Our index in Name
69  USHORT NameMatchIndex = 0; // Our index of matched components in Name
70  USHORT LongIndex; // Our index in MappingPath->LongNamePath
71  USHORT ShortIndex; // Our index in MappingPath->ShortNamePath
72 
73  BOOLEAN LongMatch; // TRUE if long name component matches
74  BOOLEAN ShortMatch; // TRUE if short name component matches
75 
76  USHORT NameLength; // Max index for NameIndex
77  USHORT LongLength;
78  USHORT ShortLength;
79 
80  BOOLEAN NameDone; // TRUE when we hit a '\',stop comparing for this component.
81  BOOLEAN LongDone;
82  BOOLEAN ShortDone;
83 
84  USHORT ComponentMatches = 0; // Number of component matches.
85  USHORT MappingComponents;
86  USHORT VolumeComponents;
87 
88  PAGED_CODE();
89 
90  if (ContainsDevice) {
91  LongName = &Mapping->LongNamePath.FullPath;
92  ShortName = &Mapping->ShortNamePath.FullPath;
93 
94  MappingComponents = Mapping->LongNamePath.NumberComponentsInFullPath;
95  VolumeComponents = Mapping->LongNamePath.NumberComponentsInVolumePath;
96  } else {
97  LongName = &Mapping->LongNamePath.VolumelessName;
98  ShortName = &Mapping->ShortNamePath.VolumelessName;
99 
100  MappingComponents = Mapping->LongNamePath.NumberComponentsInFullPath - Mapping->LongNamePath.NumberComponentsInVolumePath;
101  VolumeComponents = 0;
102  }
103 
104  NameBuff = Name->Buffer;
105  LongBuff = LongName->Buffer;
106  ShortBuff = ShortName->Buffer;
107 
108  NameLength = Name->Length/sizeof(WCHAR); // Max index for NameIndex
109  LongLength = LongName->Length/sizeof(WCHAR);
110  ShortLength = ShortName->Length/sizeof(WCHAR);
111 
112  //
113  // Names are Unicode strings, so the number of bytes in
114  // the length should always be even.
115  //
116 
117  FLT_ASSERT( Name->Length % sizeof(WCHAR) == 0 );
118  FLT_ASSERT( LongName->Length % sizeof(WCHAR) == 0 );
119  FLT_ASSERT( ShortName->Length % sizeof(WCHAR) == 0 );
120  FLT_ASSERT( Mapping->LongNamePath.NumberComponentsInFullPath == Mapping->ShortNamePath.NumberComponentsInFullPath );
121 
122  // Set initial values
123  NameIndex = 0;
124  LongIndex = 0;
125  ShortIndex = 0;
126  NameMatchIndex = 0;
127 
128  do // Loop for component scan
129  {
130 
131  //
132  // Mark that we have not reached the end of the component.
133  // Once we reach end of component, we stop going forward until everyone has caught up.
134  //
135 
136  NameDone = FALSE;
137  LongDone = FALSE;
138  ShortDone = FALSE;
139 
140  //
141  // Mark that the names match
142  // We assume they match until we prove otherwise.
143  //
144 
145  LongMatch = TRUE;
146  ShortMatch = TRUE;
147 
148  do // Loop for character scan.
149  {
150 
151  //
152  // See if we have reached the end of the component.
153  //
154 
155  NameDone = (BOOLEAN)(NameIndex >= NameLength || NcIsCharComponentTerminator( NameBuff[NameIndex] ));
156  LongDone = (BOOLEAN)(LongIndex >= LongLength || LongBuff[LongIndex] == '\\');
157  ShortDone = (BOOLEAN)(ShortIndex >= ShortLength || ShortBuff[ShortIndex] == '\\');
158 
159  if (NameDone) {
160 
161  //
162  // If name has run off end, or hit end of component,
163  // then we need to break.
164  //
165 
166  break;
167  }
168 
169  if(LongDone && ShortDone) {
170 
171  //
172  // If the paths we are comparing against have
173  // both ether run out or hit the end of their
174  // respective components, then we need to break.
175  //
176 
177  break;
178  }
179 
180  //
181  // Convert characters into case insensitive mode (if needed)
182  //
183 
184  if (IgnoreCase) {
185 
186  NameBuffCur = RtlUpcaseUnicodeChar( NameBuff[NameIndex] );
187  if (!LongDone) {
188  LongBuffCur = RtlUpcaseUnicodeChar( LongBuff[LongIndex] );
189  }
190 
191  if (!ShortDone) {
192  ShortBuffCur = RtlUpcaseUnicodeChar( ShortBuff[ShortIndex] );
193  }
194 
195  } else {
196 
197  NameBuffCur = NameBuff[NameIndex];
198  if (!LongDone) {
199  LongBuffCur = LongBuff[LongIndex];
200  }
201 
202  if (!ShortDone) {
203  ShortBuffCur = ShortBuff[ShortIndex];
204  }
205  }
206 
207  //
208  // Compare characters to verify match.
209  //
210 
211  if (!LongDone) {
212  if (NameBuffCur != LongBuffCur) {
213  LongMatch = FALSE; // If the characters are not matches, then we are not a match.
214  }
215  } else {
216  LongMatch = FALSE; // If we're comparing beyond the end of the buffer, we don't match.
217  }
218 
219  if (!ShortDone) {
220  if (NameBuffCur != ShortBuffCur) {
221  ShortMatch = FALSE; // If the characters are not matches, then we are not a match.
222  }
223  } else {
224  ShortMatch = FALSE;
225  }
226 
227  //
228  // Move to next index
229  //
230 
231  NameIndex++;
232  if (!LongDone) {
233  LongIndex++;
234  }
235  if (!ShortDone) {
236  ShortIndex++;
237  }
238 
239  //
240  // Loop if there is still chance of match
241  //
242 
243  } while(LongMatch || ShortMatch);// end of character scan.
244 
245  //
246  // We scanned until we had 2 mismatches or we ran off the end of the name,
247  // or we ran off the end of the long and short names.
248  // We should scan everyone forward until they are all off the end of their
249  // buffer, or it the end of their component.
250  //
251 
252  if (NameIndex < NameLength && !NcIsCharComponentTerminator( NameBuff[NameIndex] )) {
253 
254  //
255  // We broke out of character comparison, but were not at the end
256  // of the name's component. This means that both the long and short
257  // names were shorter than the name's component. Thus they cannot
258  // be matches. Furthermore, because neither of them are matches, we
259  // can stop the search.
260  //
261 
262  LongMatch = FALSE;
263  ShortMatch = FALSE;
264  break;
265  }
266 
267  while (NameIndex < NameLength && NameBuff[NameIndex] != '\\') {
268 
269  //
270  // We may still consider this component a match due to a stream.
271  // In this case, we need to advance the name index until we
272  // find the next slash.
273  //
274  // TODO: Note one effect of this is we will believe
275  // \dir1\foo matches \dir1:Stream\foo. The latter name is not a
276  // valid name on any Microsoft filesystem.
277  //
278 
279  NameIndex++;
280  }
281 
282  while( LongIndex < LongLength && LongBuff[LongIndex] != '\\' ) {
283 
284  //
285  // We were not at the end of the long name's component.
286  // So it cannot be a match.
287  // Scan forward until we find the end.
288  //
289 
290  LongMatch = FALSE;
291  LongIndex++;
292  }
293 
294  while( ShortIndex < ShortLength && ShortBuff[ShortIndex] != '\\' ) {
295 
296  //
297  // We were not at the end of the short name's component.
298  // So it cannot be a match.
299  // Scan forward until we find the end.
300  //
301 
302  ShortMatch = FALSE;
303  ShortIndex++;
304  }
305 
306  if ((LongMatch || ShortMatch) && NameIndex != 0) {
307  ComponentMatches++;
308  }
309 
310  //
311  // All the indexes should be at the end of their buffer or their component.
312  //
313 
314  FLT_ASSERT( NameIndex == NameLength || NameBuff[NameIndex] == '\\' );
315  FLT_ASSERT( LongIndex == LongLength || LongBuff[LongIndex] == '\\' );
316  FLT_ASSERT( ShortIndex == ShortLength || ShortBuff[ShortIndex] == '\\' );
317 
318  //
319  // Since we are all lined up on '\', lets move forward to next component...
320  //
321 
322  if (NameIndex < NameLength) {
323  NameIndex++;
324  }
325 
326  if (LongIndex < LongLength) {
327  LongIndex++;
328  }
329 
330  if (ShortIndex < ShortLength) {
331  ShortIndex++;
332  }
333 
334  if (LongMatch || ShortMatch) {
335  NameMatchIndex = NameIndex;
336  }
337 
338  //
339  // Keep looping if name's last component matched either
340  // the long or short's component and name and the mapping
341  // have more components.
342  //
343  // NOTE: if LongIndex < LongLength,
344  // then ShortIndex < ShortLength because they have the same number of components.
345  //
346 
347  FLT_ASSERT( (LongIndex < LongLength) ? (ShortIndex < ShortLength) : TRUE );
348 
349  } while( (LongMatch || ShortMatch) && NameIndex < NameLength && LongIndex < LongLength );
350 
351  //
352  // Now we need to figure out how far we made it, and
353  // apply the appropriate flags.
354  //
355 
356  Overlap->EntireFlags = 0;
357 
358  //
359  // We can't match more components than exist or something's seriously wrong.
360  //
361  FLT_ASSERT( ComponentMatches <= MappingComponents );
362 
363  if (ComponentMatches >= MappingComponents &&
364  NameLength > NameMatchIndex) {
365 
366  //
367  // If we've matched all the components in the mapping and still have data
368  // left over, we're in the mapping.
369  //
370 
371  Overlap->InMapping = TRUE;
372 
373  if (Remainder != NULL) {
374 
375  Remainder->Buffer = &NameBuff[NameMatchIndex];
376  Remainder->Length = (NameLength - NameMatchIndex) * sizeof(WCHAR);
377  Remainder->MaximumLength = Remainder->Length;
378  }
379 
380  } else if (ComponentMatches == MappingComponents) {
381 
382  //
383  // If we matched all the components in the mapping and have nothing
384  // left over, we are the mapping.
385  //
386 
387  Overlap->Match = TRUE;
388  Overlap->InMapping = TRUE;
389 
390  if (Remainder != NULL) {
391 
392  Remainder->Buffer = &NameBuff[NameIndex];
393  Remainder->MaximumLength = Remainder->Length = 0;
394  }
395 
396  } else if (ComponentMatches == MappingComponents - 1 &&
397  NameLength > NameMatchIndex) {
398 
399  //
400  // If we matched everything except the final component but have data
401  // left over, we may be a peer of the mapping. For this to be true,
402  // the only string left must be a filename, not a path.
403  //
404 
405  Overlap->Peer = TRUE;
406 
407  for (;NameIndex < NameLength;NameIndex++) {
408  if (NameBuff[NameIndex] == L'\\') {
409  Overlap->Peer = FALSE;
410  break;
411  }
412  }
413 
414 
415  } else if (ComponentMatches == MappingComponents - 1) {
416 
417  //
418  // If we matched everything except the final component, then we must
419  // be the parent.
420  //
421 
422  Overlap->Parent = TRUE;
423  Overlap->Ancestor = TRUE;
424 
425  } else if (ComponentMatches >= VolumeComponents &&
426  NameLength == NameMatchIndex) {
427 
428  //
429  // If we matched something and have no data left over, then we are
430  // an ancestor path.
431  //
432 
433  Overlap->Ancestor = TRUE;
434  }
435 
436  return (BOOLEAN)Overlap->InMapping;
437 }
438 
439 _Post_satisfies_(NewName->MaximumLength < MAXUSHORT)
440 _Post_satisfies_(NewName->Length <= NewName->MaximumLength)
441 _Must_inspect_result_
442 NTSTATUS
443 NcConstructPath (
444  _In_ PNC_MAPPING_ENTRY RealPath,
445  _In_ PUNICODE_STRING Remainder,
446  _In_ BOOLEAN IncludeVolume,
447  _Out_ _At_(NewName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING NewName
448  )
449 /*++
450 
451 Routine Description:
452 
453  Constructs a path
454 
455 Arguments:
456 
457  RealPath - Path from the mapping we want to use in name generation.
458 
459  Remainder - Remainder string generated by NcComparePath.
460 
461  IncludeVolume - If TRUE, the volume will be prepended to the name.
462 
463  NewName - Output name.
464 
465 Return Value:
466 
467  STATUS_SUCCESS or an appropriate error code.
468 
469 --*/
470 {
471  NTSTATUS Status = STATUS_SUCCESS;
472  USHORT NameLength;
473  PWCHAR NameBuffer;
474  USHORT SeparatorLength;
475 
476  PAGED_CODE();
477 
478  //
479  // Calculate length of combined name.
480  //
481 
482  SeparatorLength = (Remainder->Length == 0 ? 0 : sizeof(WCHAR));
483  NameLength = RealPath->LongNamePath.VolumelessName.Length + SeparatorLength + Remainder->Length;
484  if( IncludeVolume ) {
485 
486  NameLength = NameLength + RealPath->LongNamePath.VolumePath.Length;
487  }
488 
489  //
490  // Potentially a file may exist on disk which is less than MAXUSHORT,
491  // but our mapping changes the length such that it now exceeds
492  // MAXUSHORT. We can't really handle this object meaningfully, since
493  // all path operations have a UNICODE_STRING limitation. Accordingly,
494  // some files may be inaccessible for some purposes as a result of
495  // this. In practice it won't happen, and the important thing is to
496  // ensure we don't overflow and create security vulnerabilities.
497  //
498  if (NameLength >= MAXUSHORT) {
499 
500  Status = STATUS_OBJECT_PATH_INVALID;
501  goto NcConstructPathCleanup;
502  }
503 
504  //
505  // Allocate space for combined name.
506  //
507 
508  NameBuffer = ExAllocatePoolWithTag( PagedPool,
509  NameLength,
511 
512  if (NameBuffer == NULL) {
513 
514  Status = STATUS_INSUFFICIENT_RESOURCES;
515  goto NcConstructPathCleanup;
516  }
517 
518  //
519  // Zero out destination
520  //
521 
522  NewName->Buffer = NameBuffer;
523  NewName->Length = 0;
524  NewName->MaximumLength = NameLength;
525 
526  //
527  // Copy Volume Path
528  //
529 
530  if (IncludeVolume) {
531 
532  Status = RtlAppendUnicodeStringToString( NewName, &RealPath->LongNamePath.VolumePath );
533 
534  FLT_ASSERT( Status == STATUS_SUCCESS );
535  }
536 
537  //
538  // Copy mapping path.
539  //
540 
541  Status = RtlAppendUnicodeStringToString( NewName,
542  &RealPath->LongNamePath.VolumelessName );
543 
544  FLT_ASSERT( Status == STATUS_SUCCESS );
545 
546  //
547  // Append separator
548  //
549 
550  if (SeparatorLength > 0) {
551 
552  NewName->Buffer[NewName->Length/sizeof(WCHAR)] = NC_SEPARATOR;
553  NewName->Length += sizeof(WCHAR);
554 
555  FLT_ASSERT( NewName->Length <= NewName->MaximumLength );
556  }
557 
558  //
559  // Append Remainder.
560  //
561 
562  Status = RtlAppendUnicodeStringToString( NewName, Remainder );
563 
564  FLT_ASSERT( Status == STATUS_SUCCESS );
565 
566 NcConstructPathCleanup:
567 
568  return Status;
569 }
570 
571 NTSTATUS
573  _In_ PUNICODE_STRING EntirePath,
574  _Out_ PUNICODE_STRING ParentPath,
575  _Out_ PUNICODE_STRING FinalComponent
576  )
577 {
578  USHORT Index = EntirePath->Length / sizeof(WCHAR);
579  USHORT ParentLength;
580  USHORT FinalComponentLength;
581  BOOLEAN FoundFinalComponent = FALSE;
582  NTSTATUS Status = STATUS_SUCCESS;
583  PWSTR ParentStringBuffer = NULL;
584  PWSTR FinalStringBuffer = NULL;
585 
586  PAGED_CODE();
587 
588  while(Index > 0) {
589  Index--;
590  if (EntirePath->Buffer[Index] == L'\\') {
591  FoundFinalComponent = TRUE;
592  break;
593  }
594  }
595 
596  //
597  // Paths should contain at least one seperator.
598  // We expect all paths to be absolute, relative to
599  // the volume, so they should always start with '\'.
600  //
601  if (!FoundFinalComponent) {
602  return STATUS_INVALID_PARAMETER;
603  }
604 
605  //
606  // Typically we don't want to include a trailing
607  // slash in the parent path. The exception to this
608  // rule is for the volume root.
609  //
610  if (Index > 1) {
611  ParentLength = Index * sizeof(WCHAR);
612  } else {
613  ParentLength = (Index + 1) * sizeof(WCHAR);
614  }
615 
616  //
617  // We also don't want to have no final component.
618  // This implies a user configured the path as
619  // \a\b\c\, including the trailing slash.
620  //
621  FinalComponentLength = EntirePath->Length - (Index + 1) * sizeof(WCHAR);
622  if (FinalComponentLength == 0) {
623  return STATUS_INVALID_PARAMETER;
624  }
625 
626  ParentStringBuffer = ExAllocatePoolWithTag( NonPagedPool,
627  ParentLength,
628  NC_TAG );
629 
630  if (ParentStringBuffer == NULL) {
631  Status = STATUS_INSUFFICIENT_RESOURCES;
632  goto NcParseFinalComponentCleanup;
633  }
634 
635  FinalStringBuffer = ExAllocatePoolWithTag( NonPagedPool,
636  FinalComponentLength,
637  NC_TAG );
638 
639  if (FinalStringBuffer == NULL) {
640  Status = STATUS_INSUFFICIENT_RESOURCES;
641  goto NcParseFinalComponentCleanup;
642  }
643 
644  RtlCopyMemory( ParentStringBuffer, EntirePath->Buffer, ParentLength );
645 
646  ParentPath->Buffer = ParentStringBuffer;
647  ParentPath->MaximumLength =
648  ParentPath->Length = ParentLength;
649 
650  RtlCopyMemory( FinalStringBuffer, &EntirePath->Buffer[Index + 1], FinalComponentLength );
651 
652  FinalComponent->Buffer = FinalStringBuffer;
653  FinalComponent->MaximumLength =
654  FinalComponent->Length = FinalComponentLength;
655 
656  //
657  // We've completed successfully, and our allocated
658  // buffers are in use.
659  //
660  ParentStringBuffer = NULL;
661  FinalStringBuffer = NULL;
662 
663 NcParseFinalComponentCleanup:
664 
665  if (ParentStringBuffer) {
666  ExFreePoolWithTag( ParentStringBuffer, NC_TAG );
667  }
668 
669  if (FinalStringBuffer) {
670  ExFreePoolWithTag( FinalStringBuffer, NC_TAG );
671  }
672 
673  return Status;
674 }
RtlCopyMemory(OutputStringBuffer, TempMappingBuffer->Data, OutputString->MaximumLength)
#define NC_SEPARATOR
Definition: nc.h:32
FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject))
return TRUE
_At_(String->Length, _Out_range_(==, 0)) _At_(String -> MaximumLength, _In_) _At_(String->Buffer, _Pre_maybenull_ _Post_notnull_ _Post_writable_byte_size_(String->MaximumLength)) NTSTATUS CtxAllocateUnicodeString(_Out_ PUNICODE_STRING String)
Definition: ctx/support.c:84
#define NcIsCharComponentTerminator(C)
Definition: ncpath.c:9
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
_In_ BOOLEAN _Out_ PFILE_BASIC_INFORMATION Buffer
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
PAGED_CODE()
IoStatus Status
_Post_satisfies_()
Definition: ncpath.c:439
NTSTATUS NcParseFinalComponent(_In_ PUNICODE_STRING EntirePath, _Out_ PUNICODE_STRING ParentPath, _Out_ PUNICODE_STRING FinalComponent)
Definition: ncpath.c:572
NcLoadRegistryStringCleanup NC_TAG
Definition: ncinit.c:170
#define NC_GENERATE_NAME_TAG
Definition: nc.h:17

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