Rewrite FsRtlRegisterUncProvider() and FsRtlDeregisterUncProvider() so that they can support MUP in case it's required
They also support DFS


svn path=/trunk/; revision=68759
This commit is contained in:
Pierre Schweitzer 2015-08-18 20:17:28 +00:00
parent 05244f7905
commit 4c1d3954cd

View 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 ******************************************************************/
@ -12,11 +12,182 @@
#define NDEBUG
#include <debug.h>
#define TAG_UNC 'nuSF'
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 PUNICODE_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, DeviceName);
ASSERT(NT_SUCCESS(Status));
}
}
NTSTATUS
FsRtlpRegisterProviderWithMUP(IN HANDLE MupHandle,
IN PUNICODE_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,23 +203,63 @@ VOID
NTAPI
FsRtlDeregisterUncProvider(IN HANDLE Handle)
{
UNICODE_STRING DosDevicesUNC = RTL_CONSTANT_STRING(L"\\DosDevices\\UNC");
PAGED_CODE();
DPRINT("FsRtlDeregisterUncProvider: Handle=%p\n", Handle);
//
// Normal implementation should look like:
// - notify mup.sys?
// - at last deregistration, destroy \DosDevices\UNC symbolic link
//
/* We won't work on invalid input */
if (Handle == INVALID_HANDLE_VALUE || Handle == 0)
{
return;
}
ZwClose(Handle);
KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL);
IoDeleteSymbolicLink(&DosDevicesUNC);
/* 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
*
@ -59,6 +270,7 @@ FsRtlDeregisterUncProvider(IN HANDLE Handle)
* FILLME
*
* @param MailslotsSupported
* FILLME
*
* @return None
*
@ -67,68 +279,143 @@ FsRtlDeregisterUncProvider(IN HANDLE Handle)
*--*/
NTSTATUS
NTAPI
FsRtlRegisterUncProvider(IN OUT PHANDLE Handle,
FsRtlRegisterUncProvider(OUT PHANDLE Handle,
IN PUNICODE_STRING RedirectorDeviceName,
IN BOOLEAN MailslotsSupported)
{
UNICODE_STRING DevNull = RTL_CONSTANT_STRING(L"\\Device\\Null");
UNICODE_STRING DosDevicesUNC = RTL_CONSTANT_STRING(L"\\DosDevices\\UNC");
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK Iosb;
HANDLE FileHandle;
NTSTATUS Status;
HANDLE DeviceHandle;
UNICODE_STRING MupString;
DPRINT("FsRtlRegisterUncProvider: Redirector=%wZ MailslotsSupported=%d\n",
RedirectorDeviceName, MailslotsSupported);
PAGED_CODE();
//
// Current implementation is a hack, as it only supports one UNC provider.
// However, it doesn't require to have a functional mup.sys driver.
//
DPRINT1("FsRtlRegisterUncProvider(%p, %wZ, %u)\n", Handle, RedirectorDeviceName, MailslotsSupported);
//
// Normal implementation should look like:
// - at registration 1, creates symlink \DosDevices\UNC to new provider;
// returns handle to \Device\Null
// - at registration 2, load mup.sys, register both providers to mup.sys
// and change \DosDevices\UNC to DD_MUP_DEVICE_NAME;
// returns handle to new provider
// - at next registrations, register provider to mup.sys;
// returns handle to new provider
//
KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL);
*Handle = (HANDLE)-1;
InitializeObjectAttributes(&ObjectAttributes,
&DevNull,
OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwCreateFile(&FileHandle,
GENERIC_WRITE,
&ObjectAttributes,
&Iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
0,
NULL,
0);
if (!NT_SUCCESS(Status))
/* 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)
{
DPRINT("Failed to open %wZ\n", &DevNull);
return Status;
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;
}
}
Status = IoCreateSymbolicLink(&DosDevicesUNC, RedirectorDeviceName);
if (!NT_SUCCESS(Status))
/* In case no UNC provider was already registered,
* We'll proceed without MUP and directly redirect
* UNC to the provider.
*/
if (FsRtlpRedirs == 0)
{
DPRINT("Failed to create symbolic link %wZ -> %wZ\n", &DosDevicesUNC, RedirectorDeviceName);
DPRINT1("FIXME: multiple unc provider registered?\n");
ZwClose(FileHandle);
return Status;
/* 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);
}
*Handle = FileHandle;
return STATUS_SUCCESS;
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;
}