[CRT] Massively improve performance of rand_s
[reactos.git] / ntoskrnl / fsrtl / unc.c
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 }