windows-nt/Source/XPSP1/NT/base/ntos/fsrtl/unc.c

573 lines
13 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
unc.c
Abstract:
This file contains functions to support multiple UNC providers
on a single NT machine.
Author:
Manny Weiser [MannyW] 20-Dec-1991
Revision History:
Isaac Heizer [IsaacHe] 16-Nov-1994 Defer loading the MUP
Rewrite
Milan Shah [MilanS] 7-Mar-1996 Check for Dfs client status
before loading the MUP
--*/
#include "fsrtlp.h"
#include <zwapi.h>
#include <ntddmup.h>
#include <ntddnull.h>
#define MupRegKey L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup"
#define UNCSymbolicLink L"\\DosDevices\\UNC"
#define DevNull L"\\Device\\Null"
#define DevMup DD_MUP_DEVICE_NAME
//
// Define a tag for general pool allocations from this module
//
#undef MODULE_POOL_TAG
#define MODULE_POOL_TAG ('nuSF')
//
// Local prototypes
//
NTSTATUS
FsRtlpRegisterProviderWithMUP
(
IN HANDLE mupHandle,
IN PUNICODE_STRING RedirDevName,
IN BOOLEAN MailslotsSupported
);
NTSTATUS
FsRtlpOpenDev(
IN OUT PHANDLE Handle,
IN LPWSTR DevNameStr
);
VOID
FsRtlpSetSymbolicLink(
IN PUNICODE_STRING DevName OPTIONAL
);
BOOLEAN
FsRtlpIsDfsEnabled();
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FsRtlpRegisterProviderWithMUP)
#pragma alloc_text(PAGE, FsRtlpOpenDev)
#pragma alloc_text(PAGE, FsRtlpSetSymbolicLink)
#pragma alloc_text(PAGE, FsRtlRegisterUncProvider)
#pragma alloc_text(PAGE, FsRtlDeregisterUncProvider)
#pragma alloc_text(PAGE, FsRtlpIsDfsEnabled)
#endif
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif
//
// We defer calling the MUP with the registration data until
// the second redir loads and Dfs is disabled. This structure holds the
// data necessary to make that call.
//
struct {
HANDLE MupHandle;
HANDLE ReturnedHandle;
UNICODE_STRING RedirDevName;
BOOLEAN MailslotsSupported;
} FsRtlpDRD = {0};
//
// Number of times we've loaded redirs.
//
ULONG FsRtlpRedirs = 0;
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif
//
// Resource protection
//
KSEMAPHORE FsRtlpUncSemaphore;
NTSTATUS
FsRtlpRegisterProviderWithMUP
(
IN HANDLE mupHandle,
IN PUNICODE_STRING RedirDevName,
IN BOOLEAN MailslotsSupported
)
/*++
Routine Description:
This private routine does the FSCTL to the MUP to tell it about
a new redir
Arguments:
mupHandle - Handle to the MUP
RedirDevName - The device name of the redir.
MailslotsSupported - If TRUE, this redir supports mailslots.
Return Value:
NTSTATUS - The status of the operation.
--*/
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
ULONG paramLength;
PREDIRECTOR_REGISTRATION params;
PAGED_CODE();
paramLength = sizeof( REDIRECTOR_REGISTRATION ) +
RedirDevName->Length;
params = ExAllocatePoolWithTag( NonPagedPool, paramLength, MODULE_POOL_TAG );
if( params == NULL )
return STATUS_INSUFFICIENT_RESOURCES;
params->DeviceNameOffset = sizeof( REDIRECTOR_REGISTRATION );
params->DeviceNameLength = RedirDevName->Length;
params->MailslotsSupported = MailslotsSupported;
RtlCopyMemory(
(PCHAR)params + params->DeviceNameOffset,
RedirDevName->Buffer,
RedirDevName->Length
);
status = NtFsControlFile(
mupHandle,
0,
NULL,
NULL,
&ioStatusBlock,
FSCTL_MUP_REGISTER_UNC_PROVIDER,
params,
paramLength,
NULL,
0
);
if ( status == STATUS_PENDING ) {
status = NtWaitForSingleObject( mupHandle, TRUE, NULL );
}
if ( NT_SUCCESS( status ) ) {
status = ioStatusBlock.Status;
}
ASSERT( NT_SUCCESS( status ) );
ExFreePool( params );
return status;
}
NTSTATUS
FsRtlpOpenDev(
IN OUT PHANDLE Handle,
IN LPWSTR DevNameStr
)
{
NTSTATUS status;
UNICODE_STRING DevName;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
PAGED_CODE();
RtlInitUnicodeString( &DevName, DevNameStr );
InitializeObjectAttributes(
&objectAttributes,
&DevName,
0,
0,
NULL
);
status = ZwCreateFile(
Handle,
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 ) ) {
*Handle = (HANDLE)-1;
}
return status;
}
VOID
FsRtlpSetSymbolicLink( IN PUNICODE_STRING DevName OPTIONAL )
{
NTSTATUS status;
UNICODE_STRING UncSymbolicName;
PAGED_CODE();
RtlInitUnicodeString( &UncSymbolicName, UNCSymbolicLink );
(VOID)IoDeleteSymbolicLink( &UncSymbolicName );
if( ARGUMENT_PRESENT( DevName ) ) {
status = IoCreateSymbolicLink( &UncSymbolicName, DevName );
ASSERT( NT_SUCCESS( status ) );
}
}
NTSTATUS
FsRtlRegisterUncProvider(
IN OUT PHANDLE MupHandle,
IN PUNICODE_STRING RedirDevName,
IN BOOLEAN MailslotsSupported
)
/*++
Routine Description:
This routine registers a redir as a UNC provider.
Arguments:
Handle - Pointer to a handle. The handle is returned by the routine
to be used when calling FsRtlDeregisterUncProvider.
It is valid only if the routines returns STATUS_SUCCESS.
RedirDevName - The device name of the redir.
MailslotsSupported - If TRUE, this redir supports mailslots.
Return Value:
NTSTATUS - The status of the operation.
--*/
{
NTSTATUS status;
HANDLE mupHandle = (HANDLE)-1;
UNICODE_STRING mupDriverName;
BOOLEAN dfsEnabled;
PAGED_CODE();
KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL );
if (FsRtlpRedirs == 0) {
dfsEnabled = FsRtlpIsDfsEnabled();
if (dfsEnabled) {
FsRtlpRedirs = 1;
RtlZeroMemory((PVOID) &FsRtlpDRD, sizeof(FsRtlpDRD));
}
}
switch( FsRtlpRedirs ) {
case 0:
//
// Ok, the MUP isn't there and we don't need to use the
// MUP for the first redir.
//
// We need to return a handle, but we're not really using the MUP yet.
// And we may never use it (if there's only 1 redir). Return
// a handle to the NULL device object, since we're committed to returning
// a handle to our caller. Our caller isn't supposed to do anything with
// the handle except to call FsRtlDeregisterUncProvider() with it.
//
status = FsRtlpOpenDev( &mupHandle, DevNull );
if( !NT_SUCCESS( status ) )
break;
//
// Save up enough state to allow us to call the MUP later with
// this registration info if necessary.
//
FsRtlpDRD.RedirDevName.Buffer = ExAllocatePoolWithTag( NonPagedPool,
RedirDevName->MaximumLength,
MODULE_POOL_TAG );
if( FsRtlpDRD.RedirDevName.Buffer == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
FsRtlpDRD.RedirDevName.Length = RedirDevName->Length;
FsRtlpDRD.RedirDevName.MaximumLength = RedirDevName->MaximumLength;
RtlCopyMemory(
(PCHAR)FsRtlpDRD.RedirDevName.Buffer,
RedirDevName->Buffer,
RedirDevName->MaximumLength
);
FsRtlpDRD.MailslotsSupported = MailslotsSupported;
FsRtlpDRD.ReturnedHandle = mupHandle;
FsRtlpDRD.MupHandle = (HANDLE)-1;
//
// Set the UNC symbolic link to point to the redir we just loaded
//
FsRtlpSetSymbolicLink( RedirDevName );
break;
default:
//
// This is the second or later redir load -- MUST use the MUP
//
status = FsRtlpOpenDev( &mupHandle, DevMup );
if( !NT_SUCCESS( status ) ) {
RtlInitUnicodeString( &mupDriverName, MupRegKey );
(VOID)ZwLoadDriver( &mupDriverName );
status = FsRtlpOpenDev( &mupHandle, DevMup );
if( !NT_SUCCESS( status ) )
break;
}
//
// See if we need to tell the MUP about the first redir that registered
//
if( FsRtlpDRD.RedirDevName.Buffer ) {
status = FsRtlpRegisterProviderWithMUP( mupHandle,
&FsRtlpDRD.RedirDevName,
FsRtlpDRD.MailslotsSupported );
if( !NT_SUCCESS( status ) )
break;
FsRtlpDRD.MupHandle = mupHandle;
ExFreePool( FsRtlpDRD.RedirDevName.Buffer );
FsRtlpDRD.RedirDevName.Buffer = NULL;
//
// Set the UNC symbolic link to point to the MUP
//
RtlInitUnicodeString( &mupDriverName, DevMup );
FsRtlpSetSymbolicLink( &mupDriverName );
status = FsRtlpOpenDev( &mupHandle, DevMup );
if( !NT_SUCCESS( status ) )
break;
}
//
// Pass the request to the MUP for this redir
//
status = FsRtlpRegisterProviderWithMUP( mupHandle,
RedirDevName,
MailslotsSupported );
break;
}
if( NT_SUCCESS( status ) ) {
FsRtlpRedirs++;
*MupHandle = mupHandle;
} else {
if( mupHandle != (HANDLE)-1 && mupHandle != NULL ) {
ZwClose( mupHandle );
}
*MupHandle = (HANDLE)-1;
}
KeReleaseSemaphore(&FsRtlpUncSemaphore, 0, 1, FALSE );
return status;
}
VOID
FsRtlDeregisterUncProvider(
IN HANDLE Handle
)
/*++
Routine Description:
This routine deregisters a redir as a UNC provider.
Arguments:
Handle - A handle to the Multiple UNC router, returned by the
registration call.
Return Value:
None.
--*/
{
NTSTATUS status;
PAGED_CODE();
if( Handle == (HANDLE)-1 || Handle == NULL )
return;
status = ZwClose( Handle );
if( !NT_SUCCESS( status ) ) {
return;
}
KeWaitForSingleObject(&FsRtlpUncSemaphore, Executive, KernelMode, FALSE, NULL );
ASSERT( FsRtlpRedirs > 0 );
if( Handle == FsRtlpDRD.ReturnedHandle ) {
//
// The first redir in the system is closing. Release the state we saved
// for it, and pass the close on to the MUP if necessary
//
if( FsRtlpDRD.RedirDevName.Buffer != NULL ) {
ExFreePool( FsRtlpDRD.RedirDevName.Buffer );
FsRtlpDRD.RedirDevName.Buffer = NULL;
}
if( FsRtlpDRD.MupHandle != (HANDLE)-1 ) {
ZwClose( FsRtlpDRD.MupHandle );
FsRtlpDRD.MupHandle = (HANDLE)-1;
}
FsRtlpDRD.ReturnedHandle = (HANDLE)-1;
}
if( --FsRtlpRedirs == 0 ) {
FsRtlpSetSymbolicLink( (PUNICODE_STRING)NULL );
}
KeReleaseSemaphore(&FsRtlpUncSemaphore, 0, 1, FALSE );
}
BOOLEAN
FsRtlpIsDfsEnabled()
/*++
Routine Description:
This routine checks a registry key to see if the Dfs client is enabled.
The client is assumed to be enabled by default, and disabled only if there
is a registry value indicating that it should be disabled.
Arguments:
None
Return Value:
TRUE if Dfs client is enabled, FALSE otherwise.
--*/
{
NTSTATUS status;
HANDLE mupRegHandle;
OBJECT_ATTRIBUTES objectAttributes;
ULONG valueSize;
BOOLEAN dfsEnabled = TRUE;
UNICODE_STRING mupRegKey = {
sizeof(MupRegKey) - sizeof(WCHAR),
sizeof(MupRegKey),
MupRegKey};
#define DISABLE_DFS_VALUE_NAME L"DisableDfs"
UNICODE_STRING disableDfs = {
sizeof(DISABLE_DFS_VALUE_NAME) - sizeof(WCHAR),
sizeof(DISABLE_DFS_VALUE_NAME),
DISABLE_DFS_VALUE_NAME};
struct {
KEY_VALUE_PARTIAL_INFORMATION Info;
ULONG Buffer;
} disableDfsValue;
InitializeObjectAttributes(
&objectAttributes,
&mupRegKey,
OBJ_CASE_INSENSITIVE,
0,
NULL
);
status = ZwOpenKey(&mupRegHandle, KEY_READ, &objectAttributes);
if (NT_SUCCESS(status)) {
status = ZwQueryValueKey(
mupRegHandle,
&disableDfs,
KeyValuePartialInformation,
(PVOID) &disableDfsValue,
sizeof(disableDfsValue),
&valueSize);
if (NT_SUCCESS(status) && disableDfsValue.Info.Type == REG_DWORD) {
if ( (*((PULONG) disableDfsValue.Info.Data)) == 1 )
dfsEnabled = FALSE;
}
ZwClose( mupRegHandle );
}
return( dfsEnabled );
}