reactos/drivers/filesystems/mup/mup.c

2563 lines
78 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2002 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystems/mup/mup.c
* PURPOSE: Multi UNC Provider
* PROGRAMMER: Eric Kohl
* Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include "mup.h"
#define NDEBUG
#include <debug.h>
NTSTATUS
NTAPI
DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
);
VOID
MupInitializeData(
VOID
);
VOID
MupInitializeVcb(
PMUP_VCB Vcb
);
#if defined(ALLOC_PRAGMA)
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, MupInitializeData)
#pragma alloc_text(INIT, MupInitializeVcb)
#endif
ERESOURCE MupGlobalLock;
ERESOURCE MupPrefixTableLock;
ERESOURCE MupCcbListLock;
ERESOURCE MupVcbLock;
LIST_ENTRY MupProviderList;
LIST_ENTRY MupPrefixList;
LIST_ENTRY MupMasterQueryList;
UNICODE_PREFIX_TABLE MupPrefixTable;
LARGE_INTEGER MupKnownPrefixTimeout;
ULONG MupProviderCount;
BOOLEAN MupOrderInitialized;
BOOLEAN MupEnableDfs;
PDEVICE_OBJECT mupDeviceObject;
NTSTATUS MupOrderedErrorList[] = { STATUS_UNSUCCESSFUL,
STATUS_INVALID_PARAMETER,
STATUS_REDIRECTOR_NOT_STARTED,
STATUS_BAD_NETWORK_NAME,
STATUS_BAD_NETWORK_PATH, 0 };
/* FUNCTIONS ****************************************************************/
INIT_SECTION
VOID
MupInitializeData(VOID)
{
ExInitializeResourceLite(&MupGlobalLock);
ExInitializeResourceLite(&MupPrefixTableLock);
ExInitializeResourceLite(&MupCcbListLock);
ExInitializeResourceLite(&MupVcbLock);
MupProviderCount = 0;
InitializeListHead(&MupProviderList);
InitializeListHead(&MupPrefixList);
InitializeListHead(&MupMasterQueryList);
RtlInitializeUnicodePrefix(&MupPrefixTable);
MupKnownPrefixTimeout.QuadPart = 9000000000LL;
MupOrderInitialized = FALSE;
}
VOID
MupUninitializeData()
{
ExDeleteResourceLite(&MupGlobalLock);
ExDeleteResourceLite(&MupPrefixTableLock);
ExDeleteResourceLite(&MupCcbListLock);
ExDeleteResourceLite(&MupVcbLock);
}
INIT_SECTION
VOID
MupInitializeVcb(PMUP_VCB Vcb)
{
RtlZeroMemory(Vcb, sizeof(MUP_VCB));
Vcb->NodeType = NODE_TYPE_VCB;
Vcb->NodeStatus = NODE_STATUS_HEALTHY;
Vcb->NodeReferences = 1;
Vcb->NodeSize = sizeof(MUP_VCB);
}
BOOLEAN
MuppIsDfsEnabled(VOID)
{
HANDLE Key;
ULONG Length;
NTSTATUS Status;
UNICODE_STRING KeyName;
OBJECT_ATTRIBUTES ObjectAttributes;
struct
{
KEY_VALUE_PARTIAL_INFORMATION KeyInfo;
ULONG KeyValue;
} KeyQueryOutput;
/* You recognize FsRtlpIsDfsEnabled()! 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 know whether we have to disable DFS.
* Unless explicitly stated in registry, we will try to enable DFS.
*/
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);
}
VOID
MupCalculateTimeout(PLARGE_INTEGER EntryTime)
{
LARGE_INTEGER CurrentTime;
/* Just get now + our allowed timeout */
KeQuerySystemTime(&CurrentTime);
EntryTime->QuadPart = CurrentTime.QuadPart + MupKnownPrefixTimeout.QuadPart;
}
PMUP_CCB
MupCreateCcb(VOID)
{
PMUP_CCB Ccb;
Ccb = ExAllocatePoolWithTag(PagedPool, sizeof(MUP_CCB), TAG_MUP);
if (Ccb == NULL)
{
return NULL;
}
Ccb->NodeStatus = NODE_STATUS_HEALTHY;
Ccb->NodeReferences = 1;
Ccb->NodeType = NODE_TYPE_CCB;
Ccb->NodeSize = sizeof(MUP_CCB);
return Ccb;
}
PMUP_FCB
MupCreateFcb(VOID)
{
PMUP_FCB Fcb;
Fcb = ExAllocatePoolWithTag(PagedPool, sizeof(MUP_FCB), TAG_MUP);
if (Fcb == NULL)
{
return NULL;
}
Fcb->NodeStatus = NODE_STATUS_HEALTHY;
Fcb->NodeReferences = 1;
Fcb->NodeType = NODE_TYPE_FCB;
Fcb->NodeSize = sizeof(MUP_FCB);
InitializeListHead(&Fcb->CcbList);
return Fcb;
}
PMUP_MIC
MupAllocateMasterIoContext(VOID)
{
PMUP_MIC MasterIoContext;
MasterIoContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(MUP_MIC), TAG_MUP);
if (MasterIoContext == NULL)
{
return NULL;
}
MasterIoContext->NodeStatus = NODE_STATUS_HEALTHY;
MasterIoContext->NodeReferences = 1;
MasterIoContext->NodeType = NODE_TYPE_MIC;
MasterIoContext->NodeSize = sizeof(MUP_MIC);
return MasterIoContext;
}
PMUP_UNC
MupAllocateUncProvider(ULONG RedirectorDeviceNameLength)
{
PMUP_UNC UncProvider;
UncProvider = ExAllocatePoolWithTag(PagedPool, sizeof(MUP_UNC) + RedirectorDeviceNameLength, TAG_MUP);
if (UncProvider == NULL)
{
return NULL;
}
UncProvider->NodeReferences = 0;
UncProvider->NodeType = NODE_TYPE_UNC;
UncProvider->NodeStatus = NODE_STATUS_HEALTHY;
UncProvider->NodeSize = sizeof(MUP_UNC) + RedirectorDeviceNameLength;
UncProvider->Registered = FALSE;
return UncProvider;
}
PMUP_PFX
MupAllocatePrefixEntry(ULONG PrefixLength)
{
PMUP_PFX Prefix;
ULONG PrefixSize;
PrefixSize = sizeof(MUP_PFX) + PrefixLength;
Prefix = ExAllocatePoolWithTag(PagedPool, PrefixSize, TAG_MUP);
if (Prefix == NULL)
{
return NULL;
}
RtlZeroMemory(Prefix, PrefixSize);
Prefix->NodeType = NODE_TYPE_PFX;
Prefix->NodeStatus = NODE_STATUS_HEALTHY;
Prefix->NodeReferences = 1;
Prefix->NodeSize = PrefixSize;
/* If we got a prefix, initialize the string */
if (PrefixLength != 0)
{
Prefix->AcceptedPrefix.Buffer = (PVOID)((ULONG_PTR)Prefix + sizeof(MUP_PFX));
Prefix->AcceptedPrefix.Length = PrefixLength;
Prefix->AcceptedPrefix.MaximumLength = PrefixLength;
}
/* Otherwise, mark the fact that the allocation will be external */
else
{
Prefix->ExternalAlloc = TRUE;
}
Prefix->KeepExtraRef = FALSE;
/* Already init timeout to have one */
MupCalculateTimeout(&Prefix->ValidityTimeout);
return Prefix;
}
PMUP_MQC
MupAllocateMasterQueryContext(VOID)
{
PMUP_MQC MasterQueryContext;
MasterQueryContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(MUP_MQC), TAG_MUP);
if (MasterQueryContext == NULL)
{
return NULL;
}
MasterQueryContext->NodeStatus = NODE_STATUS_HEALTHY;
MasterQueryContext->NodeReferences = 1;
MasterQueryContext->NodeType = NODE_TYPE_MQC;
MasterQueryContext->NodeSize = sizeof(MUP_MQC);
InitializeListHead(&MasterQueryContext->QueryPathList);
InitializeListHead(&MasterQueryContext->MQCListEntry);
ExInitializeResourceLite(&MasterQueryContext->QueryPathListLock);
return MasterQueryContext;
}
ULONG
MupDecodeFileObject(PFILE_OBJECT FileObject, PMUP_FCB * pFcb, PMUP_CCB * pCcb)
{
ULONG Type;
PMUP_FCB Fcb;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
*pFcb = FileObject->FsContext;
*pCcb = FileObject->FsContext2;
Fcb = *pFcb;
if (Fcb == NULL)
{
ExReleaseResourceLite(&MupGlobalLock);
return 0;
}
Type = Fcb->NodeType;
if ((Type != NODE_TYPE_VCB && Type != NODE_TYPE_FCB) || (Fcb->NodeStatus != NODE_STATUS_HEALTHY && Fcb->NodeStatus != NODE_STATUS_CLEANUP))
{
*pFcb = 0;
ExReleaseResourceLite(&MupGlobalLock);
return 0;
}
++Fcb->NodeReferences;
ExReleaseResourceLite(&MupGlobalLock);
return Type;
}
VOID
MupFreeNode(PVOID Node)
{
ExFreePoolWithTag(Node, TAG_MUP);
}
VOID
MupDereferenceFcb(PMUP_FCB Fcb)
{
/* Just dereference and delete if no references left */
if (InterlockedDecrement(&Fcb->NodeReferences) == 0)
{
MupFreeNode(Fcb);
}
}
VOID
MupDereferenceCcb(PMUP_CCB Ccb)
{
/* Just dereference and delete (and clean) if no references left */
if (InterlockedDecrement(&Ccb->NodeReferences) == 0)
{
ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
RemoveEntryList(&Ccb->CcbListEntry);
ExReleaseResourceLite(&MupCcbListLock);
ObDereferenceObject(Ccb->FileObject);
MupDereferenceFcb(Ccb->Fcb);
MupFreeNode(Ccb);
}
}
VOID
MupDereferenceVcb(PMUP_VCB Vcb)
{
/* We cannot reach the point where no references are left */
if (InterlockedDecrement(&Vcb->NodeReferences) == 0)
{
KeBugCheckEx(FILE_SYSTEM, 3, 0, 0, 0);
}
}
NTSTATUS
MupDereferenceMasterIoContext(PMUP_MIC MasterIoContext,
PNTSTATUS NewStatus)
{
PIRP Irp;
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
Status = STATUS_SUCCESS;
if (NewStatus != NULL)
{
/* Save last status, depending on whether it failed or not */
if (!NT_SUCCESS(*NewStatus))
{
MasterIoContext->LastFailed = *NewStatus;
}
else
{
MasterIoContext->LastSuccess = STATUS_SUCCESS;
}
}
if (InterlockedDecrement(&MasterIoContext->NodeReferences) != 0)
{
return STATUS_PENDING;
}
Irp = MasterIoContext->Irp;
Stack = IoGetCurrentIrpStackLocation(Irp);
if (Stack->MajorFunction == IRP_MJ_WRITE)
{
Irp->IoStatus.Information = Stack->Parameters.Write.Length;
}
else
{
Irp->IoStatus.Information = 0;
}
/* In case we never had any success (init is a failure), get the last failed status */
if (!NT_SUCCESS(MasterIoContext->LastSuccess))
{
Status = MasterIoContext->LastFailed;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
MupDereferenceFcb(MasterIoContext->Fcb);
MupFreeNode(MasterIoContext);
return Status;
}
VOID
MupDereferenceUncProvider(PMUP_UNC UncProvider)
{
InterlockedExchangeAdd(&UncProvider->NodeReferences, -1);
}
VOID
MupDereferenceKnownPrefix(PMUP_PFX Prefix)
{
/* Just dereference and delete (and clean) if no references left */
if (InterlockedDecrement(&Prefix->NodeReferences) == 0)
{
if (Prefix->InTable)
{
RtlRemoveUnicodePrefix(&MupPrefixTable, &Prefix->PrefixTableEntry);
RemoveEntryList(&Prefix->PrefixListEntry);
}
if (Prefix->ExternalAlloc && Prefix->AcceptedPrefix.Buffer != NULL)
{
ExFreePoolWithTag(Prefix->AcceptedPrefix.Buffer, TAG_MUP);
}
if (Prefix->UncProvider)
{
MupDereferenceUncProvider(Prefix->UncProvider);
}
MupFreeNode(Prefix);
}
}
VOID
MupRemoveKnownPrefixEntry(PMUP_PFX Prefix)
{
RtlRemoveUnicodePrefix(&MupPrefixTable, &Prefix->PrefixTableEntry);
RemoveEntryList(&Prefix->PrefixListEntry);
Prefix->InTable = FALSE;
MupDereferenceKnownPrefix(Prefix);
}
VOID
MupInvalidatePrefixTable(VOID)
{
PMUP_PFX Prefix;
PLIST_ENTRY Entry;
/* Browse the prefix table */
ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
for (Entry = MupPrefixList.Flink;
Entry != &MupPrefixList;
Entry = Entry->Flink)
{
Prefix = CONTAINING_RECORD(Entry, MUP_PFX, PrefixListEntry);
/* And remove any entry in it */
if (Prefix->InTable)
{
MupRemoveKnownPrefixEntry(Prefix);
}
}
ExReleaseResourceLite(&MupPrefixTableLock);
}
VOID
MupCleanupVcb(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PMUP_VCB Vcb)
{
ExAcquireResourceExclusiveLite(&MupVcbLock, TRUE);
/* Check we're not doing anything wrong first */
if (Vcb->NodeStatus != NODE_STATUS_HEALTHY || Vcb->NodeType != NODE_TYPE_VCB)
{
ExRaiseStatus(STATUS_INVALID_HANDLE);
}
/* Remove share access */
IoRemoveShareAccess(IoGetCurrentIrpStackLocation(Irp)->FileObject, &Vcb->ShareAccess);
ExReleaseResourceLite(&MupVcbLock);
}
VOID
MupCleanupFcb(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PMUP_FCB Fcb)
{
PLIST_ENTRY Entry;
PMUP_CCB Ccb;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
/* Check we're not doing anything wrong first */
if (Fcb->NodeStatus != NODE_STATUS_HEALTHY || Fcb->NodeType != NODE_TYPE_FCB)
{
ExRaiseStatus(STATUS_INVALID_HANDLE);
}
Fcb->NodeStatus = NODE_STATUS_CLEANUP;
ExReleaseResourceLite(&MupGlobalLock);
/* Dereference any CCB associated with the FCB */
ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
for (Entry = Fcb->CcbList.Flink;
Entry != &Fcb->CcbList;
Entry = Entry->Flink)
{
Ccb = CONTAINING_RECORD(Entry, MUP_CCB, CcbListEntry);
ExReleaseResourceLite(&MupCcbListLock);
MupDereferenceCcb(Ccb);
ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
}
ExReleaseResourceLite(&MupCcbListLock);
}
NTSTATUS
CommonForwardedIoCompletionRoutine(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PFORWARDED_IO_CONTEXT FwdCtxt)
{
NTSTATUS Status;
Status = Irp->IoStatus.Status;
/* Just free everything we had allocated */
if (Irp->MdlAddress != NULL)
{
MmUnlockPages(Irp->MdlAddress);
IoFreeMdl(Irp->MdlAddress);
}
if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
{
ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer, TAG_MUP);
}
IoFreeIrp(Irp);
/* Dereference the master context
* The upper IRP will be completed once all the lower IRPs are done
* (and thus, references count reaches 0)
*/
MupDereferenceCcb(FwdCtxt->Ccb);
MupDereferenceMasterIoContext(FwdCtxt->MasterIoContext, &Status);
ExFreePoolWithTag(FwdCtxt, TAG_MUP);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
NTAPI
DeferredForwardedIoCompletionRoutine(PVOID Context)
{
PFORWARDED_IO_CONTEXT FwdCtxt = (PFORWARDED_IO_CONTEXT)Context;
CommonForwardedIoCompletionRoutine(FwdCtxt->DeviceObject, FwdCtxt->Irp, Context);
}
NTSTATUS
NTAPI
ForwardedIoCompletionRoutine(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context)
{
PFORWARDED_IO_CONTEXT FwdCtxt;
/* If we're at DISPATCH_LEVEL, we cannot complete, defer completion */
if (KeGetCurrentIrql() < DISPATCH_LEVEL)
{
CommonForwardedIoCompletionRoutine(DeviceObject, Irp, Context);
}
else
{
FwdCtxt = (PFORWARDED_IO_CONTEXT)Context;
ExInitializeWorkItem(&FwdCtxt->WorkQueueItem, DeferredForwardedIoCompletionRoutine, Context);
ExQueueWorkItem(&FwdCtxt->WorkQueueItem, CriticalWorkQueue);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
BuildAndSubmitIrp(PIRP Irp,
PMUP_CCB Ccb,
PMUP_MIC MasterIoContext)
{
PMDL Mdl;
PIRP LowerIrp;
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
PDEVICE_OBJECT DeviceObject;
PFORWARDED_IO_CONTEXT FwdCtxt;
Status = STATUS_SUCCESS;
LowerIrp = NULL;
Mdl = NULL;
/* Allocate a context for the completion routine */
FwdCtxt = ExAllocatePoolWithTag(NonPagedPool, sizeof(FORWARDED_IO_CONTEXT), TAG_MUP);
if (FwdCtxt == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
/* Init it */
FwdCtxt->DeviceObject = NULL;
FwdCtxt->Irp = NULL;
/* Allocate the IRP */
DeviceObject = IoGetRelatedDeviceObject(Ccb->FileObject);
LowerIrp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
if (LowerIrp == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
/* Initialize it */
LowerIrp->Tail.Overlay.OriginalFileObject = Ccb->FileObject;
LowerIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
LowerIrp->RequestorMode = KernelMode;
/* Copy the stack of the request we received to the IRP we'll pass below */
Stack = IoGetNextIrpStackLocation(LowerIrp);
RtlMoveMemory(Stack, IoGetCurrentIrpStackLocation(Irp), sizeof(IO_STACK_LOCATION));
Stack->FileObject = Ccb->FileObject;
/* Setup flags according to the FO */
if (Ccb->FileObject->Flags & FO_WRITE_THROUGH)
{
Stack->Flags = SL_WRITE_THROUGH;
}
_SEH2_TRY
{
/* Does the device requires we do buffered IOs? */
if (DeviceObject->Flags & DO_BUFFERED_IO)
{
LowerIrp->AssociatedIrp.SystemBuffer = NULL;
if (Stack->Parameters.Write.Length == 0)
{
LowerIrp->Flags = IRP_BUFFERED_IO;
}
/* If we have data to pass */
else
{
/* If it's coming from usermode, probe first */
if (Irp->RequestorMode == UserMode)
{
ProbeForRead(Irp->UserBuffer, Stack->Parameters.Write.Length, sizeof(UCHAR));
}
/* Allocate the buffer */
LowerIrp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuotaTag(PagedPoolCacheAligned,
Stack->Parameters.Write.Length,
TAG_MUP);
if (LowerIrp->AssociatedIrp.SystemBuffer == NULL)
{
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
/* And copy input (remember, we've to free!) */
RtlMoveMemory(LowerIrp->AssociatedIrp.SystemBuffer, Irp->UserBuffer, Stack->Parameters.Write.Length);
LowerIrp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
}
}
else
{
if (!(DeviceObject->Flags & DO_DIRECT_IO))
{
LowerIrp->UserBuffer = Irp->UserBuffer;
}
else
{
/* For direct IOs, allocate an MDL and pass it */
if (Stack->Parameters.Write.Length != 0)
{
Mdl = IoAllocateMdl(Irp->UserBuffer, Stack->Parameters.Write.Length, FALSE, TRUE, LowerIrp);
if (Mdl == NULL)
{
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoReadAccess);
}
}
}
/* Fix flags in the IRP */
if (Ccb->FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
{
LowerIrp->Flags |= IRP_WRITE_OPERATION | IRP_NOCACHE;
}
else
{
LowerIrp->Flags |= IRP_WRITE_OPERATION;
}
FwdCtxt->Ccb = Ccb;
FwdCtxt->MasterIoContext = MasterIoContext;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
++MasterIoContext->NodeReferences;
ExReleaseResourceLite(&MupGlobalLock);
/* Set out completion routine */
IoSetCompletionRoutine(LowerIrp, ForwardedIoCompletionRoutine, FwdCtxt, TRUE, TRUE, TRUE);
/* And call the device with our brand new IRP */
Status = IoCallDriver(Ccb->DeviceObject, LowerIrp);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
Cleanup:
if (!NT_SUCCESS(Status))
{
if (FwdCtxt != NULL)
{
ExFreePoolWithTag(FwdCtxt, TAG_MUP);
}
if (LowerIrp != NULL)
{
if (LowerIrp->AssociatedIrp.SystemBuffer == NULL)
{
ExFreePoolWithTag(LowerIrp->AssociatedIrp.SystemBuffer, TAG_MUP);
}
IoFreeIrp(LowerIrp);
}
if (Mdl != NULL)
{
IoFreeMdl(Mdl);
}
}
return Status;
}
NTSTATUS
NTAPI
MupForwardIoRequest(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PMUP_FCB Fcb;
PMUP_CCB Ccb;
NTSTATUS Status;
PLIST_ENTRY Entry;
PMUP_CCB FcbListCcb;
BOOLEAN CcbLockAcquired;
PMUP_MIC MasterIoContext;
PIO_STACK_LOCATION Stack;
/* If DFS is enabled, check if that's for DFS and is so relay */
if (MupEnableDfs && DeviceObject->DeviceType == FILE_DEVICE_DFS)
{
return DfsVolumePassThrough(DeviceObject, Irp);
}
Stack = IoGetCurrentIrpStackLocation(Irp);
FsRtlEnterFileSystem();
/* Write request is only possible for a mailslot, we need a FCB */
MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb);
if (Fcb == NULL || Fcb->NodeType != NODE_TYPE_FCB)
{
FsRtlExitFileSystem();
Status = STATUS_INVALID_DEVICE_REQUEST;
goto Complete;
}
/* Allocate a context */
MasterIoContext = MupAllocateMasterIoContext();
if (MasterIoContext == NULL)
{
FsRtlExitFileSystem();
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Complete;
}
/* Mark the IRP pending and init the context */
IoMarkIrpPending(Irp);
MasterIoContext->Irp = Irp;
/* Init with a failure to catch if we ever succeed */
MasterIoContext->LastSuccess = STATUS_UNSUCCESSFUL;
/* Init with the worth failure possible */
MasterIoContext->LastFailed = STATUS_BAD_NETWORK_PATH;
MasterIoContext->Fcb = Fcb;
_SEH2_TRY
{
ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
CcbLockAcquired = TRUE;
/* For all the CCB (ie, the mailslots) we have */
for (Entry = Fcb->CcbList.Flink;
Entry != &Fcb->CcbList;
Entry = Entry->Flink)
{
FcbListCcb = CONTAINING_RECORD(Entry, MUP_CCB, CcbListEntry);
ExReleaseResourceLite(&MupCcbListLock);
CcbLockAcquired = FALSE;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
++FcbListCcb->NodeReferences;
ExReleaseResourceLite(&MupGlobalLock);
/* Forward the write request */
BuildAndSubmitIrp(Irp, FcbListCcb, MasterIoContext);
ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
CcbLockAcquired = TRUE;
}
ExReleaseResourceLite(&MupCcbListLock);
CcbLockAcquired = FALSE;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
if (CcbLockAcquired)
{
ExReleaseResourceLite(&MupCcbListLock);
}
}
_SEH2_END;
/* And done */
MupDereferenceMasterIoContext(MasterIoContext, NULL);
FsRtlExitFileSystem();
return STATUS_PENDING;
Complete:
/* Failure case */
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return Status;
}
PMUP_UNC
AddUnregisteredProvider(PCWSTR DeviceName,
ULONG ProviderOrder)
{
PMUP_UNC UncProvider;
ULONG StrLen, NameLen;
/* Just allocate the node */
NameLen = wcslen(DeviceName);
StrLen = NameLen * sizeof(WCHAR);
UncProvider = MupAllocateUncProvider(StrLen);
if (UncProvider == NULL)
{
return NULL;
}
/* And init it */
UncProvider->DeviceName.MaximumLength = StrLen;
UncProvider->DeviceName.Length = StrLen;
UncProvider->DeviceName.Buffer = (PWSTR)((ULONG_PTR)UncProvider + sizeof(MUP_UNC));
UncProvider->ProviderOrder = ProviderOrder;
RtlMoveMemory(UncProvider->DeviceName.Buffer, DeviceName, StrLen);
/* And add it to the global list
* We're using tail here so that when called from registry init,
* the providers with highest priority will be in the head of
* the list
*/
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
InsertTailList(&MupProviderList, &UncProvider->ProviderListEntry);
ExReleaseResourceLite(&MupGlobalLock);
return UncProvider;
}
VOID
InitializeProvider(PCWSTR ProviderName,
ULONG ProviderOrder)
{
NTSTATUS Status;
HANDLE KeyHandle;
UNICODE_STRING Key, Value;
PKEY_VALUE_FULL_INFORMATION Info;
OBJECT_ATTRIBUTES ObjectAttributes;
ULONG NameLen, StrLen, ResultLength;
/* Get the information about the provider from registry */
NameLen = wcslen(ProviderName);
StrLen = NameLen * sizeof(WCHAR) + sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") + sizeof(L"\\NetworkProvider");
Key.Buffer = ExAllocatePoolWithTag(PagedPool, StrLen, TAG_MUP);
if (Key.Buffer == NULL)
{
return;
}
RtlMoveMemory(Key.Buffer, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\", sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"));
Key.MaximumLength = StrLen;
Key.Length = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") - sizeof(UNICODE_NULL);
RtlAppendUnicodeToString(&Key, ProviderName);
RtlAppendUnicodeToString(&Key, L"\\NetworkProvider");
InitializeObjectAttributes(&ObjectAttributes,
&Key,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
ExFreePoolWithTag(Key.Buffer, TAG_MUP);
if (!NT_SUCCESS(Status))
{
return;
}
RtlInitUnicodeString(&Value, L"DeviceName");
Status = ZwQueryValueKey(KeyHandle, &Value, KeyValueFullInformation, NULL, 0, &ResultLength);
if (Status == STATUS_BUFFER_TOO_SMALL)
{
Info = ExAllocatePoolWithTag(PagedPool, ResultLength + sizeof(UNICODE_NULL), TAG_MUP);
if (Info == NULL)
{
ZwClose(KeyHandle);
return;
}
Status = ZwQueryValueKey(KeyHandle, &Value, KeyValueFullInformation, Info, ResultLength, &ResultLength);
}
else
{
Info = NULL;
}
ZwClose(KeyHandle);
/* And create the provider
* It will remain unregistered until FsRTL receives a registration request and forwards
* it to MUP
*/
if (NT_SUCCESS(Status))
{
ASSERT(Info != NULL);
AddUnregisteredProvider((PWSTR)((ULONG_PTR)Info + Info->DataOffset), ProviderOrder);
}
if (Info != NULL)
{
ExFreePoolWithTag(Info, TAG_MUP);
}
}
VOID
MupGetProviderInformation(VOID)
{
BOOLEAN End;
NTSTATUS Status;
HANDLE KeyHandle;
PWSTR Providers, Coma;
PKEY_VALUE_FULL_INFORMATION Info;
ULONG ResultLength, ProviderCount;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING NetworkProvider, ProviderOrder;
/* Open the registry to get the order of the providers */
RtlInitUnicodeString(&NetworkProvider, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\NetworkProvider\\Order");
InitializeObjectAttributes(&ObjectAttributes,
&NetworkProvider,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
if (!NT_SUCCESS(Status))
{
return;
}
RtlInitUnicodeString(&ProviderOrder, L"ProviderOrder");
Status = ZwQueryValueKey(KeyHandle, &ProviderOrder, KeyValueFullInformation, NULL, 0, &ResultLength);
if (Status == STATUS_BUFFER_TOO_SMALL)
{
Info = ExAllocatePoolWithTag(PagedPool, ResultLength + sizeof(UNICODE_NULL), TAG_MUP);
if (Info == NULL)
{
ZwClose(KeyHandle);
return;
}
Status = ZwQueryValueKey(KeyHandle, &ProviderOrder, KeyValueFullInformation, Info, ResultLength, &ResultLength);
}
else
{
Info = NULL;
}
ZwClose(KeyHandle);
if (NT_SUCCESS(Status))
{
ASSERT(Info != NULL);
Providers = (PWSTR)((ULONG_PTR)Info + Info->DataOffset);
End = FALSE;
ProviderCount = 0;
/* For all the providers we got (coma-separated list), just create a provider node with the right order
* The order is just the order of the list
* First has highest priority (0) and then, get lower and lower priority
* The highest number is the lowest priority
*/
do
{
Coma = wcschr(Providers, L',');
if (Coma != NULL)
{
*Coma = UNICODE_NULL;
}
else
{
End = TRUE;
}
InitializeProvider(Providers, ProviderCount);
++ProviderCount;
Providers = Coma + 1;
} while (!End);
}
if (Info != NULL)
{
ExFreePoolWithTag(Info, TAG_MUP);
}
}
PMUP_UNC
MupCheckForUnregisteredProvider(PUNICODE_STRING RedirectorDeviceName)
{
PLIST_ENTRY Entry;
PMUP_UNC UncProvider;
/* Browse the list of all the providers nodes we have */
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
{
UncProvider = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
/* If one matches the device and is not registered, that's ours! */
if (!UncProvider->Registered && RtlEqualUnicodeString(RedirectorDeviceName, &UncProvider->DeviceName, TRUE))
{
UncProvider->NodeStatus = NODE_STATUS_HEALTHY;
break;
}
}
if (Entry == &MupProviderList)
{
UncProvider = NULL;
}
ExReleaseResourceLite(&MupGlobalLock);
return UncProvider;
}
NTSTATUS
RegisterUncProvider(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
BOOLEAN New;
PMUP_FCB Fcb;
PMUP_CCB Ccb;
NTSTATUS Status;
PLIST_ENTRY Entry;
PIO_STACK_LOCATION Stack;
IO_STATUS_BLOCK IoStatusBlock;
PMUP_UNC UncProvider, ListEntry;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING RedirectorDeviceName;
OBJECT_HANDLE_INFORMATION HandleInfo;
PMUP_PROVIDER_REGISTRATION_INFO RegInfo;
DPRINT1("RegisterUncProvider(%p, %p)\n", DeviceObject, Irp);
New = FALSE;
/* Check whether providers order was already initialized */
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
if (MupOrderInitialized)
{
ExReleaseResourceLite(&MupGlobalLock);
}
else
{
/* They weren't, so do it */
MupOrderInitialized = TRUE;
ExReleaseResourceLite(&MupGlobalLock);
MupGetProviderInformation();
}
Stack = IoGetCurrentIrpStackLocation(Irp);
/* This can only happen with a volume open */
if (MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb) != NODE_TYPE_VCB)
{
Irp->IoStatus.Status = STATUS_INVALID_HANDLE;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return STATUS_INVALID_HANDLE;
}
/* Get the registration information */
RegInfo = (PMUP_PROVIDER_REGISTRATION_INFO)Irp->AssociatedIrp.SystemBuffer;
_SEH2_TRY
{
RedirectorDeviceName.Length = RegInfo->RedirectorDeviceNameLength;
RedirectorDeviceName.MaximumLength = RedirectorDeviceName.Length;
RedirectorDeviceName.Buffer = (PWSTR)((ULONG_PTR)RegInfo + RegInfo->RedirectorDeviceNameOffset);
/* Have we got already a node for it? (Like from previous init) */
UncProvider = MupCheckForUnregisteredProvider(&RedirectorDeviceName);
if (UncProvider == NULL)
{
/* If we don't, allocate a new one */
New = TRUE;
UncProvider = MupAllocateUncProvider(RegInfo->RedirectorDeviceNameLength);
if (UncProvider == NULL)
{
Status = STATUS_INVALID_USER_BUFFER;
_SEH2_LEAVE;
}
/* Set it up */
UncProvider->DeviceName.Length = RedirectorDeviceName.Length;
UncProvider->DeviceName.MaximumLength = RedirectorDeviceName.MaximumLength;
UncProvider->DeviceName.Buffer = (PWSTR)((ULONG_PTR)UncProvider + sizeof(MUP_UNC));
/* As it wasn't in registry for order, give the lowest priority possible */
UncProvider->ProviderOrder = MAXLONG;
RtlMoveMemory(UncProvider->DeviceName.Buffer, (PWSTR)((ULONG_PTR)RegInfo + RegInfo->RedirectorDeviceNameOffset), RegInfo->RedirectorDeviceNameLength);
}
/* Continue registration */
UncProvider->MailslotsSupported = RegInfo->MailslotsSupported;
++UncProvider->NodeReferences;
/* Open a handle to the device */
InitializeObjectAttributes(&ObjectAttributes,
&UncProvider->DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&UncProvider->DeviceHandle,
FILE_TRAVERSE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE);
if (NT_SUCCESS(Status))
{
Status = IoStatusBlock.Status;
}
/* And return the provider (as CCB) */
if (NT_SUCCESS(Status))
{
Stack->FileObject->FsContext2 = UncProvider;
Status = ObReferenceObjectByHandle(UncProvider->DeviceHandle, 0, NULL, KernelMode, (PVOID *)&UncProvider->FileObject, &HandleInfo);
if (!NT_SUCCESS(Status))
{
NtClose(UncProvider->DeviceHandle);
}
}
if (!NT_SUCCESS(Status))
{
MupDereferenceUncProvider(UncProvider);
}
else
{
UncProvider->DeviceObject = IoGetRelatedDeviceObject(UncProvider->FileObject);
/* Now, insert the provider in our global list
* They are sorted by order
*/
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
++MupProviderCount;
if (New)
{
for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
{
ListEntry = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
if (UncProvider->ProviderOrder < ListEntry->ProviderOrder)
{
break;
}
}
InsertTailList(Entry, &UncProvider->ProviderListEntry);
}
UncProvider->Registered = TRUE;
ExReleaseResourceLite(&MupGlobalLock);
Status = STATUS_SUCCESS;
DPRINT1("UNC provider %wZ registered\n", &UncProvider->DeviceName);
}
}
_SEH2_FINALLY
{
if (_abnormal_termination())
{
Status = STATUS_INVALID_USER_BUFFER;
}
MupDereferenceVcb((PMUP_VCB)Fcb);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
_SEH2_END;
return Status;
}
NTSTATUS
NTAPI
MupFsControl(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
Stack = IoGetCurrentIrpStackLocation(Irp);
_SEH2_TRY
{
/* MUP only understands a single FSCTL code: registering UNC provider */
if (Stack->Parameters.FileSystemControl.FsControlCode == FSCTL_MUP_REGISTER_PROVIDER)
{
/* It obviously has to come from a driver/kernelmode thread */
if (Irp->RequestorMode == UserMode)
{
Status = STATUS_ACCESS_DENIED;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
_SEH2_LEAVE;
}
Status = RegisterUncProvider(DeviceObject, Irp);
}
else
{
/* If that's an unknown FSCTL code, maybe it's for DFS, pass it */
if (!MupEnableDfs)
{
Status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
_SEH2_LEAVE;
}
Status = DfsFsdFileSystemControl(DeviceObject, Irp);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
return Status;
}
VOID
MupSetFileObject(PFILE_OBJECT FileObject,
PMUP_FCB Fcb,
PMUP_CCB Ccb)
{
FileObject->FsContext = Fcb;
FileObject->FsContext2 = Ccb;
}
NTSTATUS
MupRerouteOpen(PFILE_OBJECT FileObject,
PMUP_UNC UncProvider)
{
PWSTR FullPath;
ULONG TotalLength;
DPRINT1("Rerouting %wZ with %wZ\n", &FileObject->FileName, &UncProvider->DeviceName);
/* Get the full path name (device name first, and requested file name appended) */
TotalLength = UncProvider->DeviceName.Length + FileObject->FileName.Length;
if (TotalLength > MAXUSHORT)
{
return STATUS_NAME_TOO_LONG;
}
/* Allocate a buffer big enough */
FullPath = ExAllocatePoolWithTag(PagedPool, TotalLength, TAG_MUP);
if (FullPath == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Create the full path */
RtlMoveMemory(FullPath, UncProvider->DeviceName.Buffer, UncProvider->DeviceName.Length);
RtlMoveMemory((PWSTR)((ULONG_PTR)FullPath + UncProvider->DeviceName.Length), FileObject->FileName.Buffer, FileObject->FileName.Length);
/* And redo the path in the file object */
ExFreePoolWithTag(FileObject->FileName.Buffer, 0);
FileObject->FileName.Buffer = FullPath;
FileObject->FileName.MaximumLength = TotalLength;
FileObject->FileName.Length = FileObject->FileName.MaximumLength;
/* Ob, please reparse to open the correct file at the right place, thanks! :-) */
return STATUS_REPARSE;
}
NTSTATUS
BroadcastOpen(PIRP Irp)
{
PMUP_FCB Fcb;
HANDLE Handle;
PLIST_ENTRY Entry;
PMUP_CCB Ccb = NULL;
PMUP_UNC UncProvider;
UNICODE_STRING FullPath;
PFILE_OBJECT FileObject;
PIO_STACK_LOCATION Stack;
NTSTATUS Status, LastFailed;
ULONG TotalLength, LastOrder;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
OBJECT_HANDLE_INFORMATION HandleInfo;
BOOLEAN Locked, Referenced, CcbInitialized;
Fcb = MupCreateFcb();
if (Fcb == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Stack = IoGetCurrentIrpStackLocation(Irp);
FileObject = Stack->FileObject;
Locked = FALSE;
Referenced = FALSE;
CcbInitialized = FALSE;
LastFailed = STATUS_NO_SUCH_FILE;
LastOrder = (ULONG)-1;
_SEH2_TRY
{
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
Locked = TRUE;
/* Associate our FCB with the FO */
MupSetFileObject(FileObject, Fcb, NULL);
Fcb->FileObject = FileObject;
/* Now, broadcast the open to any UNC provider that supports mailslots */
for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
{
UncProvider = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
++UncProvider->NodeReferences;
Referenced = TRUE;
ExReleaseResourceLite(&MupGlobalLock);
Locked = FALSE;
TotalLength = UncProvider->DeviceName.Length + FileObject->FileName.Length;
if (UncProvider->MailslotsSupported && TotalLength <= MAXUSHORT)
{
/* Provide the correct name for the mailslot (ie, happened the device name of the provider) */
FullPath.Buffer = ExAllocatePoolWithTag(PagedPool, TotalLength, TAG_MUP);
if (FullPath.Buffer == NULL)
{
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
FullPath.Length = TotalLength;
FullPath.MaximumLength = TotalLength;
RtlMoveMemory(FullPath.Buffer, UncProvider->DeviceName.Buffer, UncProvider->DeviceName.Length);
RtlMoveMemory((PWSTR)((ULONG_PTR)FullPath.Buffer + UncProvider->DeviceName.Length),
FileObject->FileName.Buffer,
FileObject->FileName.Length);
/* And just forward the creation request */
InitializeObjectAttributes(&ObjectAttributes,
&FullPath,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = IoCreateFile(&Handle,
Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_SIMPLE_RIGHTS_MASK,
&ObjectAttributes,
&IoStatusBlock,
NULL,
Stack->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS,
Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS,
FILE_OPEN,
Stack->Parameters.Create.Options & FILE_VALID_SET_FLAGS,
NULL,
0,
CreateFileTypeNone,
NULL,
IO_NO_PARAMETER_CHECKING);
ExFreePoolWithTag(FullPath.Buffer, TAG_MUP);
/* If opening succeed */
if (NT_SUCCESS(Status))
{
Status = IoStatusBlock.Status;
/* Create a CCB */
Ccb = MupCreateCcb();
if (Ccb == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
/* And associated a FO to it */
if (NT_SUCCESS(Status))
{
Status = ObReferenceObjectByHandle(Handle, 0, 0, 0, (PVOID *)&Ccb->FileObject, &HandleInfo);
ZwClose(Handle);
}
}
/* If we failed, remember the last failed status of the higher priority provider */
if (!NT_SUCCESS(Status))
{
if (UncProvider->ProviderOrder <= LastOrder)
{
LastOrder = UncProvider->ProviderOrder;
LastFailed = Status;
}
}
/* Otherwise, properly attach our CCB to the mailslot */
else
{
Ccb->DeviceObject = IoGetRelatedDeviceObject(Ccb->FileObject);
Ccb->Fcb = Fcb;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
Locked = TRUE;
++Fcb->NodeReferences;
ExReleaseResourceLite(&MupGlobalLock);
Locked = FALSE;
CcbInitialized = TRUE;
InsertTailList(&Fcb->CcbList, &Ccb->CcbListEntry);
}
}
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
Locked = TRUE;
MupDereferenceUncProvider(UncProvider);
Referenced = FALSE;
}
ExReleaseResourceLite(&MupGlobalLock);
Locked = FALSE;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* If we at least opened one mailslot, return success */
Status = (CcbInitialized ? STATUS_SUCCESS : LastFailed);
if (Referenced)
{
MupDereferenceUncProvider(UncProvider);
}
if (Locked)
{
ExReleaseResourceLite(&MupGlobalLock);
}
/* In case of failure, don't leak CCB */
if (!NT_SUCCESS(Status) && Ccb != NULL)
{
MupFreeNode(Ccb);
}
return Status;
}
PIRP
MupBuildIoControlRequest(PFILE_OBJECT FileObject,
PVOID Context,
ULONG MajorFunction,
ULONG IoctlCode,
PVOID InputBuffer,
ULONG InputBufferSize,
PVOID OutputBuffer,
ULONG OutputBufferSize,
PIO_COMPLETION_ROUTINE CompletionRoutine)
{
PIRP Irp;
PIO_STACK_LOCATION Stack;
PDEVICE_OBJECT DeviceObject;
if (InputBuffer == NULL)
{
return NULL;
}
/* Get the device object */
DeviceObject = IoGetRelatedDeviceObject(FileObject);
/* Allocate the IRP (with one more location for us */
Irp = IoAllocateIrp(DeviceObject->StackSize + 1, FALSE);
if (Irp == NULL)
{
return NULL;
}
/* Skip our location */
IoSetNextIrpStackLocation(Irp);
/* Setup the IRP */
Irp->Tail.Overlay.OriginalFileObject = FileObject;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
IoSetCompletionRoutine(Irp, CompletionRoutine, Context, TRUE, TRUE, TRUE);
/* Setup the stack */
Stack = IoGetNextIrpStackLocation(Irp);
Stack->MajorFunction = MajorFunction;
Stack->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferSize;
Stack->Parameters.DeviceIoControl.InputBufferLength = InputBufferSize;
Stack->Parameters.DeviceIoControl.IoControlCode = IoctlCode;
Stack->MinorFunction = 0;
Stack->FileObject = FileObject;
Stack->DeviceObject = DeviceObject;
switch (IO_METHOD_FROM_CTL_CODE(IoctlCode))
{
case METHOD_BUFFERED:
/* If it's buffered, just pass the buffers we got */
Irp->MdlAddress = NULL;
Irp->AssociatedIrp.SystemBuffer = InputBuffer;
Irp->UserBuffer = OutputBuffer;
Irp->Flags = IRP_BUFFERED_IO;
if (OutputBuffer != NULL)
{
Irp->Flags |= IRP_INPUT_OPERATION;
}
break;
case METHOD_IN_DIRECT:
case METHOD_OUT_DIRECT:
/* Otherwise, allocate an MDL */
if (IoAllocateMdl(InputBuffer, InputBufferSize, FALSE, FALSE, Irp) == NULL)
{
IoFreeIrp(Irp);
return NULL;
}
Irp->AssociatedIrp.SystemBuffer = InputBuffer;
Irp->Flags = IRP_BUFFERED_IO;
MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess);
break;
case METHOD_NEITHER:
/* Or pass the buffers */
Irp->UserBuffer = OutputBuffer;
Irp->MdlAddress = NULL;
Irp->AssociatedIrp.SystemBuffer = NULL;
Stack->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer;
break;
}
return Irp;
}
VOID
MupFreeMasterQueryContext(PMUP_MQC MasterQueryContext)
{
ExDeleteResourceLite(&MasterQueryContext->QueryPathListLock);
ExFreePoolWithTag(MasterQueryContext, TAG_MUP);
}
NTSTATUS
MupDereferenceMasterQueryContext(PMUP_MQC MasterQueryContext)
{
LONG References;
NTSTATUS Status;
BOOLEAN KeepExtraRef;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
--MasterQueryContext->NodeReferences;
References = MasterQueryContext->NodeReferences;
ExReleaseResourceLite(&MupGlobalLock);
if (References != 0)
{
DPRINT("Still having refs (%ld)\n", References);
return STATUS_PENDING;
}
/* We HAVE an IRP to complete. It cannot be NULL
* Please, help preserving kittens, don't provide NULL IRPs.
*/
if (MasterQueryContext->Irp == NULL)
{
KeBugCheck(FILE_SYSTEM);
}
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
RemoveEntryList(&MasterQueryContext->MQCListEntry);
ExReleaseResourceLite(&MupGlobalLock);
ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
KeepExtraRef = MasterQueryContext->Prefix->KeepExtraRef;
MupDereferenceKnownPrefix(MasterQueryContext->Prefix);
/* We found a provider? */
if (MasterQueryContext->LatestProvider != NULL)
{
/* With a successful status? */
if (MasterQueryContext->LatestStatus == STATUS_SUCCESS)
{
/* Then, it's time to reroute, someone accepted to handle the file creation request! */
if (!KeepExtraRef)
{
MupDereferenceKnownPrefix(MasterQueryContext->Prefix);
}
ExReleaseResourceLite(&MupPrefixTableLock);
/* Reroute & complete :-) */
Status = MupRerouteOpen(MasterQueryContext->FileObject, MasterQueryContext->LatestProvider);
goto Complete;
}
else
{
MupDereferenceUncProvider(MasterQueryContext->LatestProvider);
}
}
MupDereferenceKnownPrefix(MasterQueryContext->Prefix);
ExReleaseResourceLite(&MupPrefixTableLock);
/* Return the highest failed status we had */
Status = MasterQueryContext->LatestStatus;
Complete:
/* In finally, complete the IRP for real! */
MasterQueryContext->Irp->IoStatus.Status = Status;
IoCompleteRequest(MasterQueryContext->Irp, IO_DISK_INCREMENT);
MasterQueryContext->Irp = NULL;
MupFreeMasterQueryContext(MasterQueryContext);
return Status;
}
NTSTATUS
NTAPI
QueryPathCompletionRoutine(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context)
{
PMUP_PFX Prefix;
ULONG LatestPos, Pos;
PWSTR AcceptedPrefix;
PMUP_MQC MasterQueryContext;
NTSTATUS Status, TableStatus;
PQUERY_PATH_CONTEXT QueryContext;
PQUERY_PATH_RESPONSE QueryResponse;
/* Get all the data from our query to the provider */
QueryContext = (PQUERY_PATH_CONTEXT)Context;
QueryResponse = (PQUERY_PATH_RESPONSE)QueryContext->QueryPathRequest;
MasterQueryContext = QueryContext->MasterQueryContext;
Status = Irp->IoStatus.Status;
DPRINT("Reply from %wZ: %u (Status: %lx)\n", &QueryContext->UncProvider->DeviceName, QueryResponse->LengthAccepted, Status);
ExAcquireResourceExclusiveLite(&MasterQueryContext->QueryPathListLock, TRUE);
RemoveEntryList(&QueryContext->QueryPathListEntry);
/* If the driver returned a success, and an acceptance length */
if (NT_SUCCESS(Status) && QueryResponse->LengthAccepted > 0)
{
Prefix = MasterQueryContext->Prefix;
/* Check if we already found a provider from a previous iteration */
if (MasterQueryContext->LatestProvider != NULL)
{
/* If the current provider has a lower priority (ie, a greater order), then, bailout and keep previous one */
if (QueryContext->UncProvider->ProviderOrder >= MasterQueryContext->LatestProvider->ProviderOrder)
{
MupDereferenceUncProvider(QueryContext->UncProvider);
goto Cleanup;
}
/* Otherwise, if the prefix was in the prefix table, just drop it:
* we have a provider which supersedes the accepted prefix, so leave
* room for the new prefix/provider
*/
ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
if (Prefix->InTable)
{
RtlRemoveUnicodePrefix(&MupPrefixTable, &Prefix->PrefixTableEntry);
RemoveEntryList(&Prefix->PrefixListEntry);
Prefix->InTable = FALSE;
}
ExReleaseResourceLite(&MupPrefixTableLock);
Prefix->KeepExtraRef = FALSE;
/* Release data associated with the current prefix, if any
* We'll renew them with the new accepted prefix
*/
if (Prefix->AcceptedPrefix.Length != 0 && Prefix->AcceptedPrefix.Buffer != NULL)
{
ExFreePoolWithTag(Prefix->AcceptedPrefix.Buffer, TAG_MUP);
Prefix->AcceptedPrefix.MaximumLength = 0;
Prefix->AcceptedPrefix.Length = 0;
Prefix->AcceptedPrefix.Buffer = NULL;
Prefix->ExternalAlloc = FALSE;
}
/* If there was also a provider, drop it, the new one
* is different
*/
if (Prefix->UncProvider != NULL)
{
MupDereferenceUncProvider(Prefix->UncProvider);
Prefix->UncProvider = NULL;
}
}
/* Now, set our information about the provider that accepted the prefix */
MasterQueryContext->LatestProvider = QueryContext->UncProvider;
MasterQueryContext->LatestStatus = Status;
if (MasterQueryContext->FileObject->FsContext2 != (PVOID)DFS_DOWNLEVEL_OPEN_CONTEXT)
{
/* Allocate a buffer for the prefix */
AcceptedPrefix = ExAllocatePoolWithTag(PagedPool, QueryResponse->LengthAccepted, TAG_MUP);
if (AcceptedPrefix == NULL)
{
Prefix->InTable = FALSE;
}
else
{
/* Set it up to the accepted length */
RtlMoveMemory(AcceptedPrefix, MasterQueryContext->FileObject->FileName.Buffer, QueryResponse->LengthAccepted);
Prefix->UncProvider = MasterQueryContext->LatestProvider;
Prefix->AcceptedPrefix.Buffer = AcceptedPrefix;
Prefix->AcceptedPrefix.Length = QueryResponse->LengthAccepted;
Prefix->AcceptedPrefix.MaximumLength = QueryResponse->LengthAccepted;
Prefix->ExternalAlloc = TRUE;
/* Insert the accepted prefix in the table of known prefixes */
DPRINT1("%wZ accepted %wZ\n", &Prefix->UncProvider->DeviceName, &Prefix->AcceptedPrefix);
ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
if (RtlInsertUnicodePrefix(&MupPrefixTable, &Prefix->AcceptedPrefix, &Prefix->PrefixTableEntry))
{
InsertHeadList(&MupPrefixList, &Prefix->PrefixListEntry);
Prefix->InTable = TRUE;
Prefix->KeepExtraRef = TRUE;
}
else
{
Prefix->InTable = FALSE;
}
ExReleaseResourceLite(&MupPrefixTableLock);
}
}
}
else
{
MupDereferenceUncProvider(QueryContext->UncProvider);
/* We failed and didn't find any provider over the latest iterations */
if (MasterQueryContext->LatestProvider == NULL)
{
/* If we had a success though (broken provider?) set our failed status */
if (NT_SUCCESS(MasterQueryContext->LatestStatus))
{
MasterQueryContext->LatestStatus = Status;
}
else
{
TableStatus = MupOrderedErrorList[0];
LatestPos = 0;
/* Otherwise, time to compare statuses, between the latest failed
* and the current failure.
* We have an order table of failed status: the deeper you go in the
* table, the more the error is critical.
* Our goal is to return the most critical status that was returned by
* any of the providers
*/
/* Look for latest status position */
while (TableStatus != 0 && TableStatus != MasterQueryContext->LatestStatus)
{
++LatestPos;
TableStatus = MupOrderedErrorList[LatestPos];
}
/* If at pos 0, the new status is likely more critical */
if (LatestPos == 0)
{
MasterQueryContext->LatestStatus = Status;
}
else
{
/* Otherwise, find position of the new status in the table */
Pos = 0;
do
{
if (Status == MupOrderedErrorList[Pos])
{
break;
}
++Pos;
}
while (Pos < LatestPos);
/* If it has a higher position (more critical), return it */
if (Pos >= LatestPos)
{
MasterQueryContext->LatestStatus = Status;
}
}
}
}
}
Cleanup:
ExFreePoolWithTag(QueryResponse, TAG_MUP);
ExFreePoolWithTag(QueryContext, TAG_MUP);
IoFreeIrp(Irp);
ExReleaseResourceLite(&MasterQueryContext->QueryPathListLock);
MupDereferenceMasterQueryContext(MasterQueryContext);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
CreateRedirectedFile(PIRP Irp,
PFILE_OBJECT FileObject,
PIO_SECURITY_CONTEXT SecurityContext)
{
LONG Len;
WCHAR Cur;
PWSTR Name;
PIRP QueryIrp;
NTSTATUS Status;
PMUP_PFX Prefix;
PLIST_ENTRY Entry;
PMUP_UNC UncProvider;
PIO_STACK_LOCATION Stack;
LARGE_INTEGER CurrentTime;
PMUP_MQC MasterQueryContext;
PQUERY_PATH_CONTEXT QueryContext;
PQUERY_PATH_REQUEST QueryPathRequest;
PUNICODE_PREFIX_TABLE_ENTRY TableEntry;
BOOLEAN Locked, Referenced, BreakOnFirst;
/* We cannot open a file without a name */
if (FileObject->FileName.Length == 0)
{
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
DPRINT1("Request for opening: %wZ\n", &FileObject->FileName);
Referenced = FALSE;
BreakOnFirst = TRUE;
Status = STATUS_BAD_NETWORK_PATH;
ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
/* First, try to see if that's a prefix we already know */
TableEntry = RtlFindUnicodePrefix(&MupPrefixTable, &FileObject->FileName, 1);
if (TableEntry != NULL)
{
Prefix = CONTAINING_RECORD(TableEntry, MUP_PFX, PrefixTableEntry);
DPRINT("Matching prefix found: %wZ\n", &Prefix->AcceptedPrefix);
/* If so, check whether the prefix is still valid */
KeQuerySystemTime(&CurrentTime);
if (Prefix->ValidityTimeout.QuadPart < CurrentTime.QuadPart)
{
/* It is: so, update its validity period and reroute file opening */
MupCalculateTimeout(&Prefix->ValidityTimeout);
Status = MupRerouteOpen(FileObject, Prefix->UncProvider);
ExReleaseResourceLite(&MupPrefixTableLock);
if (Status == STATUS_REPARSE)
{
Irp->IoStatus.Information = FILE_SUPERSEDED;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return Status;
}
/* When here, we found a matching prefix, but expired, remove it from the table
* We'll redo a full search
*/
if (Prefix->InTable)
{
MupRemoveKnownPrefixEntry(Prefix);
}
}
ExReleaseResourceLite(&MupPrefixTableLock);
Stack = IoGetCurrentIrpStackLocation(Irp);
/* First of all, start looking for a mailslot */
if (FileObject->FileName.Buffer[0] == L'\\' && Stack->MajorFunction != IRP_MJ_CREATE)
{
Name = &FileObject->FileName.Buffer[1];
Len = FileObject->FileName.Length;
/* Skip the remote destination name */
do
{
Len -= sizeof(WCHAR);
if (Len <= 0)
{
break;
}
Cur = *Name;
++Name;
} while (Cur != L'\\');
Len -= sizeof(WCHAR);
/* If we still have room for "Mailslot" to fit */
if (Len >= (sizeof(L"Mailslot") - sizeof(UNICODE_NULL)))
{
/* Get the len in terms of chars count */
Len /= sizeof(WCHAR);
if (Len > ((sizeof(L"Mailslot") - sizeof(UNICODE_NULL)) / sizeof(WCHAR)))
{
Len = (sizeof(L"Mailslot") - sizeof(UNICODE_NULL)) / sizeof(WCHAR);
}
/* It's indeed a mailslot opening! */
if (_wcsnicmp(Name, L"Mailslot", Len) == 0)
{
/* Broadcast open */
Status = BroadcastOpen(Irp);
if (Status == STATUS_REPARSE)
{
Irp->IoStatus.Information = FILE_SUPERSEDED;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return Status;
}
}
}
/* Ok, at that point, that's a regular MUP opening (if no DFS) */
if (!MupEnableDfs || FileObject->FsContext2 == (PVOID)DFS_DOWNLEVEL_OPEN_CONTEXT)
{
/* We won't complete immediately */
IoMarkIrpPending(Irp);
/* Allocate a new prefix for our search */
Prefix = MupAllocatePrefixEntry(0);
if (Prefix == NULL)
{
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return STATUS_PENDING;
}
/* Allocate a context for our search */
MasterQueryContext = MupAllocateMasterQueryContext();
if (MasterQueryContext == NULL)
{
MupFreeNode(Prefix);
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return STATUS_PENDING;
}
MasterQueryContext->Irp = Irp;
MasterQueryContext->FileObject = FileObject;
MasterQueryContext->LatestProvider = NULL;
MasterQueryContext->Prefix = Prefix;
MasterQueryContext->LatestStatus = STATUS_BAD_NETWORK_PATH;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
InsertTailList(&MupMasterQueryList, &MasterQueryContext->MQCListEntry);
++Prefix->NodeReferences;
ExReleaseResourceLite(&MupGlobalLock);
_SEH2_TRY
{
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
Locked = TRUE;
/* Now, we will browse all the providers we know, to ask for their accepted prefix regarding the path */
for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
{
UncProvider = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
++UncProvider->NodeReferences;
Referenced = TRUE;
ExReleaseResourceLite(&MupGlobalLock);
Locked = FALSE;
/* We will obviously only query registered providers */
if (UncProvider->Registered)
{
/* We will issue an IOCTL_REDIR_QUERY_PATH, so allocate input buffer */
QueryPathRequest = ExAllocatePoolWithTag(PagedPool, FileObject->FileName.Length + sizeof(QUERY_PATH_REQUEST), TAG_MUP);
if (QueryPathRequest == NULL)
{
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
/* Allocate a context for IRP completion routine
* In case a prefix matches the path, the reroute will happen
* in the completion routine, when we have return from the provider
*/
QueryContext = ExAllocatePoolWithTag(PagedPool, sizeof(QUERY_PATH_CONTEXT), TAG_MUP);
if (QueryContext == NULL)
{
ExFreePoolWithTag(QueryPathRequest, TAG_MUP);
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
InitializeListHead(&QueryContext->QueryPathListEntry);
QueryContext->MasterQueryContext = MasterQueryContext;
QueryContext->QueryPathRequest = QueryPathRequest;
QueryPathRequest->PathNameLength = FileObject->FileName.Length;
QueryPathRequest->SecurityContext = SecurityContext;
RtlMoveMemory(QueryPathRequest->FilePathName, FileObject->FileName.Buffer, FileObject->FileName.Length);
/* Build our IRP for the query */
QueryIrp = MupBuildIoControlRequest(UncProvider->FileObject,
QueryContext,
IRP_MJ_DEVICE_CONTROL,
IOCTL_REDIR_QUERY_PATH,
QueryPathRequest,
FileObject->FileName.Length + sizeof(QUERY_PATH_REQUEST),
QueryPathRequest,
sizeof(QUERY_PATH_RESPONSE),
QueryPathCompletionRoutine);
if (QueryIrp == NULL)
{
ExFreePoolWithTag(QueryContext, TAG_MUP);
ExFreePoolWithTag(QueryPathRequest, TAG_MUP);
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
QueryIrp->RequestorMode = KernelMode;
QueryContext->UncProvider = UncProvider;
QueryContext->Irp = QueryIrp;
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
++UncProvider->NodeReferences;
++MasterQueryContext->NodeReferences;
ExReleaseResourceLite(&MupGlobalLock);
ExAcquireResourceExclusiveLite(&MasterQueryContext->QueryPathListLock, TRUE);
InsertTailList(&MasterQueryContext->QueryPathList, &QueryContext->QueryPathListEntry);
ExReleaseResourceLite(&MasterQueryContext->QueryPathListLock);
/* Query the provider !*/
DPRINT1("Requesting UNC provider: %wZ\n", &UncProvider->DeviceName);
DPRINT("Calling: %wZ\n", &UncProvider->DeviceObject->DriverObject->DriverName);
Status = IoCallDriver(UncProvider->DeviceObject, QueryIrp);
}
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
Locked = TRUE;
/* We're done with that provider */
MupDereferenceUncProvider(UncProvider);
Referenced = FALSE;
/* If query went fine on the first request, just break and leave */
if (BreakOnFirst && Status == STATUS_SUCCESS)
{
break;
}
BreakOnFirst = FALSE;
}
}
_SEH2_FINALLY
{
if (_abnormal_termination())
{
MasterQueryContext->LatestStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (Referenced)
{
MupDereferenceUncProvider(UncProvider);
}
if (Locked)
{
ExReleaseResourceLite(&MupGlobalLock);
}
MupDereferenceMasterQueryContext(MasterQueryContext);
Status = STATUS_PENDING;
}
_SEH2_END;
}
else
{
UNIMPLEMENTED;
Status = STATUS_NOT_IMPLEMENTED;
}
return Status;
}
NTSTATUS
OpenMupFileSystem(PMUP_VCB Vcb,
PFILE_OBJECT FileObject,
ACCESS_MASK DesiredAccess,
USHORT ShareAccess)
{
NTSTATUS Status;
DPRINT1("Opening MUP\n");
ExAcquireResourceExclusiveLite(&MupVcbLock, TRUE);
_SEH2_TRY
{
/* Update share access, increase reference count, and associated VCB to the FO, that's it! */
Status = IoCheckShareAccess(DesiredAccess, ShareAccess, FileObject, &Vcb->ShareAccess, TRUE);
if (NT_SUCCESS(Status))
{
++Vcb->NodeReferences;
MupSetFileObject(FileObject, (PMUP_FCB)Vcb, NULL);
Status = STATUS_SUCCESS;
}
}
_SEH2_FINALLY
{
ExReleaseResourceLite(&MupVcbLock);
}
_SEH2_END;
return Status;
}
NTSTATUS
NTAPI
MupCreate(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
PFILE_OBJECT FileObject, RelatedFileObject;
FsRtlEnterFileSystem();
_SEH2_TRY
{
/* If DFS is enabled, check if that's for DFS and is so relay */
if (MupEnableDfs && (DeviceObject->DeviceType == FILE_DEVICE_DFS || DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM))
{
Status = DfsFsdCreate(DeviceObject, Irp);
}
else
{
Stack = IoGetCurrentIrpStackLocation(Irp);
FileObject = Stack->FileObject;
RelatedFileObject = FileObject->RelatedFileObject;
/* If we have a file name or if the associated FCB of the related FO isn't the VCB, then, it's a regular opening */
if (FileObject->FileName.Length != 0 || (RelatedFileObject != NULL && ((PMUP_FCB)(RelatedFileObject->FsContext))->NodeType != NODE_TYPE_VCB))
{
Status = CreateRedirectedFile(Irp, FileObject, Stack->Parameters.Create.SecurityContext);
}
/* Otherwise, it's just a volume open */
else
{
Status = OpenMupFileSystem(DeviceObject->DeviceExtension,
FileObject,
Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess);
Irp->IoStatus.Information = FILE_OPENED;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
_SEH2_END;
FsRtlExitFileSystem();
return Status;
}
VOID
MupCloseUncProvider(PMUP_UNC UncProvider)
{
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
/* If the node was still valid, reregister the UNC provider */
if (UncProvider->NodeStatus == NODE_STATUS_HEALTHY)
{
UncProvider->NodeStatus = NODE_STATUS_CLEANUP;
UncProvider->Registered = FALSE;
ExReleaseResourceLite(&MupGlobalLock);
if (UncProvider->FileObject != NULL)
{
ZwClose(UncProvider->DeviceHandle);
ObDereferenceObject(UncProvider->FileObject);
}
}
else
{
ExReleaseResourceLite(&MupGlobalLock);
}
}
NTSTATUS
NTAPI
MupCleanup(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
ULONG Type;
PMUP_FCB Fcb;
PMUP_CCB Ccb;
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
/* If DFS is enabled, check if that's for DFS and is so relay */
if (MupEnableDfs)
{
if (DeviceObject->DeviceType == FILE_DEVICE_DFS || DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM)
{
return DfsFsdCleanup(DeviceObject, Irp);
}
}
FsRtlEnterFileSystem();
_SEH2_TRY
{
Stack = IoGetCurrentIrpStackLocation(Irp);
Type = MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb);
switch (Type)
{
case NODE_TYPE_VCB:
/* If we got a VCB, clean it up */
MupCleanupVcb(DeviceObject, Irp, (PMUP_VCB)Fcb);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
MupDereferenceVcb((PMUP_VCB)Fcb);
/* If Ccb is not null, then, it's a UNC provider node */
if (Ccb)
{
/* Close it, and dereference */
MupCloseUncProvider((PMUP_UNC)Ccb);
MupDereferenceUncProvider((PMUP_UNC)Ccb);
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
--MupProviderCount;
ExReleaseResourceLite(&MupGlobalLock);
}
Status = STATUS_SUCCESS;
break;
case NODE_TYPE_FCB:
/* If the node wasn't already cleaned, do it */
if (Fcb->NodeStatus == NODE_STATUS_HEALTHY)
{
MupCleanupFcb(DeviceObject, Irp, Fcb);
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_INVALID_HANDLE;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
MupDereferenceFcb(Fcb);
break;
default:
Status = STATUS_INVALID_HANDLE;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
break;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
FsRtlExitFileSystem();
return Status;
}
NTSTATUS
MupCloseVcb(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PMUP_VCB Vcb,
PFILE_OBJECT FileObject)
{
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
/* Remove FCB, UNC from FO */
MupSetFileObject(FileObject, NULL, NULL);
MupDereferenceVcb(Vcb);
ExReleaseResourceLite(&MupGlobalLock);
return STATUS_SUCCESS;
}
NTSTATUS
MupCloseFcb(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PMUP_FCB Fcb,
PFILE_OBJECT FileObject)
{
ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
/* Remove FCB, CCB from FO */
MupSetFileObject(FileObject, NULL, NULL);
MupDereferenceFcb(Fcb);
ExReleaseResourceLite(&MupGlobalLock);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
MupClose(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PMUP_FCB Fcb;
PMUP_CCB Ccb;
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
/* If DFS is enabled, check if that's for DFS and is so relay */
if (MupEnableDfs)
{
if (DeviceObject->DeviceType == FILE_DEVICE_DFS || DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM)
{
return DfsFsdClose(DeviceObject, Irp);
}
}
FsRtlEnterFileSystem();
_SEH2_TRY
{
/* Get our internal structures from FO */
Stack = IoGetCurrentIrpStackLocation(Irp);
MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb);
if (Fcb == NULL)
{
Status = STATUS_INVALID_HANDLE;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
_SEH2_LEAVE;
}
/* If we got the VCB, that's a volume close */
if (Fcb->NodeType == NODE_TYPE_VCB)
{
Status = MupCloseVcb(DeviceObject, Irp, (PMUP_VCB)Fcb, Stack->FileObject);
}
/* Otherwise close the FCB */
else if (Fcb->NodeType == NODE_TYPE_FCB)
{
MupDereferenceFcb(Fcb);
Status = MupCloseFcb(DeviceObject, Irp, Fcb, Stack->FileObject);
}
else
{
Status = STATUS_INVALID_HANDLE;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
_SEH2_LEAVE;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
FsRtlExitFileSystem();
return Status;
}
VOID
NTAPI
MupUnload(PDRIVER_OBJECT DriverObject)
{
IoDeleteDevice(mupDeviceObject);
if (MupEnableDfs)
{
DfsUnload(DriverObject);
}
MupUninitializeData();
}
/*
* FUNCTION: Called by the system to initialize the driver
* ARGUMENTS:
* DriverObject = object describing this driver
* RegistryPath = path to our configuration entries
* RETURNS: Success or failure
*/
INIT_SECTION
NTSTATUS
NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
{
NTSTATUS Status;
UNICODE_STRING MupString;
PDEVICE_OBJECT DeviceObject;
/* Only initialize global state of the driver
* Other inits will happen when required
*/
MupInitializeData();
/* Check if DFS is disabled */
MupEnableDfs = MuppIsDfsEnabled();
/* If it's not disabled but when cannot init, disable it */
if (MupEnableDfs && !NT_SUCCESS(DfsDriverEntry(DriverObject, RegistryPath)))
{
MupEnableDfs = FALSE;
}
/* Create the MUP device */
RtlInitUnicodeString(&MupString, L"\\Device\\Mup");
Status = IoCreateDevice(DriverObject, sizeof(MUP_VCB), &MupString, FILE_DEVICE_MULTI_UNC_PROVIDER, 0, FALSE, &DeviceObject);
if (!NT_SUCCESS(Status))
{
if (MupEnableDfs)
{
DfsUnload(DriverObject);
}
MupUninitializeData();
return Status;
}
/* Set our MJ */
DriverObject->DriverUnload = MupUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = MupCreate;
DriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = MupCreate;
DriverObject->MajorFunction[IRP_MJ_CREATE_MAILSLOT] = MupCreate;
DriverObject->MajorFunction[IRP_MJ_WRITE] = MupForwardIoRequest;
DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = MupFsControl;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MupCleanup;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MupClose;
/* And finish init */
mupDeviceObject = DeviceObject;
MupInitializeVcb(DeviceObject->DeviceExtension);
return STATUS_SUCCESS;
}