xref: /reactos/ntoskrnl/fsrtl/unc.c (revision 845faec4)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/fsrtl/unc.c
5  * PURPOSE:         Manages UNC support routines for file system drivers.
6  * PROGRAMMERS:     Pierre Schweitzer (pierre@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 KSEMAPHORE FsRtlpUncSemaphore;
16 
17 ULONG FsRtlpRedirs = 0;
18 
19 struct
20 {
21     HANDLE MupHandle;
22     HANDLE NullHandle;
23     UNICODE_STRING RedirectorDeviceName;
24     BOOLEAN MailslotsSupported;
25 } FsRtlpDRD;
26 
27 BOOLEAN
28 FsRtlpIsDfsEnabled(VOID)
29 {
30     HANDLE Key;
31     ULONG Length;
32     NTSTATUS Status;
33     UNICODE_STRING KeyName;
34     OBJECT_ATTRIBUTES ObjectAttributes;
35     struct
36     {
37         KEY_VALUE_PARTIAL_INFORMATION KeyInfo;
38         ULONG KeyValue;
39     } KeyQueryOutput;
40 
41     /* You recognize MuppIsDfsEnabled()! Congratz :-) */
42     KeyName.Buffer = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup";
43     KeyName.Length = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup") - sizeof(UNICODE_NULL);
44     KeyName.MaximumLength = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup");
45 
46     /* Simply query registry to get whether DFS is disabled.
47      * If DFS isn't disabled from registry side, assume it is enabled
48      * and go through MUP.
49      * MUP itself might disable it, but that's not our concern
50      * any longer
51      */
52     InitializeObjectAttributes(&ObjectAttributes,
53                                &KeyName,
54                                OBJ_CASE_INSENSITIVE,
55                                NULL,
56                                NULL);
57     Status = ZwOpenKey(&Key, KEY_READ, &ObjectAttributes);
58     if (!NT_SUCCESS(Status))
59     {
60         return TRUE;
61     }
62 
63     KeyName.Buffer = L"DisableDfs";
64     KeyName.Length = sizeof(L"DisableDfs") - sizeof(UNICODE_NULL);
65     KeyName.MaximumLength = sizeof(L"DisableDfs");
66 
67     Status = ZwQueryValueKey(Key, &KeyName, KeyValuePartialInformation, &KeyQueryOutput, sizeof(KeyQueryOutput), &Length);
68     ZwClose(Key);
69     if (!NT_SUCCESS(Status) || KeyQueryOutput.KeyInfo.Type != REG_DWORD)
70     {
71         return TRUE;
72     }
73 
74     return ((*(PULONG)KeyQueryOutput.KeyInfo.Data) != 1);
75 }
76 
77 NTSTATUS
78 FsRtlpOpenDev(OUT PHANDLE DeviceHandle,
79               IN PCWSTR DeviceName)
80 {
81     NTSTATUS Status;
82     UNICODE_STRING StrDeviceName;
83     IO_STATUS_BLOCK IoStatusBlock;
84     OBJECT_ATTRIBUTES ObjectAttributes;
85 
86     PAGED_CODE();
87 
88     /* Just open the device and return the obtained handle */
89     RtlInitUnicodeString(&StrDeviceName, DeviceName);
90     InitializeObjectAttributes(&ObjectAttributes,
91                                &StrDeviceName,
92                                0,
93                                NULL,
94                                NULL);
95     Status = ZwCreateFile(DeviceHandle,
96                           GENERIC_WRITE,
97                           &ObjectAttributes,
98                           &IoStatusBlock,
99                           NULL,
100                           FILE_ATTRIBUTE_NORMAL,
101                           FILE_SHARE_READ | FILE_SHARE_WRITE,
102                           FILE_OPEN, 0, NULL, 0);
103     if (NT_SUCCESS(Status))
104     {
105         Status = IoStatusBlock.Status;
106     }
107 
108     if (!NT_SUCCESS(Status))
109     {
110         *DeviceHandle = INVALID_HANDLE_VALUE;
111     }
112 
113     return Status;
114 }
115 
116 VOID
117 FsRtlpSetSymbolicLink(IN PCUNICODE_STRING DeviceName)
118 {
119     NTSTATUS Status;
120     UNICODE_STRING UncDevice;
121 
122     PAGED_CODE();
123 
124     /* Delete the old link, and set the new one if we have a name */
125     RtlInitUnicodeString(&UncDevice, L"\\DosDevices\\UNC");
126     IoDeleteSymbolicLink(&UncDevice);
127     if (DeviceName != NULL)
128     {
129         Status = IoCreateSymbolicLink(&UncDevice, (PUNICODE_STRING)DeviceName);
130         ASSERT(NT_SUCCESS(Status));
131     }
132 }
133 
134 NTSTATUS
135 FsRtlpRegisterProviderWithMUP(IN HANDLE MupHandle,
136                               IN PCUNICODE_STRING RedirectorDeviceName,
137                               IN BOOLEAN MailslotsSupported)
138 {
139     NTSTATUS Status;
140     ULONG BufferSize;
141     IO_STATUS_BLOCK IoStatusBlock;
142     PMUP_PROVIDER_REGISTRATION_INFO RegistrationInfo;
143 
144     PAGED_CODE();
145 
146     DPRINT1("FsRtlpRegisterProviderWithMUP(%p, %wZ, %u)\n", (PVOID)MupHandle, RedirectorDeviceName, MailslotsSupported);
147 
148     /* We have to be able to store the name and the registration information */
149     BufferSize = RedirectorDeviceName->Length + sizeof(MUP_PROVIDER_REGISTRATION_INFO);
150     RegistrationInfo = ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG_UNC);
151     if (RegistrationInfo == NULL)
152     {
153         return STATUS_INSUFFICIENT_RESOURCES;
154     }
155 
156     /* Set the information about the provider (including its name) */
157     RegistrationInfo->RedirectorDeviceNameOffset = sizeof(MUP_PROVIDER_REGISTRATION_INFO);
158     RegistrationInfo->RedirectorDeviceNameLength = RedirectorDeviceName->Length;
159     RegistrationInfo->MailslotsSupported = MailslotsSupported;
160     RtlCopyMemory((PWSTR)((ULONG_PTR)RegistrationInfo + RegistrationInfo->RedirectorDeviceNameOffset),
161                   RedirectorDeviceName->Buffer, RedirectorDeviceName->Length);
162 
163     /* Call MUP with the registration FSCTL */
164     Status = NtFsControlFile(MupHandle, NULL, NULL, NULL,
165                              &IoStatusBlock, FSCTL_MUP_REGISTER_PROVIDER,
166                              RegistrationInfo, BufferSize, NULL, 0);
167     if (Status == STATUS_PENDING)
168     {
169         Status = NtWaitForSingleObject(MupHandle, TRUE, NULL);
170     }
171 
172     if (NT_SUCCESS(Status))
173     {
174         Status = IoStatusBlock.Status;
175     }
176 
177     /* And we're done! */
178     ASSERT(NT_SUCCESS(Status));
179     ExFreePoolWithTag(RegistrationInfo, TAG_UNC);
180 
181     return Status;
182 }
183 
184 /* PUBLIC FUNCTIONS **********************************************************/
185 
186 /*++
187  * @name FsRtlDeregisterUncProvider
188  * @implemented
189  *
190  * FILLME
191  *
192  * @param Handle
193  *        FILLME
194  *
195  * @return None
196  *
197  * @remarks None
198  *
199  *--*/
200 VOID
201 NTAPI
202 FsRtlDeregisterUncProvider(IN HANDLE Handle)
203 {
204     PAGED_CODE();
205 
206     /* We won't work on invalid input */
207     if (Handle == INVALID_HANDLE_VALUE || Handle == 0)
208     {
209         return;
210     }
211 
212     KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL);
213 
214     /* Sanity check: we need to have providers */
215     ASSERT(FsRtlpRedirs > 0);
216 
217     /* At that point, we had only one provider at a time */
218     if (Handle == (HANDLE)FsRtlpDRD.NullHandle)
219     {
220         /* Free its name if possible (it might have been overtaken in case of
221          * registration of other UNC provider */
222         if (FsRtlpDRD.RedirectorDeviceName.Buffer != NULL)
223         {
224             ExFreePoolWithTag(FsRtlpDRD.RedirectorDeviceName.Buffer, TAG_UNC);
225             FsRtlpDRD.RedirectorDeviceName.Buffer = NULL;
226         }
227 
228         /* Close the handle to MUP */
229         if (FsRtlpDRD.MupHandle != INVALID_HANDLE_VALUE)
230         {
231             ZwClose(FsRtlpDRD.MupHandle);
232             FsRtlpDRD.MupHandle = INVALID_HANDLE_VALUE;
233         }
234 
235         /* Last handle isn't required anymore */
236         FsRtlpDRD.NullHandle = INVALID_HANDLE_VALUE;
237     }
238 
239     /* One less provider */
240     --FsRtlpRedirs;
241 
242     /* In case we reach no provider anylonger, reset the symbolic link */
243     if (FsRtlpRedirs == 0)
244     {
245         FsRtlpSetSymbolicLink(NULL);
246     }
247 
248     KeReleaseSemaphore(&FsRtlpUncSemaphore, IO_NO_INCREMENT, 1, FALSE);
249 
250     /* Final note:
251      * NULL device handle and 'normal' MUP device handle are not closed by
252      * FsRtl. It's up to the user to close them afterwards.
253      * If the handle is leaked, MUP will never be notified about the
254      * unregistration.
255      */
256 }
257 
258 /*++
259  * @name FsRtlRegisterUncProvider
260  * @implemented
261  *
262  * FILLME
263  *
264  * @param Handle
265  *        FILLME
266  *
267  * @param RedirectorDeviceName
268  *        FILLME
269  *
270  * @param MailslotsSupported
271  *        FILLME
272  *
273  * @return None
274  *
275  * @remarks None
276  *
277  *--*/
278 NTSTATUS
279 NTAPI
280 FsRtlRegisterUncProvider(OUT PHANDLE Handle,
281                          IN PCUNICODE_STRING RedirectorDeviceName,
282                          IN BOOLEAN MailslotsSupported)
283 {
284     NTSTATUS Status;
285     HANDLE DeviceHandle;
286     UNICODE_STRING MupString;
287 
288     PAGED_CODE();
289 
290     DPRINT1("FsRtlRegisterUncProvider(%p, %wZ, %u)\n", Handle, RedirectorDeviceName, MailslotsSupported);
291 
292     KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL);
293 
294     /* In case no provider was registered yet, check for DFS present.
295      * If DFS is present, we need to go with MUP, whatever the case
296      */
297     if (FsRtlpRedirs == 0)
298     {
299         if (FsRtlpIsDfsEnabled())
300         {
301             DPRINT1("DFS is not disabled. Going through MUP\n");
302 
303             /* We've to go with MUP, make sure our internal structure doesn't
304              * contain any leftover data and raise redirs to one, to make sure
305              * we use MUP.
306              */
307             RtlZeroMemory(&FsRtlpDRD, sizeof(FsRtlpDRD));
308             FsRtlpRedirs = 1;
309         }
310     }
311 
312     /* In case no UNC provider was already registered,
313      * We'll proceed without MUP and directly redirect
314      * UNC to the provider.
315      */
316     if (FsRtlpRedirs == 0)
317     {
318         /* As we don't provide MUP, just give a handle to NULL device */
319         Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Null");
320         if (!NT_SUCCESS(Status))
321         {
322             goto Cleanup;
323         }
324 
325         /* Allocate a buffer big enough to keep a local copy of UNC provider device */
326         FsRtlpDRD.RedirectorDeviceName.Buffer = ExAllocatePoolWithTag(NonPagedPool, RedirectorDeviceName->MaximumLength, TAG_UNC);
327         if (FsRtlpDRD.RedirectorDeviceName.Buffer == NULL)
328         {
329             Status = STATUS_INSUFFICIENT_RESOURCES;
330             goto Cleanup;
331         }
332 
333         FsRtlpDRD.RedirectorDeviceName.Length = RedirectorDeviceName->Length;
334         FsRtlpDRD.RedirectorDeviceName.MaximumLength = RedirectorDeviceName->MaximumLength;
335         RtlCopyMemory(FsRtlpDRD.RedirectorDeviceName.Buffer, RedirectorDeviceName->Buffer, RedirectorDeviceName->MaximumLength);
336 
337         /* We don't have MUP, and copy provider information */
338         FsRtlpDRD.MupHandle = INVALID_HANDLE_VALUE;
339         FsRtlpDRD.MailslotsSupported = MailslotsSupported;
340         FsRtlpDRD.NullHandle = DeviceHandle;
341 
342         /* Set DOS device UNC to use provider device */
343         FsRtlpSetSymbolicLink(RedirectorDeviceName);
344     }
345     else
346     {
347         /* We (will) have several providers, MUP is required */
348         Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Mup");
349         if (!NT_SUCCESS(Status))
350         {
351             /* Opening MUP may have failed because the driver was not loaded, so load it and retry */
352             RtlInitUnicodeString(&MupString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup");
353             ZwLoadDriver(&MupString);
354             Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Mup");
355             if (!NT_SUCCESS(Status))
356             {
357                 goto Cleanup;
358             }
359         }
360 
361         /* In case we had a single provider till now, we have to forward the old provider to MUP
362          * And then, register the new one to MUP as well
363          */
364         if (FsRtlpDRD.RedirectorDeviceName.Buffer != NULL)
365         {
366             /* We will only continue if we can register previous provider in MUP */
367             Status = FsRtlpRegisterProviderWithMUP(DeviceHandle, &FsRtlpDRD.RedirectorDeviceName, FsRtlpDRD.MailslotsSupported);
368             if (!NT_SUCCESS(Status))
369             {
370                 goto Cleanup;
371             }
372 
373             /* Save our Mup handle for later usage */
374             FsRtlpDRD.MupHandle = DeviceHandle;
375 
376             /* Release information about previous provider */
377             ExFreePoolWithTag(FsRtlpDRD.RedirectorDeviceName.Buffer, TAG_UNC);
378             FsRtlpDRD.RedirectorDeviceName.Buffer = NULL;
379 
380             /* Re-open MUP to have a handle to give back to the user */
381             Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Mup");
382             if (!NT_SUCCESS(Status))
383             {
384                 goto Cleanup;
385             }
386         }
387 
388         /* Redirect UNC DOS device to MUP */
389         RtlInitUnicodeString(&MupString, L"\\Device\\Mup");
390         FsRtlpSetSymbolicLink(&MupString);
391 
392         /* Register new provider */
393         Status = FsRtlpRegisterProviderWithMUP(DeviceHandle, RedirectorDeviceName, MailslotsSupported);
394     }
395 
396 Cleanup:
397 
398     /* In case of success, increment number of providers and return handle
399      * to the device pointed by UNC DOS device
400      */
401     if (NT_SUCCESS(Status))
402     {
403         ++FsRtlpRedirs;
404         *Handle = DeviceHandle;
405     }
406     else
407     {
408         /* Cleanup in case of failure */
409         if (DeviceHandle != INVALID_HANDLE_VALUE && DeviceHandle != 0)
410         {
411             ZwClose(DeviceHandle);
412         }
413 
414         *Handle = INVALID_HANDLE_VALUE;
415     }
416 
417     KeReleaseSemaphore(&FsRtlpUncSemaphore, IO_NO_INCREMENT, 1, FALSE);
418     return Status;
419 }
420