WDK Mini Filter Example
ncinit.c
Go to the documentation of this file.
1 
2 #include "nc.h"
3 
4 _At_(OutputString->Buffer, _Post_notnull_)
5 NTSTATUS
6 NcLoadRegistryString (
7  _In_ HANDLE Key,
8  _In_ PCWSTR valueName,
9  _Out_ PUNICODE_STRING OutputString
10  );
11 
12 BOOLEAN
14  _In_ PUNICODE_STRING TestName,
15  _In_opt_ PUNICODE_STRING LongName
16  );
17 
18 #ifdef ALLOC_PRAGMA
19 #pragma alloc_text(INIT, NcInitializeMapping)
20 #pragma alloc_text(INIT, NcLoadRegistryString)
21 #pragma alloc_text(INIT, NcIs8DOT3Compatible)
22 #endif
23 
24 // The #pragma is a notation to the static code analyzer that the Buffer
25 // returned from the function will always be properly initialized.
26 // The multiple allocations/frees of the buffers causes it to lose track.
27 #pragma warning(push)
28 #pragma warning(disable:6001)
29 _At_(OutputString->Buffer, _Post_notnull_)
30 NTSTATUS
31 NcLoadRegistryString (
32  _In_ HANDLE Key,
33  _In_ PCWSTR valueName,
34  _Out_ PUNICODE_STRING OutputString
35  )
36 {
37 #pragma warning(pop)
38  PKEY_VALUE_PARTIAL_INFORMATION TempMappingBuffer = NULL;
40  UNICODE_STRING ValueString;
42  NTSTATUS Status;
43 
44  PAGED_CODE();
45 
46  //
47  // Query the length of the registry value.
48  //
49 
50  RtlInitUnicodeString( &ValueString, valueName );
51 
52 NcLoadRegistryStringRetry:
53  Status = ZwQueryValueKey( Key,
54  &ValueString,
56  NULL,
57  0,
58  &TempMappingKeyLength );
59 
60  //
61  // If we could not successfully locate the value, return the
62  // error to our caller.
63  //
64 
65  if (Status != STATUS_BUFFER_TOO_SMALL &&
66  Status != STATUS_BUFFER_OVERFLOW) {
67 
68  goto NcLoadRegistryStringCleanup;
69  }
70 
71  //
72  // Allocate a buffer large enough to hold the string.
73  //
74 
75  if (TempMappingBuffer != NULL) {
76  ExFreePoolWithTag( TempMappingBuffer, NC_TAG );
77  }
78 
79  TempMappingBuffer = ExAllocatePoolWithTag( PagedPool,
80  TempMappingKeyLength,
81  NC_TAG );
82 
83  if (TempMappingBuffer == NULL) {
84 
85  Status = STATUS_INSUFFICIENT_RESOURCES;
86  goto NcLoadRegistryStringCleanup;
87  }
88 
89  //
90  // Now attempt to read the string.
91  //
92 
93  Status = ZwQueryValueKey( Key,
94  &ValueString,
96  TempMappingBuffer,
97  TempMappingKeyLength,
98  &TempMappingKeyLength );
99 
100  //
101  // If the value is changing underneath us, the length we
102  // collected above may be stale. Loop back, reallocate
103  // and try again.
104  //
105 
106  if (Status == STATUS_BUFFER_TOO_SMALL ||
107  Status == STATUS_BUFFER_OVERFLOW) {
108 
109  goto NcLoadRegistryStringRetry;
110  }
111 
112  if (!NT_SUCCESS( Status )) {
113 
114  goto NcLoadRegistryStringCleanup;
115  }
116 
117  //
118  // If we're reading a string, it had better:
119  // 1. Be a string.
120  // 2. Fit in a UNICODE_STRING.
121  // 3. Have some characters in it (we never need empty strings in this filter.)
122  //
123 
124  if (TempMappingBuffer->Type != REG_SZ ||
125  TempMappingBuffer->DataLength >= MAXUSHORT ||
126  TempMappingBuffer->DataLength <= sizeof(WCHAR)) {
127 
128  Status = STATUS_INVALID_PARAMETER;
129  goto NcLoadRegistryStringCleanup;
130  }
131 
132  //
133  // Allocate a buffer for the target string. Note that we
134  // allocate one fewer WCHAR, as we have no need for the
135  // NULL terminator in our UNICODE_STRING.
136  //
137 
138  OutputStringBuffer = ExAllocatePoolWithTag( NonPagedPool,
139  TempMappingBuffer->DataLength - sizeof(WCHAR),
140  NC_TAG );
141 
142  if (OutputStringBuffer == NULL) {
143 
144  Status = STATUS_INSUFFICIENT_RESOURCES;
145  goto NcLoadRegistryStringCleanup;
146  }
147 
148  //
149  // We only modify the output string on success. On failure, it is
150  // left with previous values.
151  //
152 
153  Status = STATUS_SUCCESS;
154 
155  OutputString->MaximumLength = (USHORT)TempMappingBuffer->DataLength - sizeof(WCHAR);
156  OutputString->Buffer = OutputStringBuffer;
157 
158  RtlCopyMemory( OutputStringBuffer, TempMappingBuffer->Data, OutputString->MaximumLength );
159  OutputString->Length = OutputString->MaximumLength;
160 
161  //
162  // This buffer is in use and should not be cleaned up.
163  //
164 
165  OutputStringBuffer = NULL;
166 
167 NcLoadRegistryStringCleanup:
168 
169  if (TempMappingBuffer != NULL) {
170  ExFreePoolWithTag( TempMappingBuffer, NC_TAG );
171  }
172 
173  if (OutputStringBuffer != NULL) {
174  ExFreePoolWithTag( OutputStringBuffer, NC_TAG );
175  }
176 
177  return Status;
178 }
179 
180 BOOLEAN
182  _In_ PUNICODE_STRING TestName,
183  _In_opt_ PUNICODE_STRING LongName
184  )
185 {
186  BOOLEAN SpacesPresent;
187  USHORT Index;
188  PAGED_CODE();
189 
190  //
191  // When the user supplies a shortname, we expect it to be a valid
192  // shortname. This function will check the name's length.
193  //
194 
195  if (!RtlIsNameLegalDOS8Dot3( TestName,
196  NULL,
197  &SpacesPresent )) {
198 
199  return FALSE;
200 
201  }
202 
203  //
204  // Our shortnames should not have spaces.
205  //
206 
207  if (SpacesPresent) {
208  return FALSE;
209  }
210 
211  //
212  // In this sample, we enforce that the shortname must NOT contain
213  // a tilde (~).
214  //
215  // If the shortname could contain a tilde, the filesystem would
216  // be able to autogenerate a conflicting shortname in response to
217  // an operation on a long name. We could only detect this
218  // afterwards (in a post-operation callback), but we may not be
219  // able to handle the condition correctly. Explicitly changing
220  // a shortname requires NTFS and restore privilege. Rather than
221  // attempt to obtain this functionality via creative mechanism,
222  // this filter simply prevents a shortname which could create
223  // this condition.
224  //
225  // Note that in a product it may be advantageous to create both
226  // sides of the mapping on disk. Part of this sample is to
227  // illustrate the emulation of an object which does not exist,
228  // so we did not do this here.
229  //
230  // We enforce:
231  // 1. The name must not have a tilde (for the above reasons);
232  // 2. The name must not contain a path seperator;
233  // 3. The name must be fully uppercase to be a valid DOS name
234  //
235 
236  for (Index = 0;
237  Index < TestName->Length/sizeof(WCHAR);
238  Index++) {
239 
240  if (TestName->Buffer[Index] == L'~' ||
241  TestName->Buffer[Index] == L'\\' ||
242  TestName->Buffer[Index] != RtlUpcaseUnicodeChar( TestName->Buffer[Index] )) {
243 
244  return FALSE;
245 
246  }
247  }
248 
249  //
250  // We're done validating the short name. Now we must check the
251  // long and short names for consistency. If we have no long name,
252  // we're done.
253  //
254 
255  if (!ARGUMENT_PRESENT( LongName )) {
256  return TRUE;
257  }
258 
259  //
260  // Check if the long name is a valid shortname. We recurse into
261  // ourselves for this. Note that since we're not specifying a
262  // long name, the recursion is bounded.
263  //
264 
265  if (NcIs8DOT3Compatible( LongName, NULL )) {
266 
267  //
268  // If both our long and short paths are compliant, they had
269  // better be the same.
270  //
271 
272  if (!RtlEqualUnicodeString( TestName, LongName, FALSE )) {
273  return FALSE;
274  }
275  }
276 
277  return TRUE;
278 
279 }
280 
281 NTSTATUS
283  _In_ PUNICODE_STRING RegistryPath
284  )
285 /*++
286 
287 Routine Descrition:
288 
289  This routine initializes the mapping structure. It will
290  try to populate it from the registry, and if that fails
291  use a default string.
292 
293 Arguments:
294 
295  RegistryPath - The path key passed to the driver during DriverEntry.
296 
297 Return Value:
298 
299  None.
300 
301 --*/
302 {
303  NTSTATUS Status;
304  OBJECT_ATTRIBUTES Attributes;
305  HANDLE DriverRegKey = NULL;
306  UNICODE_STRING TempPath = EMPTY_UNICODE_STRING;
307  USHORT Index;
308 
309  PAGED_CODE();
310 
311  RtlZeroMemory( &NcGlobalData, sizeof( NcGlobalData ));
312 
313  //
314  // Open the mapping registry key.
315  //
316 
317  InitializeObjectAttributes( &Attributes,
318  RegistryPath,
319  OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
320  NULL,
321  NULL );
322 
323  Status = ZwOpenKey( &DriverRegKey,
324  KEY_READ,
325  &Attributes );
326 
327  if (!NT_SUCCESS( Status )) {
328 
329  FLT_ASSERT( DriverRegKey == NULL );
330  goto NcInitializeMappingCleanup;
331  }
332 
333  Status = NcLoadRegistryString( DriverRegKey,
334  L"UserMapping",
335  &TempPath );
336 
337  if (!NT_SUCCESS( Status )) {
338  goto NcInitializeMappingCleanup;
339  }
340 
341  //
342  // We check that the name does not contain two
343  // contiguous slashes. This implies an empty
344  // name component, which we make no attempt to
345  // handle.
346  //
347 
348  for (Index = 1; Index < TempPath.Length/sizeof(WCHAR); Index++) {
349 
350  if (TempPath.Buffer[Index] == L'\\' &&
351  TempPath.Buffer[Index - 1] == L'\\') {
352 
353  Status = STATUS_INVALID_PARAMETER;
354  goto NcInitializeMappingCleanup;
355 
356  }
357  }
358 
359  Status = NcParseFinalComponent( &TempPath,
362 
363  if (!NT_SUCCESS( Status )) {
364  goto NcInitializeMappingCleanup;
365  }
366 
367  NcFreeUnicodeString( &TempPath );
368 
369  Status = NcLoadRegistryString( DriverRegKey,
370  L"UserMappingFinalComponentShort",
372 
373  if (!NT_SUCCESS( Status )) {
374  goto NcInitializeMappingCleanup;
375  }
376 
377  Status = NcLoadRegistryString( DriverRegKey,
378  L"RealMapping",
379  &TempPath );
380 
381  if (!NT_SUCCESS( Status )) {
382  goto NcInitializeMappingCleanup;
383  }
384 
385  //
386  // We check that the name does not contain two
387  // contiguous slashes. This implies an empty
388  // name component, which we make no attempt to
389  // handle.
390  //
391 
392  for (Index = 1; Index < TempPath.Length/sizeof(WCHAR); Index++) {
393 
394  if (TempPath.Buffer[Index] == L'\\' &&
395  TempPath.Buffer[Index - 1] == L'\\') {
396 
397  Status = STATUS_INVALID_PARAMETER;
398  goto NcInitializeMappingCleanup;
399 
400  }
401  }
402 
403  Status = NcParseFinalComponent( &TempPath,
406 
407  if (!NT_SUCCESS( Status )) {
408  goto NcInitializeMappingCleanup;
409  }
410 
411  //
412  // We expect out parent mappings to start with '\', and we expect
413  // no final component to start with '\'. We have already checked
414  // that the strings contain one WCHAR, but we require two for the
415  // parent mappings, since the first one is '\'.
416  //
417 
418  if (NcGlobalData.RealMappingPath.Length < (2 * sizeof(WCHAR)) ||
419  NcGlobalData.UserMappingPath.Length < (2 * sizeof(WCHAR)) ||
420  NcGlobalData.RealMappingPath.Buffer[0] != L'\\' ||
421  NcGlobalData.UserMappingPath.Buffer[0] != L'\\' ||
422  NcGlobalData.UserMappingFinalComponentShort.Buffer[0] == L'\\' ||
423  NcGlobalData.UserMappingFinalComponentLong.Buffer[0] == L'\\' ||
424  NcGlobalData.RealMappingFinalComponent.Buffer[0] == L'\\') {
425 
426  Status = STATUS_INVALID_PARAMETER;
427  goto NcInitializeMappingCleanup;
428  }
429 
432 
433  Status = STATUS_INVALID_PARAMETER;
434  goto NcInitializeMappingCleanup;
435  }
436 
437  //
438  // TODO: This sample assumes that the real mapping final component
439  // is short. This should not be a required assumption. Note that
440  // since we only have one component for the real mapping (long and
441  // short), we don't need to check the long name for compatibility
442  // with the short name.
443  //
444 
446 
447  Status = STATUS_INVALID_PARAMETER;
448  goto NcInitializeMappingCleanup;
449  }
450 
451 NcInitializeMappingCleanup:
452 
453 
454  if (!NT_SUCCESS( Status )) {
455 
456  if (NcGlobalData.UserMappingPath.Buffer != NULL) {
458  }
459 
462  }
463 
466  }
467 
468  if (NcGlobalData.RealMappingPath.Buffer != NULL) {
470  }
471 
474  }
475  }
476 
477  if (TempPath.Buffer != NULL) {
478  NcFreeUnicodeString( &TempPath );
479  }
480 
481  if (DriverRegKey != NULL) {
482 
483  NTSTATUS BogusStatus;
484 
485  BogusStatus = ZwClose( DriverRegKey );
486  FLT_ASSERT(NT_SUCCESS( BogusStatus ));
487  }
488 
489  return Status;
490 }
491 
#define EMPTY_UNICODE_STRING
Definition: nc.h:33
UNICODE_STRING UserMappingFinalComponentLong
Definition: nc.h:470
UNICODE_STRING UserMappingFinalComponentShort
Definition: nc.h:469
_In_ PCWSTR valueName
Definition: ncinit.c:8
BOOLEAN NcIs8DOT3Compatible(_In_ PUNICODE_STRING TestName, _In_opt_ PUNICODE_STRING LongName)
Definition: ncinit.c:181
RtlCopyMemory(OutputStringBuffer, TempMappingBuffer->Data, OutputString->MaximumLength)
UNICODE_STRING UserMappingPath
Definition: nc.h:468
FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject))
#define NcFreeUnicodeString(UCS)
Definition: nc.h:40
TempMappingBuffer
Definition: ncinit.c:79
return TRUE
UNICODE_STRING ValueString
Definition: ncinit.c:40
NcLoadRegistryStringRetry KeyValuePartialInformation
Definition: ncinit.c:53
NTSTATUS NcParseFinalComponent(_In_ PUNICODE_STRING EntirePath, _Out_ PUNICODE_STRING ParentPath, _Out_ PUNICODE_STRING FinalComponent)
Definition: ncpath.c:572
NTSTATUS NcInitializeMapping(_In_ PUNICODE_STRING RegistryPath)
Definition: ncinit.c:282
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
PWCHAR OutputStringBuffer
Definition: ncinit.c:41
PAGED_CODE()
NC_GLOBAL_DATA NcGlobalData
Definition: nc.c:335
ULONG TempMappingKeyLength
Definition: ncinit.c:39
NcLoadRegistryStringCleanup NC_TAG
Definition: ncinit.c:170
NTSTATUS Status
Definition: ncinit.c:42
_At_(OutputString->Buffer, _Post_notnull_) NTSTATUS NcLoadRegistryString(_In_ HANDLE Key
_In_ PCWSTR _Out_ PUNICODE_STRING OutputString
Definition: ncinit.c:8
UNICODE_STRING RealMappingFinalComponent
Definition: nc.h:473
UNICODE_STRING RealMappingPath
Definition: nc.h:472

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