[MUP]
[reactos.git] / reactos / drivers / filesystems / mup / mup.c
index 9fa0617..84f318a 100644 (file)
 /*
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
- * FILE:             drivers/fs/mup/mup.c
+ * FILE:             drivers/filesystems/mup/mup.c
  * PURPOSE:          Multi UNC Provider
  * PROGRAMMER:       Eric Kohl
+ *                   Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 #define NDEBUG
 #include <debug.h>
 
-/* GLOBALS *****************************************************************/
+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:
  *           RegistryPath = path to our configuration entries
  * RETURNS: Success or failure
  */
-NTSTATUS NTAPI
+INIT_SECTION
+NTSTATUS
+NTAPI
 DriverEntry(PDRIVER_OBJECT DriverObject,
             PUNICODE_STRING RegistryPath)
 {
-    PDEVICE_OBJECT DeviceObject;
     NTSTATUS Status;
-    UNICODE_STRING DeviceName;
+    UNICODE_STRING MupString;
+    PDEVICE_OBJECT DeviceObject;
+
+    /* Only initialize global state of the driver
+     * Other inits will happen when required
+     */
+    MupInitializeData();
 
-    DPRINT("MUP 0.0.1\n");
+    /* 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;
+    }
 
-    RtlInitUnicodeString(&DeviceName,
-                         L"\\Device\\Mup");
-    Status = IoCreateDevice(DriverObject,
-                            sizeof(DEVICE_EXTENSION),
-                            &DeviceName,
-                            FILE_DEVICE_MULTI_UNC_PROVIDER,
-                            0,
-                            FALSE,
-                            &DeviceObject);
+    /* 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;
     }
 
-    /* Initialize driver data */
-    DeviceObject->Flags |= DO_DIRECT_IO;
-//    DriverObject->MajorFunction[IRP_MJ_CLOSE] = NtfsClose;
+    /* 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_READ] = NtfsRead;
-//    DriverObject->MajorFunction[IRP_MJ_WRITE] = NtfsWrite;
-//    DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] =
-//        NtfsFileSystemControl;
-//    DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] =
-//        NtfsDirectoryControl;
-//    DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] =
-//        NtfsQueryInformation;
-//    DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] =
-//        NtfsQueryVolumeInformation;
-//    DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] =
-//        NtfsSetVolumeInformation;
-
-    DriverObject->DriverUnload = NULL;
-
-
-    /* Initialize global data */
-//    DeviceExtensionNtfsGlobalData = DeviceObject->DeviceExtension;
-//    RtlZeroMemory(NtfsGlobalData,
-//                  sizeof(NTFS_GLOBAL_DATA));
-//    NtfsGlobalData->DriverObject = DriverObject;
-//    NtfsGlobalData->DeviceObject = DeviceObject;
+    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;
 }
-