[STORPORT] Fix x64 build
[reactos.git] / ntoskrnl / fsrtl / unc.c
index 8f68e8f..2f8a1af 100644 (file)
@@ -3,7 +3,7 @@
  * LICENSE:         GPL - See COPYING in the top level directory
  * FILE:            ntoskrnl/fsrtl/unc.c
  * PURPOSE:         Manages UNC support routines for file system drivers.
- * PROGRAMMERS:     None.
+ * PROGRAMMERS:     Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES ******************************************************************/
 #define NDEBUG
 #include <debug.h>
 
+KSEMAPHORE FsRtlpUncSemaphore;
+
+ULONG FsRtlpRedirs = 0;
+
+struct
+{
+    HANDLE MupHandle;
+    HANDLE NullHandle;
+    UNICODE_STRING RedirectorDeviceName;
+    BOOLEAN MailslotsSupported;
+} FsRtlpDRD;
+
+BOOLEAN
+FsRtlpIsDfsEnabled(VOID)
+{
+    HANDLE Key;
+    ULONG Length;
+    NTSTATUS Status;
+    UNICODE_STRING KeyName;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    struct
+    {
+        KEY_VALUE_PARTIAL_INFORMATION KeyInfo;
+        ULONG KeyValue;
+    } KeyQueryOutput;
+
+    /* You recognize MuppIsDfsEnabled()! Congratz :-) */
+    KeyName.Buffer = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup";
+    KeyName.Length = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup") - sizeof(UNICODE_NULL);
+    KeyName.MaximumLength = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup");
+
+    /* Simply query registry to get whether DFS is disabled.
+     * If DFS isn't disabled from registry side, assume it is enabled
+     * and go through MUP.
+     * MUP itself might disable it, but that's not our concern
+     * any longer
+     */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    Status = ZwOpenKey(&Key, KEY_READ, &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
+    {
+        return TRUE;
+    }
+
+    KeyName.Buffer = L"DisableDfs";
+    KeyName.Length = sizeof(L"DisableDfs") - sizeof(UNICODE_NULL);
+    KeyName.MaximumLength = sizeof(L"DisableDfs");
+
+    Status = ZwQueryValueKey(Key, &KeyName, KeyValuePartialInformation, &KeyQueryOutput, sizeof(KeyQueryOutput), &Length);
+    ZwClose(Key);
+    if (!NT_SUCCESS(Status) || KeyQueryOutput.KeyInfo.Type != REG_DWORD)
+    {
+        return TRUE;
+    }
+
+    return ((ULONG)KeyQueryOutput.KeyInfo.Data != 1);
+}
+
+NTSTATUS
+FsRtlpOpenDev(OUT PHANDLE DeviceHandle,
+              IN PCWSTR DeviceName)
+{
+    NTSTATUS Status;
+    UNICODE_STRING StrDeviceName;
+    IO_STATUS_BLOCK IoStatusBlock;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
+    PAGED_CODE();
+
+    /* Just open the device and return the obtained handle */
+    RtlInitUnicodeString(&StrDeviceName, DeviceName);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &StrDeviceName,
+                               0,
+                               NULL,
+                               NULL);
+    Status = ZwCreateFile(DeviceHandle,
+                          GENERIC_WRITE,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          FILE_ATTRIBUTE_NORMAL,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE,
+                          FILE_OPEN, 0, NULL, 0);
+    if (NT_SUCCESS(Status))
+    {
+        Status = IoStatusBlock.Status;
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        *DeviceHandle = INVALID_HANDLE_VALUE;
+    }
+
+    return Status;
+}
+
+VOID
+FsRtlpSetSymbolicLink(IN PCUNICODE_STRING DeviceName)
+{
+    NTSTATUS Status;
+    UNICODE_STRING UncDevice;
+
+    PAGED_CODE();
+
+    /* Delete the old link, and set the new one if we have a name */
+    RtlInitUnicodeString(&UncDevice, L"\\DosDevices\\UNC");
+    IoDeleteSymbolicLink(&UncDevice);
+    if (DeviceName != NULL)
+    {
+        Status = IoCreateSymbolicLink(&UncDevice, (PUNICODE_STRING)DeviceName);
+        ASSERT(NT_SUCCESS(Status));
+    }
+}
+
+NTSTATUS
+FsRtlpRegisterProviderWithMUP(IN HANDLE MupHandle,
+                              IN PCUNICODE_STRING RedirectorDeviceName,
+                              IN BOOLEAN MailslotsSupported)
+{
+    NTSTATUS Status;
+    ULONG BufferSize;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PMUP_PROVIDER_REGISTRATION_INFO RegistrationInfo;
+
+    PAGED_CODE();
+
+    DPRINT1("FsRtlpRegisterProviderWithMUP(%p, %wZ, %u)\n", (PVOID)MupHandle, RedirectorDeviceName, MailslotsSupported);
+
+    /* We have to be able to store the name and the registration information */
+    BufferSize = RedirectorDeviceName->Length + sizeof(MUP_PROVIDER_REGISTRATION_INFO);
+    RegistrationInfo = ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG_UNC);
+    if (RegistrationInfo == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Set the information about the provider (including its name) */
+    RegistrationInfo->RedirectorDeviceNameOffset = sizeof(MUP_PROVIDER_REGISTRATION_INFO);
+    RegistrationInfo->RedirectorDeviceNameLength = RedirectorDeviceName->Length;
+    RegistrationInfo->MailslotsSupported = MailslotsSupported;
+    RtlCopyMemory((PWSTR)((ULONG_PTR)RegistrationInfo + RegistrationInfo->RedirectorDeviceNameOffset),
+                  RedirectorDeviceName->Buffer, RedirectorDeviceName->Length);
+
+    /* Call MUP with the registration FSCTL */
+    Status = NtFsControlFile(MupHandle, NULL, NULL, NULL,
+                             &IoStatusBlock, FSCTL_MUP_REGISTER_PROVIDER,
+                             RegistrationInfo, BufferSize, NULL, 0);
+    if (Status == STATUS_PENDING)
+    {
+        Status = NtWaitForSingleObject(MupHandle, TRUE, NULL);
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        Status = IoStatusBlock.Status;
+    }
+
+    /* And we're done! */
+    ASSERT(NT_SUCCESS(Status));
+    ExFreePoolWithTag(RegistrationInfo, TAG_UNC);
+
+    return Status;
+}
+
 /* PUBLIC FUNCTIONS **********************************************************/
 
 /*++
  * @name FsRtlDeregisterUncProvider
- * @unimplemented
+ * @implemented
  *
  * FILLME
  *
@@ -32,13 +201,63 @@ VOID
 NTAPI
 FsRtlDeregisterUncProvider(IN HANDLE Handle)
 {
-    /* Unimplemented */
-    KeBugCheck(FILE_SYSTEM);
+    PAGED_CODE();
+
+    /* We won't work on invalid input */
+    if (Handle == INVALID_HANDLE_VALUE || Handle == 0)
+    {
+        return;
+    }
+
+    KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL);
+
+    /* Sanity check: we need to have providers */
+    ASSERT(FsRtlpRedirs > 0);
+
+    /* At that point, we had only one provider at a time */
+    if (Handle == (HANDLE)FsRtlpDRD.NullHandle)
+    {
+        /* Free its name if possible (it might have been overtaken in case of
+         * registration of other UNC provider */
+        if (FsRtlpDRD.RedirectorDeviceName.Buffer != NULL)
+        {
+            ExFreePoolWithTag(FsRtlpDRD.RedirectorDeviceName.Buffer, TAG_UNC);
+            FsRtlpDRD.RedirectorDeviceName.Buffer = NULL;
+        }
+
+        /* Close the handle to MUP */
+        if (FsRtlpDRD.MupHandle != INVALID_HANDLE_VALUE)
+        {
+            ZwClose(FsRtlpDRD.MupHandle);
+            FsRtlpDRD.MupHandle = INVALID_HANDLE_VALUE;
+        }
+
+        /* Last handle isn't required anymore */
+        FsRtlpDRD.NullHandle = INVALID_HANDLE_VALUE;
+    }
+
+    /* One less provider */
+    --FsRtlpRedirs;
+
+    /* In case we reach no provider anylonger, reset the symbolic link */
+    if (FsRtlpRedirs == 0)
+    {
+        FsRtlpSetSymbolicLink(NULL);
+    }
+
+    KeReleaseSemaphore(&FsRtlpUncSemaphore, IO_NO_INCREMENT, 1, FALSE);
+
+    /* Final note:
+     * NULL device handle and 'normal' MUP device handle are not closed by
+     * FsRtl. It's up to the user to close them afterwards.
+     * If the handle is leaked, MUP will never be notified about the
+     * unregistration.
+     */
 }
 
 /*++
  * @name FsRtlRegisterUncProvider
- * @unimplemented
+ * @implemented
  *
  * FILLME
  *
@@ -49,6 +268,7 @@ FsRtlDeregisterUncProvider(IN HANDLE Handle)
  *        FILLME
  *
  * @param MailslotsSupported
+ *        FILLME
  *
  * @return None
  *
@@ -57,11 +277,143 @@ FsRtlDeregisterUncProvider(IN HANDLE Handle)
  *--*/
 NTSTATUS
 NTAPI
-FsRtlRegisterUncProvider(IN OUT PHANDLE Handle,
-                         IN PUNICODE_STRING RedirectorDeviceName,
+FsRtlRegisterUncProvider(OUT PHANDLE Handle,
+                         IN PCUNICODE_STRING RedirectorDeviceName,
                          IN BOOLEAN MailslotsSupported)
 {
-    /* Unimplemented */
-    KeBugCheck(FILE_SYSTEM);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    HANDLE DeviceHandle;
+    UNICODE_STRING MupString;
+
+    PAGED_CODE();
+
+    DPRINT1("FsRtlRegisterUncProvider(%p, %wZ, %u)\n", Handle, RedirectorDeviceName, MailslotsSupported);
+
+    KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL);
+
+    /* In case no provider was registered yet, check for DFS present.
+     * If DFS is present, we need to go with MUP, whatever the case
+     */
+    if (FsRtlpRedirs == 0)
+    {
+        if (FsRtlpIsDfsEnabled())
+        {
+            DPRINT1("DFS is not disabled. Going through MUP\n");
+
+            /* We've to go with MUP, make sure our internal structure doesn't
+             * contain any leftover data and raise redirs to one, to make sure
+             * we use MUP.
+             */
+            RtlZeroMemory(&FsRtlpDRD, sizeof(FsRtlpDRD));
+            FsRtlpRedirs = 1;
+        }
+    }
+
+    /* In case no UNC provider was already registered,
+     * We'll proceed without MUP and directly redirect
+     * UNC to the provider.
+     */
+    if (FsRtlpRedirs == 0)
+    {
+        /* As we don't provide MUP, just give a handle to NULL device */
+        Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Null");
+        if (!NT_SUCCESS(Status))
+        {
+            goto Cleanup;
+        }
+
+        /* Allocate a buffer big enough to keep a local copy of UNC provider device */
+        FsRtlpDRD.RedirectorDeviceName.Buffer = ExAllocatePoolWithTag(NonPagedPool, RedirectorDeviceName->MaximumLength, TAG_UNC);
+        if (FsRtlpDRD.RedirectorDeviceName.Buffer == NULL)
+        {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto Cleanup;
+        }
+
+        FsRtlpDRD.RedirectorDeviceName.Length = RedirectorDeviceName->Length;
+        FsRtlpDRD.RedirectorDeviceName.MaximumLength = RedirectorDeviceName->MaximumLength;
+        RtlCopyMemory(FsRtlpDRD.RedirectorDeviceName.Buffer, RedirectorDeviceName->Buffer, RedirectorDeviceName->MaximumLength);
+
+        /* We don't have MUP, and copy provider information */
+        FsRtlpDRD.MupHandle = INVALID_HANDLE_VALUE;
+        FsRtlpDRD.MailslotsSupported = MailslotsSupported;
+        FsRtlpDRD.NullHandle = DeviceHandle;
+
+        /* Set DOS device UNC to use provider device */
+        FsRtlpSetSymbolicLink(RedirectorDeviceName);
+    }
+    else
+    {
+        /* We (will) have several providers, MUP is required */
+        Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Mup");
+        if (!NT_SUCCESS(Status))
+        {
+            /* Opening MUP may have failed because the driver was not loaded, so load it and retry */
+            RtlInitUnicodeString(&MupString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup");
+            ZwLoadDriver(&MupString);
+            Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Mup");
+            if (!NT_SUCCESS(Status))
+            {
+                goto Cleanup;
+            }
+        }
+
+        /* In case we had a single provider till now, we have to forward the old provider to MUP
+         * And then, register the new one to MUP as well
+         */
+        if (FsRtlpDRD.RedirectorDeviceName.Buffer != NULL)
+        {
+            /* We will only continue if we can register previous provider in MUP */
+            Status = FsRtlpRegisterProviderWithMUP(DeviceHandle, &FsRtlpDRD.RedirectorDeviceName, FsRtlpDRD.MailslotsSupported);
+            if (!NT_SUCCESS(Status))
+            {
+                goto Cleanup;
+            }
+
+            /* Save our Mup handle for later usage */
+            FsRtlpDRD.MupHandle = DeviceHandle;
+
+            /* Release information about previous provider */
+            ExFreePoolWithTag(FsRtlpDRD.RedirectorDeviceName.Buffer, TAG_UNC);
+            FsRtlpDRD.RedirectorDeviceName.Buffer = NULL;
+
+            /* Re-open MUP to have a handle to give back to the user */
+            Status = FsRtlpOpenDev(&DeviceHandle, L"\\Device\\Mup");
+            if (!NT_SUCCESS(Status))
+            {
+                goto Cleanup;
+            }
+        }
+
+        /* Redirect UNC DOS device to MUP */
+        RtlInitUnicodeString(&MupString, L"\\Device\\Mup");
+        FsRtlpSetSymbolicLink(&MupString);
+
+        /* Register new provider */
+        Status = FsRtlpRegisterProviderWithMUP(DeviceHandle, RedirectorDeviceName, MailslotsSupported);
+    }
+
+Cleanup:
+
+    /* In case of success, increment number of providers and return handle
+     * to the device pointed by UNC DOS device
+     */
+    if (NT_SUCCESS(Status))
+    {
+        ++FsRtlpRedirs;
+        *Handle = DeviceHandle;
+    }
+    else
+    {
+        /* Cleanup in case of failure */
+        if (DeviceHandle != INVALID_HANDLE_VALUE && DeviceHandle != 0)
+        {
+            ZwClose(DeviceHandle);
+        }
+
+        *Handle = INVALID_HANDLE_VALUE;
+    }
+
+    KeReleaseSemaphore(&FsRtlpUncSemaphore, IO_NO_INCREMENT, 1, FALSE);
+    return Status;
 }