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