xref: /reactos/drivers/filesystems/mup/mup.c (revision 6d8aafb6)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:        See COPYING in the top level directory
21  * PROJECT:          ReactOS kernel
22  * FILE:             drivers/filesystems/mup/mup.c
23  * PURPOSE:          Multi UNC Provider
24  * PROGRAMMER:       Eric Kohl
25  *                   Pierre Schweitzer (pierre@reactos.org)
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include "mup.h"
31 
32 #define NDEBUG
33 #include <debug.h>
34 
35 CODE_SEG("INIT")
36 NTSTATUS
37 NTAPI
38 DriverEntry(
39     PDRIVER_OBJECT DriverObject,
40     PUNICODE_STRING RegistryPath
41 );
42 
43 CODE_SEG("INIT")
44 VOID
45 MupInitializeData(
46     VOID
47 );
48 
49 CODE_SEG("INIT")
50 VOID
51 MupInitializeVcb(
52     PMUP_VCB Vcb
53 );
54 
55 #if defined(ALLOC_PRAGMA)
56 #pragma alloc_text(INIT, DriverEntry)
57 #pragma alloc_text(INIT, MupInitializeData)
58 #pragma alloc_text(INIT, MupInitializeVcb)
59 #endif
60 
61 ERESOURCE MupGlobalLock;
62 ERESOURCE MupPrefixTableLock;
63 ERESOURCE MupCcbListLock;
64 ERESOURCE MupVcbLock;
65 LIST_ENTRY MupProviderList;
66 LIST_ENTRY MupPrefixList;
67 LIST_ENTRY MupMasterQueryList;
68 UNICODE_PREFIX_TABLE MupPrefixTable;
69 LARGE_INTEGER MupKnownPrefixTimeout;
70 ULONG MupProviderCount;
71 BOOLEAN MupOrderInitialized;
72 BOOLEAN MupEnableDfs;
73 PDEVICE_OBJECT mupDeviceObject;
74 NTSTATUS MupOrderedErrorList[] = { STATUS_UNSUCCESSFUL,
75                                    STATUS_INVALID_PARAMETER,
76                                    STATUS_REDIRECTOR_NOT_STARTED,
77                                    STATUS_BAD_NETWORK_NAME,
78                                    STATUS_BAD_NETWORK_PATH, 0 };
79 
80 /* FUNCTIONS ****************************************************************/
81 
82 CODE_SEG("INIT")
83 VOID
84 MupInitializeData(VOID)
85 {
86     ExInitializeResourceLite(&MupGlobalLock);
87     ExInitializeResourceLite(&MupPrefixTableLock);
88     ExInitializeResourceLite(&MupCcbListLock);
89     ExInitializeResourceLite(&MupVcbLock);
90     MupProviderCount = 0;
91     InitializeListHead(&MupProviderList);
92     InitializeListHead(&MupPrefixList);
93     InitializeListHead(&MupMasterQueryList);
94     RtlInitializeUnicodePrefix(&MupPrefixTable);
95     MupKnownPrefixTimeout.QuadPart = 9000000000LL;
96     MupOrderInitialized = FALSE;
97 }
98 
99 VOID
100 MupUninitializeData()
101 {
102   ExDeleteResourceLite(&MupGlobalLock);
103   ExDeleteResourceLite(&MupPrefixTableLock);
104   ExDeleteResourceLite(&MupCcbListLock);
105   ExDeleteResourceLite(&MupVcbLock);
106 }
107 
108 CODE_SEG("INIT")
109 VOID
110 MupInitializeVcb(PMUP_VCB Vcb)
111 {
112     RtlZeroMemory(Vcb, sizeof(MUP_VCB));
113     Vcb->NodeType = NODE_TYPE_VCB;
114     Vcb->NodeStatus = NODE_STATUS_HEALTHY;
115     Vcb->NodeReferences = 1;
116     Vcb->NodeSize = sizeof(MUP_VCB);
117 }
118 
119 BOOLEAN
120 MuppIsDfsEnabled(VOID)
121 {
122     HANDLE Key;
123     ULONG Length;
124     NTSTATUS Status;
125     UNICODE_STRING KeyName;
126     OBJECT_ATTRIBUTES ObjectAttributes;
127     struct
128     {
129         KEY_VALUE_PARTIAL_INFORMATION KeyInfo;
130         ULONG KeyValue;
131     } KeyQueryOutput;
132 
133     /* You recognize FsRtlpIsDfsEnabled()! Congratz :-) */
134     KeyName.Buffer = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup";
135     KeyName.Length = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup") - sizeof(UNICODE_NULL);
136     KeyName.MaximumLength = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Mup");
137 
138     /* Simply query registry to know whether we have to disable DFS.
139      * Unless explicitly stated in registry, we will try to enable DFS.
140      */
141     InitializeObjectAttributes(&ObjectAttributes,
142                                &KeyName,
143                                OBJ_CASE_INSENSITIVE,
144                                NULL,
145                                NULL);
146 
147     Status = ZwOpenKey(&Key, KEY_READ, &ObjectAttributes);
148     if (!NT_SUCCESS(Status))
149     {
150         return TRUE;
151     }
152 
153     KeyName.Buffer = L"DisableDfs";
154     KeyName.Length = sizeof(L"DisableDfs") - sizeof(UNICODE_NULL);
155     KeyName.MaximumLength = sizeof(L"DisableDfs");
156 
157     Status = ZwQueryValueKey(Key, &KeyName, KeyValuePartialInformation, &KeyQueryOutput, sizeof(KeyQueryOutput), &Length);
158     ZwClose(Key);
159     if (!NT_SUCCESS(Status) || KeyQueryOutput.KeyInfo.Type != REG_DWORD)
160     {
161         return TRUE;
162     }
163 
164     return ((ULONG_PTR)KeyQueryOutput.KeyInfo.Data != 1);
165 }
166 
167 VOID
168 MupCalculateTimeout(PLARGE_INTEGER EntryTime)
169 {
170     LARGE_INTEGER CurrentTime;
171 
172     /* Just get now + our allowed timeout */
173     KeQuerySystemTime(&CurrentTime);
174     EntryTime->QuadPart = CurrentTime.QuadPart + MupKnownPrefixTimeout.QuadPart;
175 }
176 
177 PMUP_CCB
178 MupCreateCcb(VOID)
179 {
180     PMUP_CCB Ccb;
181 
182     Ccb = ExAllocatePoolWithTag(PagedPool, sizeof(MUP_CCB), TAG_MUP);
183     if (Ccb == NULL)
184     {
185         return NULL;
186     }
187 
188     Ccb->NodeStatus = NODE_STATUS_HEALTHY;
189     Ccb->NodeReferences = 1;
190     Ccb->NodeType = NODE_TYPE_CCB;
191     Ccb->NodeSize = sizeof(MUP_CCB);
192 
193     return Ccb;
194 }
195 
196 PMUP_FCB
197 MupCreateFcb(VOID)
198 {
199     PMUP_FCB Fcb;
200 
201     Fcb = ExAllocatePoolWithTag(PagedPool, sizeof(MUP_FCB), TAG_MUP);
202     if (Fcb == NULL)
203     {
204         return NULL;
205     }
206 
207     Fcb->NodeStatus = NODE_STATUS_HEALTHY;
208     Fcb->NodeReferences = 1;
209     Fcb->NodeType = NODE_TYPE_FCB;
210     Fcb->NodeSize = sizeof(MUP_FCB);
211     InitializeListHead(&Fcb->CcbList);
212 
213     return Fcb;
214 }
215 
216 PMUP_MIC
217 MupAllocateMasterIoContext(VOID)
218 {
219     PMUP_MIC MasterIoContext;
220 
221     MasterIoContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(MUP_MIC), TAG_MUP);
222     if (MasterIoContext == NULL)
223     {
224         return NULL;
225     }
226 
227     MasterIoContext->NodeStatus = NODE_STATUS_HEALTHY;
228     MasterIoContext->NodeReferences = 1;
229     MasterIoContext->NodeType = NODE_TYPE_MIC;
230     MasterIoContext->NodeSize = sizeof(MUP_MIC);
231 
232     return MasterIoContext;
233 }
234 
235 PMUP_UNC
236 MupAllocateUncProvider(ULONG RedirectorDeviceNameLength)
237 {
238     PMUP_UNC UncProvider;
239 
240     UncProvider = ExAllocatePoolWithTag(PagedPool, sizeof(MUP_UNC) + RedirectorDeviceNameLength, TAG_MUP);
241     if (UncProvider == NULL)
242     {
243         return NULL;
244     }
245 
246     UncProvider->NodeReferences = 0;
247     UncProvider->NodeType = NODE_TYPE_UNC;
248     UncProvider->NodeStatus = NODE_STATUS_HEALTHY;
249     UncProvider->NodeSize = sizeof(MUP_UNC) + RedirectorDeviceNameLength;
250     UncProvider->Registered = FALSE;
251 
252     return UncProvider;
253 }
254 
255 PMUP_PFX
256 MupAllocatePrefixEntry(ULONG PrefixLength)
257 {
258     PMUP_PFX Prefix;
259     ULONG PrefixSize;
260 
261     PrefixSize = sizeof(MUP_PFX) + PrefixLength;
262     Prefix = ExAllocatePoolWithTag(PagedPool, PrefixSize, TAG_MUP);
263     if (Prefix == NULL)
264     {
265         return NULL;
266     }
267 
268     RtlZeroMemory(Prefix, PrefixSize);
269     Prefix->NodeType = NODE_TYPE_PFX;
270     Prefix->NodeStatus = NODE_STATUS_HEALTHY;
271     Prefix->NodeReferences = 1;
272     Prefix->NodeSize = PrefixSize;
273 
274     /* If we got a prefix, initialize the string */
275     if (PrefixLength != 0)
276     {
277         Prefix->AcceptedPrefix.Buffer = (PVOID)((ULONG_PTR)Prefix + sizeof(MUP_PFX));
278         Prefix->AcceptedPrefix.Length = PrefixLength;
279         Prefix->AcceptedPrefix.MaximumLength = PrefixLength;
280     }
281     /* Otherwise, mark the fact that the allocation will be external */
282     else
283     {
284         Prefix->ExternalAlloc = TRUE;
285     }
286 
287     Prefix->KeepExtraRef = FALSE;
288     /* Already init timeout to have one */
289     MupCalculateTimeout(&Prefix->ValidityTimeout);
290 
291     return Prefix;
292 }
293 
294 PMUP_MQC
295 MupAllocateMasterQueryContext(VOID)
296 {
297     PMUP_MQC MasterQueryContext;
298 
299     MasterQueryContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(MUP_MQC), TAG_MUP);
300     if (MasterQueryContext == NULL)
301     {
302         return NULL;
303     }
304 
305     MasterQueryContext->NodeStatus = NODE_STATUS_HEALTHY;
306     MasterQueryContext->NodeReferences = 1;
307     MasterQueryContext->NodeType = NODE_TYPE_MQC;
308     MasterQueryContext->NodeSize = sizeof(MUP_MQC);
309     InitializeListHead(&MasterQueryContext->QueryPathList);
310     InitializeListHead(&MasterQueryContext->MQCListEntry);
311     ExInitializeResourceLite(&MasterQueryContext->QueryPathListLock);
312 
313     return MasterQueryContext;
314 }
315 
316 ULONG
317 MupDecodeFileObject(PFILE_OBJECT FileObject, PMUP_FCB * pFcb, PMUP_CCB * pCcb)
318 {
319     ULONG Type;
320     PMUP_FCB Fcb;
321 
322     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
323     *pFcb = FileObject->FsContext;
324     *pCcb = FileObject->FsContext2;
325     Fcb = *pFcb;
326 
327     if (Fcb == NULL)
328     {
329         ExReleaseResourceLite(&MupGlobalLock);
330         return 0;
331     }
332 
333     Type = Fcb->NodeType;
334     if ((Type != NODE_TYPE_VCB && Type != NODE_TYPE_FCB) || (Fcb->NodeStatus != NODE_STATUS_HEALTHY && Fcb->NodeStatus != NODE_STATUS_CLEANUP))
335     {
336         *pFcb = 0;
337         ExReleaseResourceLite(&MupGlobalLock);
338         return 0;
339     }
340 
341     ++Fcb->NodeReferences;
342     ExReleaseResourceLite(&MupGlobalLock);
343 
344     return Type;
345 }
346 
347 VOID
348 MupFreeNode(PVOID Node)
349 {
350     ExFreePoolWithTag(Node, TAG_MUP);
351 }
352 
353 VOID
354 MupDereferenceFcb(PMUP_FCB Fcb)
355 {
356     /* Just dereference and delete if no references left */
357     if (InterlockedDecrement(&Fcb->NodeReferences) == 0)
358     {
359         MupFreeNode(Fcb);
360     }
361 }
362 
363 VOID
364 MupDereferenceCcb(PMUP_CCB Ccb)
365 {
366     /* Just dereference and delete (and clean) if no references left */
367     if (InterlockedDecrement(&Ccb->NodeReferences) == 0)
368     {
369         ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
370         RemoveEntryList(&Ccb->CcbListEntry);
371         ExReleaseResourceLite(&MupCcbListLock);
372         ObDereferenceObject(Ccb->FileObject);
373         MupDereferenceFcb(Ccb->Fcb);
374         MupFreeNode(Ccb);
375     }
376 }
377 
378 VOID
379 MupDereferenceVcb(PMUP_VCB Vcb)
380 {
381     /* We cannot reach the point where no references are left */
382     if (InterlockedDecrement(&Vcb->NodeReferences) == 0)
383     {
384         KeBugCheckEx(FILE_SYSTEM, 3, 0, 0, 0);
385     }
386 }
387 
388 NTSTATUS
389 MupDereferenceMasterIoContext(PMUP_MIC MasterIoContext,
390                               PNTSTATUS NewStatus)
391 {
392     PIRP Irp;
393     NTSTATUS Status;
394     PIO_STACK_LOCATION Stack;
395 
396     Status = STATUS_SUCCESS;
397 
398     if (NewStatus != NULL)
399     {
400         /* Save last status, depending on whether it failed or not */
401         if (!NT_SUCCESS(*NewStatus))
402         {
403             MasterIoContext->LastFailed = *NewStatus;
404         }
405         else
406         {
407             MasterIoContext->LastSuccess = STATUS_SUCCESS;
408         }
409     }
410 
411     if (InterlockedDecrement(&MasterIoContext->NodeReferences) != 0)
412     {
413         return STATUS_PENDING;
414     }
415 
416     Irp = MasterIoContext->Irp;
417     Stack = IoGetCurrentIrpStackLocation(Irp);
418     if (Stack->MajorFunction == IRP_MJ_WRITE)
419     {
420         Irp->IoStatus.Information = Stack->Parameters.Write.Length;
421     }
422     else
423     {
424         Irp->IoStatus.Information = 0;
425     }
426 
427     /* In case we never had any success (init is a failure), get the last failed status */
428     if (!NT_SUCCESS(MasterIoContext->LastSuccess))
429     {
430         Status = MasterIoContext->LastFailed;
431     }
432 
433     Irp->IoStatus.Status = Status;
434     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
435     MupDereferenceFcb(MasterIoContext->Fcb);
436     MupFreeNode(MasterIoContext);
437 
438     return Status;
439 }
440 
441 VOID
442 MupDereferenceUncProvider(PMUP_UNC UncProvider)
443 {
444     InterlockedExchangeAdd(&UncProvider->NodeReferences, -1);
445 }
446 
447 VOID
448 MupDereferenceKnownPrefix(PMUP_PFX Prefix)
449 {
450     /* Just dereference and delete (and clean) if no references left */
451     if (InterlockedDecrement(&Prefix->NodeReferences) == 0)
452     {
453         if (Prefix->InTable)
454         {
455             RtlRemoveUnicodePrefix(&MupPrefixTable, &Prefix->PrefixTableEntry);
456             RemoveEntryList(&Prefix->PrefixListEntry);
457         }
458 
459         if (Prefix->ExternalAlloc && Prefix->AcceptedPrefix.Buffer != NULL)
460         {
461             ExFreePoolWithTag(Prefix->AcceptedPrefix.Buffer, TAG_MUP);
462         }
463 
464         if (Prefix->UncProvider)
465         {
466             MupDereferenceUncProvider(Prefix->UncProvider);
467         }
468 
469         MupFreeNode(Prefix);
470     }
471 }
472 
473 VOID
474 MupRemoveKnownPrefixEntry(PMUP_PFX Prefix)
475 {
476     RtlRemoveUnicodePrefix(&MupPrefixTable, &Prefix->PrefixTableEntry);
477     RemoveEntryList(&Prefix->PrefixListEntry);
478     Prefix->InTable = FALSE;
479     MupDereferenceKnownPrefix(Prefix);
480 }
481 
482 VOID
483 MupInvalidatePrefixTable(VOID)
484 {
485     PMUP_PFX Prefix;
486     PLIST_ENTRY Entry;
487 
488     /* Browse the prefix table */
489     ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
490     for (Entry = MupPrefixList.Flink;
491          Entry != &MupPrefixList;
492          Entry = Entry->Flink)
493     {
494         Prefix = CONTAINING_RECORD(Entry, MUP_PFX, PrefixListEntry);
495 
496         /* And remove any entry in it */
497         if (Prefix->InTable)
498         {
499             MupRemoveKnownPrefixEntry(Prefix);
500         }
501     }
502     ExReleaseResourceLite(&MupPrefixTableLock);
503 }
504 
505 VOID
506 MupCleanupVcb(PDEVICE_OBJECT DeviceObject,
507               PIRP Irp,
508               PMUP_VCB Vcb)
509 {
510     ExAcquireResourceExclusiveLite(&MupVcbLock, TRUE);
511 
512     /* Check we're not doing anything wrong first */
513     if (Vcb->NodeStatus != NODE_STATUS_HEALTHY || Vcb->NodeType != NODE_TYPE_VCB)
514     {
515         ExRaiseStatus(STATUS_INVALID_HANDLE);
516     }
517 
518     /* Remove share access */
519     IoRemoveShareAccess(IoGetCurrentIrpStackLocation(Irp)->FileObject, &Vcb->ShareAccess);
520 
521     ExReleaseResourceLite(&MupVcbLock);
522 }
523 
524 VOID
525 MupCleanupFcb(PDEVICE_OBJECT DeviceObject,
526               PIRP Irp,
527               PMUP_FCB Fcb)
528 {
529     PLIST_ENTRY Entry;
530     PMUP_CCB Ccb;
531 
532     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
533     /* Check we're not doing anything wrong first */
534     if (Fcb->NodeStatus != NODE_STATUS_HEALTHY || Fcb->NodeType != NODE_TYPE_FCB)
535     {
536         ExRaiseStatus(STATUS_INVALID_HANDLE);
537     }
538     Fcb->NodeStatus = NODE_STATUS_CLEANUP;
539     ExReleaseResourceLite(&MupGlobalLock);
540 
541     /* Dereference any CCB associated with the FCB */
542     ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
543     for (Entry = Fcb->CcbList.Flink;
544          Entry != &Fcb->CcbList;
545          Entry = Entry->Flink)
546     {
547         Ccb = CONTAINING_RECORD(Entry, MUP_CCB, CcbListEntry);
548         ExReleaseResourceLite(&MupCcbListLock);
549         MupDereferenceCcb(Ccb);
550         ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
551     }
552     ExReleaseResourceLite(&MupCcbListLock);
553 }
554 
555 NTSTATUS
556 CommonForwardedIoCompletionRoutine(PDEVICE_OBJECT DeviceObject,
557                                    PIRP Irp,
558                                    PFORWARDED_IO_CONTEXT FwdCtxt)
559 {
560     NTSTATUS Status;
561 
562     Status = Irp->IoStatus.Status;
563 
564     /* Just free everything we had allocated */
565     if (Irp->MdlAddress != NULL)
566     {
567         MmUnlockPages(Irp->MdlAddress);
568         IoFreeMdl(Irp->MdlAddress);
569     }
570 
571     if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
572     {
573         ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer, TAG_MUP);
574     }
575 
576     IoFreeIrp(Irp);
577 
578     /* Dereference the master context
579      * The upper IRP will be completed once all the lower IRPs are done
580      * (and thus, references count reaches 0)
581      */
582     MupDereferenceCcb(FwdCtxt->Ccb);
583     MupDereferenceMasterIoContext(FwdCtxt->MasterIoContext, &Status);
584     ExFreePoolWithTag(FwdCtxt, TAG_MUP);
585 
586     return STATUS_MORE_PROCESSING_REQUIRED;
587 }
588 
589 VOID
590 NTAPI
591 DeferredForwardedIoCompletionRoutine(PVOID Context)
592 {
593     PFORWARDED_IO_CONTEXT FwdCtxt = (PFORWARDED_IO_CONTEXT)Context;
594 
595     CommonForwardedIoCompletionRoutine(FwdCtxt->DeviceObject, FwdCtxt->Irp, Context);
596 }
597 
598 NTSTATUS
599 NTAPI
600 ForwardedIoCompletionRoutine(PDEVICE_OBJECT DeviceObject,
601                              PIRP Irp,
602                              PVOID Context)
603 {
604     PFORWARDED_IO_CONTEXT FwdCtxt;
605 
606     /* If we're at DISPATCH_LEVEL, we cannot complete, defer completion */
607     if (KeGetCurrentIrql() < DISPATCH_LEVEL)
608     {
609         CommonForwardedIoCompletionRoutine(DeviceObject, Irp, Context);
610     }
611     else
612     {
613         FwdCtxt = (PFORWARDED_IO_CONTEXT)Context;
614 
615         ExInitializeWorkItem(&FwdCtxt->WorkQueueItem, DeferredForwardedIoCompletionRoutine, Context);
616         ExQueueWorkItem(&FwdCtxt->WorkQueueItem, CriticalWorkQueue);
617     }
618 
619     return STATUS_MORE_PROCESSING_REQUIRED;
620 }
621 
622 NTSTATUS
623 BuildAndSubmitIrp(PIRP Irp,
624                   PMUP_CCB Ccb,
625                   PMUP_MIC MasterIoContext)
626 {
627     PMDL Mdl;
628     PIRP LowerIrp;
629     NTSTATUS Status;
630     PIO_STACK_LOCATION Stack;
631     PDEVICE_OBJECT DeviceObject;
632     PFORWARDED_IO_CONTEXT FwdCtxt;
633 
634     Status = STATUS_SUCCESS;
635     LowerIrp = NULL;
636     Mdl = NULL;
637 
638     /* Allocate a context for the completion routine */
639     FwdCtxt = ExAllocatePoolWithTag(NonPagedPool, sizeof(FORWARDED_IO_CONTEXT), TAG_MUP);
640     if (FwdCtxt == NULL)
641     {
642         Status = STATUS_INSUFFICIENT_RESOURCES;
643         goto Cleanup;
644     }
645 
646     /* Init it */
647     FwdCtxt->DeviceObject = NULL;
648     FwdCtxt->Irp = NULL;
649 
650     /* Allocate the IRP */
651     DeviceObject = IoGetRelatedDeviceObject(Ccb->FileObject);
652     LowerIrp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
653     if (LowerIrp == NULL)
654     {
655         Status = STATUS_INSUFFICIENT_RESOURCES;
656         goto Cleanup;
657     }
658 
659     /* Initialize it */
660     LowerIrp->Tail.Overlay.OriginalFileObject = Ccb->FileObject;
661     LowerIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
662     LowerIrp->RequestorMode = KernelMode;
663 
664     /* Copy the stack of the request we received to the IRP we'll pass below */
665     Stack = IoGetNextIrpStackLocation(LowerIrp);
666     RtlMoveMemory(Stack, IoGetCurrentIrpStackLocation(Irp), sizeof(IO_STACK_LOCATION));
667     Stack->FileObject = Ccb->FileObject;
668     /* Setup flags according to the FO */
669     if (Ccb->FileObject->Flags & FO_WRITE_THROUGH)
670     {
671         Stack->Flags = SL_WRITE_THROUGH;
672     }
673 
674     _SEH2_TRY
675     {
676         /* Does the device requires we do buffered IOs? */
677         if (DeviceObject->Flags & DO_BUFFERED_IO)
678         {
679             LowerIrp->AssociatedIrp.SystemBuffer = NULL;
680 
681             if (Stack->Parameters.Write.Length == 0)
682             {
683                 LowerIrp->Flags = IRP_BUFFERED_IO;
684             }
685             /* If we have data to pass */
686             else
687             {
688                 /* If it's coming from usermode, probe first */
689                 if (Irp->RequestorMode == UserMode)
690                 {
691                     ProbeForRead(Irp->UserBuffer, Stack->Parameters.Write.Length, sizeof(UCHAR));
692                 }
693 
694                 /* Allocate the buffer */
695                 LowerIrp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuotaTag(PagedPoolCacheAligned,
696                                                                                   Stack->Parameters.Write.Length,
697                                                                                   TAG_MUP);
698                 if (LowerIrp->AssociatedIrp.SystemBuffer == NULL)
699                 {
700                     ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
701                 }
702 
703                 /* And copy input (remember, we've to free!) */
704                 RtlMoveMemory(LowerIrp->AssociatedIrp.SystemBuffer, Irp->UserBuffer, Stack->Parameters.Write.Length);
705                 LowerIrp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
706             }
707         }
708         else
709         {
710             if (!(DeviceObject->Flags & DO_DIRECT_IO))
711             {
712                 LowerIrp->UserBuffer = Irp->UserBuffer;
713             }
714             else
715             {
716                 /* For direct IOs, allocate an MDL and pass it */
717                 if (Stack->Parameters.Write.Length != 0)
718                 {
719                     Mdl = IoAllocateMdl(Irp->UserBuffer, Stack->Parameters.Write.Length, FALSE, TRUE, LowerIrp);
720                     if (Mdl == NULL)
721                     {
722                         ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
723                     }
724 
725                     MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoReadAccess);
726                 }
727             }
728         }
729 
730         /* Fix flags in the IRP */
731         if (Ccb->FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
732         {
733             LowerIrp->Flags |= IRP_WRITE_OPERATION | IRP_NOCACHE;
734         }
735         else
736         {
737             LowerIrp->Flags |= IRP_WRITE_OPERATION;
738         }
739 
740         FwdCtxt->Ccb = Ccb;
741         FwdCtxt->MasterIoContext = MasterIoContext;
742 
743         ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
744         ++MasterIoContext->NodeReferences;
745         ExReleaseResourceLite(&MupGlobalLock);
746 
747         /* Set out completion routine */
748         IoSetCompletionRoutine(LowerIrp, ForwardedIoCompletionRoutine, FwdCtxt, TRUE, TRUE, TRUE);
749         /* And call the device with our brand new IRP */
750         Status = IoCallDriver(Ccb->DeviceObject, LowerIrp);
751     }
752     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
753     {
754         Status = _SEH2_GetExceptionCode();
755     }
756     _SEH2_END;
757 
758 Cleanup:
759     if (!NT_SUCCESS(Status))
760     {
761         if (FwdCtxt != NULL)
762         {
763             ExFreePoolWithTag(FwdCtxt, TAG_MUP);
764         }
765 
766         if (LowerIrp != NULL)
767         {
768             if (LowerIrp->AssociatedIrp.SystemBuffer == NULL)
769             {
770                 ExFreePoolWithTag(LowerIrp->AssociatedIrp.SystemBuffer, TAG_MUP);
771             }
772 
773             IoFreeIrp(LowerIrp);
774         }
775 
776         if (Mdl != NULL)
777         {
778             IoFreeMdl(Mdl);
779         }
780     }
781 
782     return Status;
783 }
784 
785 NTSTATUS
786 NTAPI
787 MupForwardIoRequest(PDEVICE_OBJECT DeviceObject,
788                     PIRP Irp)
789 {
790     PMUP_FCB Fcb;
791     PMUP_CCB Ccb;
792     NTSTATUS Status;
793     PLIST_ENTRY Entry;
794     PMUP_CCB FcbListCcb;
795     BOOLEAN CcbLockAcquired;
796     PMUP_MIC MasterIoContext;
797     PIO_STACK_LOCATION Stack;
798 
799     /* If DFS is enabled, check if that's for DFS and is so relay */
800     if (MupEnableDfs && DeviceObject->DeviceType == FILE_DEVICE_DFS)
801     {
802         return DfsVolumePassThrough(DeviceObject, Irp);
803     }
804 
805     Stack = IoGetCurrentIrpStackLocation(Irp);
806 
807     FsRtlEnterFileSystem();
808 
809     /* Write request is only possible for a mailslot, we need a FCB */
810     MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb);
811     if (Fcb == NULL || Fcb->NodeType != NODE_TYPE_FCB)
812     {
813         FsRtlExitFileSystem();
814         Status = STATUS_INVALID_DEVICE_REQUEST;
815         goto Complete;
816     }
817 
818     /* Allocate a context */
819     MasterIoContext = MupAllocateMasterIoContext();
820     if (MasterIoContext == NULL)
821     {
822         FsRtlExitFileSystem();
823         Status = STATUS_INSUFFICIENT_RESOURCES;
824         goto Complete;
825     }
826 
827     /* Mark the IRP pending and init the context */
828     IoMarkIrpPending(Irp);
829     MasterIoContext->Irp = Irp;
830     /* Init with a failure to catch if we ever succeed */
831     MasterIoContext->LastSuccess = STATUS_UNSUCCESSFUL;
832     /* Init with the worth failure possible */
833     MasterIoContext->LastFailed = STATUS_BAD_NETWORK_PATH;
834     MasterIoContext->Fcb = Fcb;
835 
836     _SEH2_TRY
837     {
838         ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
839         CcbLockAcquired = TRUE;
840 
841         /* For all the CCB (ie, the mailslots) we have */
842         for (Entry = Fcb->CcbList.Flink;
843              Entry != &Fcb->CcbList;
844              Entry = Entry->Flink)
845         {
846             FcbListCcb = CONTAINING_RECORD(Entry, MUP_CCB, CcbListEntry);
847             ExReleaseResourceLite(&MupCcbListLock);
848             CcbLockAcquired = FALSE;
849 
850             ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
851             ++FcbListCcb->NodeReferences;
852             ExReleaseResourceLite(&MupGlobalLock);
853 
854             /* Forward the write request */
855             BuildAndSubmitIrp(Irp, FcbListCcb, MasterIoContext);
856             ExAcquireResourceExclusiveLite(&MupCcbListLock, TRUE);
857             CcbLockAcquired = TRUE;
858         }
859 
860         ExReleaseResourceLite(&MupCcbListLock);
861         CcbLockAcquired = FALSE;
862     }
863     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
864     {
865         if (CcbLockAcquired)
866         {
867             ExReleaseResourceLite(&MupCcbListLock);
868         }
869     }
870     _SEH2_END;
871 
872     /* And done */
873     MupDereferenceMasterIoContext(MasterIoContext, NULL);
874     FsRtlExitFileSystem();
875 
876     return STATUS_PENDING;
877 
878 Complete:
879     /* Failure case */
880     Irp->IoStatus.Status = Status;
881     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
882     return Status;
883 }
884 
885 PMUP_UNC
886 AddUnregisteredProvider(PCWSTR DeviceName,
887                         ULONG ProviderOrder)
888 {
889     PMUP_UNC UncProvider;
890     ULONG StrLen, NameLen;
891 
892     /* Just allocate the node */
893     NameLen = wcslen(DeviceName);
894     StrLen = NameLen * sizeof(WCHAR);
895     UncProvider = MupAllocateUncProvider(StrLen);
896     if (UncProvider == NULL)
897     {
898         return NULL;
899     }
900 
901     /* And init it */
902     UncProvider->DeviceName.MaximumLength = StrLen;
903     UncProvider->DeviceName.Length = StrLen;
904     UncProvider->DeviceName.Buffer = (PWSTR)((ULONG_PTR)UncProvider + sizeof(MUP_UNC));
905     UncProvider->ProviderOrder = ProviderOrder;
906     RtlMoveMemory(UncProvider->DeviceName.Buffer, DeviceName, StrLen);
907 
908     /* And add it to the global list
909      * We're using tail here so that when called from registry init,
910      * the providers with highest priority will be in the head of
911      * the list
912      */
913     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
914     InsertTailList(&MupProviderList, &UncProvider->ProviderListEntry);
915     ExReleaseResourceLite(&MupGlobalLock);
916 
917     return UncProvider;
918 }
919 
920 VOID
921 InitializeProvider(PCWSTR ProviderName,
922                    ULONG ProviderOrder)
923 {
924     NTSTATUS Status;
925     HANDLE KeyHandle;
926     UNICODE_STRING Key, Value;
927     PKEY_VALUE_FULL_INFORMATION Info;
928     OBJECT_ATTRIBUTES ObjectAttributes;
929     ULONG NameLen, StrLen, ResultLength;
930 
931     /* Get the information about the provider from registry */
932     NameLen = wcslen(ProviderName);
933     StrLen = NameLen * sizeof(WCHAR) + sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") + sizeof(L"\\NetworkProvider");
934     Key.Buffer = ExAllocatePoolWithTag(PagedPool, StrLen, TAG_MUP);
935     if (Key.Buffer == NULL)
936     {
937         return;
938     }
939 
940     RtlMoveMemory(Key.Buffer, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\", sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"));
941     Key.MaximumLength = StrLen;
942     Key.Length = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") - sizeof(UNICODE_NULL);
943     RtlAppendUnicodeToString(&Key, ProviderName);
944     RtlAppendUnicodeToString(&Key, L"\\NetworkProvider");
945 
946     InitializeObjectAttributes(&ObjectAttributes,
947                                &Key,
948                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
949                                NULL,
950                                NULL);
951     Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
952     ExFreePoolWithTag(Key.Buffer, TAG_MUP);
953     if (!NT_SUCCESS(Status))
954     {
955         return;
956     }
957 
958     RtlInitUnicodeString(&Value, L"DeviceName");
959     Status = ZwQueryValueKey(KeyHandle, &Value, KeyValueFullInformation, NULL, 0, &ResultLength);
960     if (Status == STATUS_BUFFER_TOO_SMALL)
961     {
962         Info = ExAllocatePoolWithTag(PagedPool, ResultLength + sizeof(UNICODE_NULL), TAG_MUP);
963         if (Info == NULL)
964         {
965             ZwClose(KeyHandle);
966             return;
967         }
968 
969         Status = ZwQueryValueKey(KeyHandle, &Value, KeyValueFullInformation, Info, ResultLength, &ResultLength);
970     }
971     else
972     {
973         Info = NULL;
974     }
975 
976     ZwClose(KeyHandle);
977 
978     /* And create the provider
979      * It will remain unregistered until FsRTL receives a registration request and forwards
980      * it to MUP
981      */
982     if (NT_SUCCESS(Status))
983     {
984         ASSERT(Info != NULL);
985         AddUnregisteredProvider((PWSTR)((ULONG_PTR)Info + Info->DataOffset), ProviderOrder);
986     }
987 
988     if (Info != NULL)
989     {
990         ExFreePoolWithTag(Info, TAG_MUP);
991     }
992 }
993 
994 VOID
995 MupGetProviderInformation(VOID)
996 {
997     BOOLEAN End;
998     NTSTATUS Status;
999     HANDLE KeyHandle;
1000     PWSTR Providers, Coma;
1001     PKEY_VALUE_FULL_INFORMATION Info;
1002     ULONG ResultLength, ProviderCount;
1003     OBJECT_ATTRIBUTES ObjectAttributes;
1004     UNICODE_STRING NetworkProvider, ProviderOrder;
1005 
1006     /* Open the registry to get the order of the providers */
1007     RtlInitUnicodeString(&NetworkProvider, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\NetworkProvider\\Order");
1008     InitializeObjectAttributes(&ObjectAttributes,
1009                                &NetworkProvider,
1010                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1011                                NULL,
1012                                NULL);
1013     Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
1014     if (!NT_SUCCESS(Status))
1015     {
1016         return;
1017     }
1018 
1019     RtlInitUnicodeString(&ProviderOrder, L"ProviderOrder");
1020     Status = ZwQueryValueKey(KeyHandle, &ProviderOrder, KeyValueFullInformation, NULL, 0, &ResultLength);
1021     if (Status == STATUS_BUFFER_TOO_SMALL)
1022     {
1023         Info = ExAllocatePoolWithTag(PagedPool, ResultLength + sizeof(UNICODE_NULL), TAG_MUP);
1024         if (Info == NULL)
1025         {
1026             ZwClose(KeyHandle);
1027             return;
1028         }
1029 
1030         Status = ZwQueryValueKey(KeyHandle, &ProviderOrder, KeyValueFullInformation, Info, ResultLength, &ResultLength);
1031     }
1032     else
1033     {
1034         Info = NULL;
1035     }
1036 
1037     ZwClose(KeyHandle);
1038 
1039     if (NT_SUCCESS(Status))
1040     {
1041         ASSERT(Info != NULL);
1042 
1043         Providers = (PWSTR)((ULONG_PTR)Info + Info->DataOffset);
1044         End = FALSE;
1045         ProviderCount = 0;
1046 
1047         /* For all the providers we got (coma-separated list), just create a provider node with the right order
1048          * The order is just the order of the list
1049          * First has highest priority (0) and then, get lower and lower priority
1050          * The highest number is the lowest priority
1051          */
1052         do
1053         {
1054             Coma = wcschr(Providers, L',');
1055             if (Coma != NULL)
1056             {
1057                 *Coma = UNICODE_NULL;
1058             }
1059             else
1060             {
1061                 End = TRUE;
1062             }
1063 
1064             InitializeProvider(Providers, ProviderCount);
1065             ++ProviderCount;
1066 
1067             Providers = Coma + 1;
1068         } while (!End);
1069     }
1070 
1071     if (Info != NULL)
1072     {
1073         ExFreePoolWithTag(Info, TAG_MUP);
1074     }
1075 }
1076 
1077 PMUP_UNC
1078 MupCheckForUnregisteredProvider(PUNICODE_STRING RedirectorDeviceName)
1079 {
1080     PLIST_ENTRY Entry;
1081     PMUP_UNC UncProvider;
1082 
1083     /* Browse the list of all the providers nodes we have */
1084     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1085     for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
1086     {
1087         UncProvider = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
1088 
1089         /* If one matches the device and is not registered, that's ours! */
1090         if (!UncProvider->Registered && RtlEqualUnicodeString(RedirectorDeviceName, &UncProvider->DeviceName, TRUE))
1091         {
1092             UncProvider->NodeStatus = NODE_STATUS_HEALTHY;
1093             break;
1094         }
1095     }
1096 
1097     if (Entry == &MupProviderList)
1098     {
1099         UncProvider = NULL;
1100     }
1101     ExReleaseResourceLite(&MupGlobalLock);
1102 
1103     return UncProvider;
1104 }
1105 
1106 NTSTATUS
1107 RegisterUncProvider(PDEVICE_OBJECT DeviceObject,
1108                     PIRP Irp)
1109 
1110 {
1111     BOOLEAN New;
1112     PMUP_FCB Fcb;
1113     PMUP_CCB Ccb;
1114     NTSTATUS Status;
1115     PLIST_ENTRY Entry;
1116     PIO_STACK_LOCATION Stack;
1117     IO_STATUS_BLOCK IoStatusBlock;
1118     PMUP_UNC UncProvider, ListEntry;
1119     OBJECT_ATTRIBUTES ObjectAttributes;
1120     UNICODE_STRING RedirectorDeviceName;
1121     OBJECT_HANDLE_INFORMATION HandleInfo;
1122     PMUP_PROVIDER_REGISTRATION_INFO RegInfo;
1123 
1124     DPRINT1("RegisterUncProvider(%p, %p)\n", DeviceObject, Irp);
1125     New = FALSE;
1126 
1127     /* Check whether providers order was already initialized */
1128     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1129     if (MupOrderInitialized)
1130     {
1131         ExReleaseResourceLite(&MupGlobalLock);
1132     }
1133     else
1134     {
1135         /* They weren't, so do it */
1136         MupOrderInitialized = TRUE;
1137         ExReleaseResourceLite(&MupGlobalLock);
1138         MupGetProviderInformation();
1139     }
1140 
1141     Stack = IoGetCurrentIrpStackLocation(Irp);
1142 
1143     /* This can only happen with a volume open */
1144     if (MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb) != NODE_TYPE_VCB)
1145     {
1146         Irp->IoStatus.Status = STATUS_INVALID_HANDLE;
1147         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1148 
1149         return STATUS_INVALID_HANDLE;
1150     }
1151 
1152     /* Get the registration information */
1153     RegInfo = (PMUP_PROVIDER_REGISTRATION_INFO)Irp->AssociatedIrp.SystemBuffer;
1154     _SEH2_TRY
1155     {
1156         RedirectorDeviceName.Length = RegInfo->RedirectorDeviceNameLength;
1157         RedirectorDeviceName.MaximumLength = RedirectorDeviceName.Length;
1158         RedirectorDeviceName.Buffer = (PWSTR)((ULONG_PTR)RegInfo + RegInfo->RedirectorDeviceNameOffset);
1159 
1160         /* Have we got already a node for it? (Like from previous init) */
1161         UncProvider = MupCheckForUnregisteredProvider(&RedirectorDeviceName);
1162         if (UncProvider == NULL)
1163         {
1164             /* If we don't, allocate a new one */
1165             New = TRUE;
1166             UncProvider = MupAllocateUncProvider(RegInfo->RedirectorDeviceNameLength);
1167             if (UncProvider == NULL)
1168             {
1169                 Status = STATUS_INVALID_USER_BUFFER;
1170                 _SEH2_LEAVE;
1171             }
1172 
1173             /* Set it up */
1174             UncProvider->DeviceName.Length = RedirectorDeviceName.Length;
1175             UncProvider->DeviceName.MaximumLength = RedirectorDeviceName.MaximumLength;
1176             UncProvider->DeviceName.Buffer = (PWSTR)((ULONG_PTR)UncProvider + sizeof(MUP_UNC));
1177 
1178             /* As it wasn't in registry for order, give the lowest priority possible */
1179             UncProvider->ProviderOrder = MAXLONG;
1180             RtlMoveMemory(UncProvider->DeviceName.Buffer, (PWSTR)((ULONG_PTR)RegInfo + RegInfo->RedirectorDeviceNameOffset), RegInfo->RedirectorDeviceNameLength);
1181         }
1182 
1183         /* Continue registration */
1184         UncProvider->MailslotsSupported = RegInfo->MailslotsSupported;
1185         ++UncProvider->NodeReferences;
1186 
1187         /* Open a handle to the device */
1188         InitializeObjectAttributes(&ObjectAttributes,
1189                                    &UncProvider->DeviceName,
1190                                    OBJ_CASE_INSENSITIVE,
1191                                    NULL,
1192                                    NULL);
1193         Status = NtOpenFile(&UncProvider->DeviceHandle,
1194                             FILE_TRAVERSE,
1195                             &ObjectAttributes,
1196                             &IoStatusBlock,
1197                             FILE_SHARE_READ | FILE_SHARE_WRITE,
1198                             FILE_DIRECTORY_FILE);
1199         if (NT_SUCCESS(Status))
1200         {
1201             Status = IoStatusBlock.Status;
1202         }
1203 
1204         /* And return the provider (as CCB) */
1205         if (NT_SUCCESS(Status))
1206         {
1207             Stack->FileObject->FsContext2 = UncProvider;
1208             Status = ObReferenceObjectByHandle(UncProvider->DeviceHandle, 0, NULL, KernelMode, (PVOID *)&UncProvider->FileObject, &HandleInfo);
1209             if (!NT_SUCCESS(Status))
1210             {
1211                 NtClose(UncProvider->DeviceHandle);
1212             }
1213         }
1214 
1215         if (!NT_SUCCESS(Status))
1216         {
1217             MupDereferenceUncProvider(UncProvider);
1218         }
1219         else
1220         {
1221             UncProvider->DeviceObject = IoGetRelatedDeviceObject(UncProvider->FileObject);
1222 
1223             /* Now, insert the provider in our global list
1224              * They are sorted by order
1225              */
1226             ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1227             ++MupProviderCount;
1228             if (New)
1229             {
1230                 for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
1231                 {
1232                     ListEntry = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
1233 
1234                     if (UncProvider->ProviderOrder < ListEntry->ProviderOrder)
1235                     {
1236                         break;
1237                     }
1238                 }
1239 
1240                 InsertTailList(Entry, &UncProvider->ProviderListEntry);
1241             }
1242             UncProvider->Registered = TRUE;
1243             ExReleaseResourceLite(&MupGlobalLock);
1244             Status = STATUS_SUCCESS;
1245 
1246             DPRINT1("UNC provider %wZ registered\n", &UncProvider->DeviceName);
1247         }
1248     }
1249     _SEH2_FINALLY
1250     {
1251         if (_SEH2_AbnormalTermination())
1252         {
1253             Status = STATUS_INVALID_USER_BUFFER;
1254         }
1255 
1256         MupDereferenceVcb((PMUP_VCB)Fcb);
1257 
1258         Irp->IoStatus.Status = Status;
1259         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1260     }
1261     _SEH2_END;
1262 
1263     return Status;
1264 }
1265 
1266 NTSTATUS
1267 NTAPI
1268 MupFsControl(PDEVICE_OBJECT DeviceObject,
1269              PIRP Irp)
1270 {
1271     NTSTATUS Status;
1272     PIO_STACK_LOCATION Stack;
1273 
1274     Stack = IoGetCurrentIrpStackLocation(Irp);
1275 
1276     _SEH2_TRY
1277     {
1278         /* MUP only understands a single FSCTL code: registering UNC provider */
1279         if (Stack->Parameters.FileSystemControl.FsControlCode == FSCTL_MUP_REGISTER_PROVIDER)
1280         {
1281             /* It obviously has to come from a driver/kernelmode thread */
1282             if (Irp->RequestorMode == UserMode)
1283             {
1284                 Status = STATUS_ACCESS_DENIED;
1285 
1286                 Irp->IoStatus.Status = Status;
1287                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1288 
1289                 _SEH2_LEAVE;
1290             }
1291 
1292             Status = RegisterUncProvider(DeviceObject, Irp);
1293         }
1294         else
1295         {
1296             /* If that's an unknown FSCTL code, maybe it's for DFS, pass it */
1297             if (!MupEnableDfs)
1298             {
1299                 Status = STATUS_INVALID_PARAMETER;
1300 
1301                 Irp->IoStatus.Status = Status;
1302                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1303 
1304                 _SEH2_LEAVE;
1305             }
1306 
1307             Status = DfsFsdFileSystemControl(DeviceObject, Irp);
1308         }
1309     }
1310     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1311     {
1312         Status = _SEH2_GetExceptionCode();
1313     }
1314     _SEH2_END;
1315 
1316     return Status;
1317 }
1318 
1319 VOID
1320 MupSetFileObject(PFILE_OBJECT FileObject,
1321                  PMUP_FCB Fcb,
1322                  PMUP_CCB Ccb)
1323 {
1324     FileObject->FsContext = Fcb;
1325     FileObject->FsContext2 = Ccb;
1326 }
1327 
1328 NTSTATUS
1329 MupRerouteOpen(PFILE_OBJECT FileObject,
1330                PMUP_UNC UncProvider)
1331 {
1332     PWSTR FullPath;
1333     ULONG TotalLength;
1334 
1335     DPRINT1("Rerouting %wZ with %wZ\n", &FileObject->FileName, &UncProvider->DeviceName);
1336 
1337     /* Get the full path name (device name first, and requested file name appended) */
1338     TotalLength = UncProvider->DeviceName.Length + FileObject->FileName.Length;
1339     if (TotalLength > MAXUSHORT)
1340     {
1341         return STATUS_NAME_TOO_LONG;
1342     }
1343 
1344     /* Allocate a buffer big enough */
1345     FullPath = ExAllocatePoolWithTag(PagedPool, TotalLength, TAG_MUP);
1346     if (FullPath == NULL)
1347     {
1348         return STATUS_INSUFFICIENT_RESOURCES;
1349     }
1350 
1351     /* Create the full path */
1352     RtlMoveMemory(FullPath, UncProvider->DeviceName.Buffer, UncProvider->DeviceName.Length);
1353     RtlMoveMemory((PWSTR)((ULONG_PTR)FullPath + UncProvider->DeviceName.Length), FileObject->FileName.Buffer, FileObject->FileName.Length);
1354 
1355     /* And redo the path in the file object */
1356     ExFreePoolWithTag(FileObject->FileName.Buffer, 0);
1357     FileObject->FileName.Buffer = FullPath;
1358     FileObject->FileName.MaximumLength = TotalLength;
1359     FileObject->FileName.Length = FileObject->FileName.MaximumLength;
1360 
1361     /* Ob, please reparse to open the correct file at the right place, thanks! :-) */
1362     return STATUS_REPARSE;
1363 }
1364 
1365 NTSTATUS
1366 BroadcastOpen(PIRP Irp)
1367 {
1368     PMUP_FCB Fcb;
1369     HANDLE Handle;
1370     PLIST_ENTRY Entry;
1371     PMUP_CCB Ccb = NULL;
1372     PMUP_UNC UncProvider;
1373     UNICODE_STRING FullPath;
1374     PFILE_OBJECT FileObject;
1375     PIO_STACK_LOCATION Stack;
1376     NTSTATUS Status, LastFailed;
1377     ULONG TotalLength, LastOrder;
1378     IO_STATUS_BLOCK IoStatusBlock;
1379     OBJECT_ATTRIBUTES ObjectAttributes;
1380     OBJECT_HANDLE_INFORMATION HandleInfo;
1381     BOOLEAN Locked, Referenced, CcbInitialized;
1382 
1383     Fcb = MupCreateFcb();
1384     if (Fcb == NULL)
1385     {
1386         return STATUS_INSUFFICIENT_RESOURCES;
1387     }
1388 
1389     Stack = IoGetCurrentIrpStackLocation(Irp);
1390     FileObject = Stack->FileObject;
1391     Locked = FALSE;
1392     Referenced = FALSE;
1393     CcbInitialized = FALSE;
1394     LastFailed = STATUS_NO_SUCH_FILE;
1395     LastOrder = (ULONG)-1;
1396 
1397     _SEH2_TRY
1398     {
1399         ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1400         Locked = TRUE;
1401 
1402         /* Associate our FCB with the FO */
1403         MupSetFileObject(FileObject, Fcb, NULL);
1404         Fcb->FileObject = FileObject;
1405 
1406         /* Now, broadcast the open to any UNC provider that supports mailslots */
1407         for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
1408         {
1409             UncProvider = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
1410             ++UncProvider->NodeReferences;
1411             Referenced = TRUE;
1412 
1413             ExReleaseResourceLite(&MupGlobalLock);
1414             Locked = FALSE;
1415 
1416             TotalLength = UncProvider->DeviceName.Length + FileObject->FileName.Length;
1417             if (UncProvider->MailslotsSupported && TotalLength <= MAXUSHORT)
1418             {
1419                 /* Provide the correct name for the mailslot (ie, happened the device name of the provider) */
1420                 FullPath.Buffer = ExAllocatePoolWithTag(PagedPool, TotalLength, TAG_MUP);
1421                 if (FullPath.Buffer == NULL)
1422                 {
1423                     ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
1424                 }
1425 
1426                 FullPath.Length = TotalLength;
1427                 FullPath.MaximumLength = TotalLength;
1428                 RtlMoveMemory(FullPath.Buffer, UncProvider->DeviceName.Buffer, UncProvider->DeviceName.Length);
1429                 RtlMoveMemory((PWSTR)((ULONG_PTR)FullPath.Buffer + UncProvider->DeviceName.Length),
1430                               FileObject->FileName.Buffer,
1431                               FileObject->FileName.Length);
1432 
1433                 /* And just forward the creation request */
1434                 InitializeObjectAttributes(&ObjectAttributes,
1435                                            &FullPath,
1436                                            OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
1437                                            NULL,
1438                                            NULL);
1439                 Status = IoCreateFile(&Handle,
1440                                       Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_SIMPLE_RIGHTS_MASK,
1441                                       &ObjectAttributes,
1442                                       &IoStatusBlock,
1443                                       NULL,
1444                                       Stack->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS,
1445                                       Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS,
1446                                       FILE_OPEN,
1447                                       Stack->Parameters.Create.Options & FILE_VALID_SET_FLAGS,
1448                                       NULL,
1449                                       0,
1450                                       CreateFileTypeNone,
1451                                       NULL,
1452                                       IO_NO_PARAMETER_CHECKING);
1453 
1454                 ExFreePoolWithTag(FullPath.Buffer, TAG_MUP);
1455 
1456                 /* If opening succeed */
1457                 if (NT_SUCCESS(Status))
1458                 {
1459                     Status = IoStatusBlock.Status;
1460 
1461                     /* Create a CCB */
1462                     Ccb = MupCreateCcb();
1463                     if (Ccb == NULL)
1464                     {
1465                         Status = STATUS_INSUFFICIENT_RESOURCES;
1466                     }
1467 
1468                     /* And associated a FO to it */
1469                     if (NT_SUCCESS(Status))
1470                     {
1471                         Status = ObReferenceObjectByHandle(Handle, 0, 0, 0, (PVOID *)&Ccb->FileObject, &HandleInfo);
1472                         ZwClose(Handle);
1473                     }
1474                 }
1475 
1476                 /* If we failed, remember the last failed status of the higher priority provider */
1477                 if (!NT_SUCCESS(Status))
1478                 {
1479                     if (UncProvider->ProviderOrder <= LastOrder)
1480                     {
1481                         LastOrder = UncProvider->ProviderOrder;
1482                         LastFailed = Status;
1483                     }
1484                 }
1485                 /* Otherwise, properly attach our CCB to the mailslot */
1486                 else
1487                 {
1488                     Ccb->DeviceObject = IoGetRelatedDeviceObject(Ccb->FileObject);
1489                     Ccb->Fcb = Fcb;
1490 
1491                     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1492                     Locked = TRUE;
1493                     ++Fcb->NodeReferences;
1494                     ExReleaseResourceLite(&MupGlobalLock);
1495                     Locked = FALSE;
1496                     CcbInitialized = TRUE;
1497 
1498                     InsertTailList(&Fcb->CcbList, &Ccb->CcbListEntry);
1499                 }
1500             }
1501 
1502             ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1503             Locked = TRUE;
1504             MupDereferenceUncProvider(UncProvider);
1505             Referenced = FALSE;
1506         }
1507 
1508         ExReleaseResourceLite(&MupGlobalLock);
1509         Locked = FALSE;
1510     }
1511     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1512     {
1513         Status = _SEH2_GetExceptionCode();
1514     }
1515     _SEH2_END;
1516 
1517     /* If we at least opened one mailslot, return success */
1518     Status = (CcbInitialized ? STATUS_SUCCESS : LastFailed);
1519 
1520     if (Referenced)
1521     {
1522         MupDereferenceUncProvider(UncProvider);
1523     }
1524 
1525     if (Locked)
1526     {
1527         ExReleaseResourceLite(&MupGlobalLock);
1528     }
1529 
1530     /* In case of failure, don't leak CCB */
1531     if (!NT_SUCCESS(Status) && Ccb != NULL)
1532     {
1533         MupFreeNode(Ccb);
1534     }
1535 
1536     return Status;
1537 }
1538 
1539 PIRP
1540 MupBuildIoControlRequest(PFILE_OBJECT FileObject,
1541                          PVOID Context,
1542                          ULONG MajorFunction,
1543                          ULONG IoctlCode,
1544                          PVOID InputBuffer,
1545                          ULONG InputBufferSize,
1546                          PVOID OutputBuffer,
1547                          ULONG OutputBufferSize,
1548                          PIO_COMPLETION_ROUTINE CompletionRoutine)
1549 {
1550     PIRP Irp;
1551     PIO_STACK_LOCATION Stack;
1552     PDEVICE_OBJECT DeviceObject;
1553 
1554     if (InputBuffer == NULL)
1555     {
1556         return NULL;
1557     }
1558 
1559     /* Get the device object */
1560     DeviceObject = IoGetRelatedDeviceObject(FileObject);
1561     /* Allocate the IRP (with one more location for us */
1562     Irp = IoAllocateIrp(DeviceObject->StackSize + 1, FALSE);
1563     if (Irp == NULL)
1564     {
1565         return NULL;
1566     }
1567 
1568     /* Skip our location */
1569     IoSetNextIrpStackLocation(Irp);
1570     /* Setup the IRP */
1571     Irp->Tail.Overlay.OriginalFileObject = FileObject;
1572     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
1573     IoSetCompletionRoutine(Irp, CompletionRoutine, Context, TRUE, TRUE, TRUE);
1574 
1575     /* Setup the stack */
1576     Stack = IoGetNextIrpStackLocation(Irp);
1577     Stack->MajorFunction = MajorFunction;
1578     Stack->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferSize;
1579     Stack->Parameters.DeviceIoControl.InputBufferLength = InputBufferSize;
1580     Stack->Parameters.DeviceIoControl.IoControlCode = IoctlCode;
1581     Stack->MinorFunction = 0;
1582     Stack->FileObject = FileObject;
1583     Stack->DeviceObject = DeviceObject;
1584 
1585     switch (IO_METHOD_FROM_CTL_CODE(IoctlCode))
1586     {
1587         case METHOD_BUFFERED:
1588             /* If it's buffered, just pass the buffers we got */
1589             Irp->MdlAddress = NULL;
1590             Irp->AssociatedIrp.SystemBuffer = InputBuffer;
1591             Irp->UserBuffer = OutputBuffer;
1592             Irp->Flags = IRP_BUFFERED_IO;
1593 
1594             if (OutputBuffer != NULL)
1595             {
1596                 Irp->Flags |= IRP_INPUT_OPERATION;
1597             }
1598             break;
1599 
1600         case METHOD_IN_DIRECT:
1601         case METHOD_OUT_DIRECT:
1602             /* Otherwise, allocate an MDL */
1603             if (IoAllocateMdl(InputBuffer, InputBufferSize, FALSE, FALSE, Irp) == NULL)
1604             {
1605                 IoFreeIrp(Irp);
1606                 return NULL;
1607             }
1608 
1609             Irp->AssociatedIrp.SystemBuffer = InputBuffer;
1610             Irp->Flags = IRP_BUFFERED_IO;
1611             MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess);
1612             break;
1613 
1614         case METHOD_NEITHER:
1615             /* Or pass the buffers */
1616             Irp->UserBuffer = OutputBuffer;
1617             Irp->MdlAddress = NULL;
1618             Irp->AssociatedIrp.SystemBuffer = NULL;
1619             Stack->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer;
1620             break;
1621     }
1622 
1623     return Irp;
1624 }
1625 
1626 VOID
1627 MupFreeMasterQueryContext(PMUP_MQC MasterQueryContext)
1628 {
1629     ExDeleteResourceLite(&MasterQueryContext->QueryPathListLock);
1630     ExFreePoolWithTag(MasterQueryContext, TAG_MUP);
1631 }
1632 
1633 NTSTATUS
1634 MupDereferenceMasterQueryContext(PMUP_MQC MasterQueryContext)
1635 {
1636     LONG References;
1637     NTSTATUS Status;
1638     BOOLEAN KeepExtraRef;
1639 
1640     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1641     --MasterQueryContext->NodeReferences;
1642     References = MasterQueryContext->NodeReferences;
1643     ExReleaseResourceLite(&MupGlobalLock);
1644 
1645     if (References != 0)
1646     {
1647         DPRINT("Still having refs (%ld)\n", References);
1648         return STATUS_PENDING;
1649     }
1650 
1651     /* We HAVE an IRP to complete. It cannot be NULL
1652      * Please, help preserving kittens, don't provide NULL IRPs.
1653      */
1654     if (MasterQueryContext->Irp == NULL)
1655     {
1656         KeBugCheck(FILE_SYSTEM);
1657     }
1658 
1659     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
1660     RemoveEntryList(&MasterQueryContext->MQCListEntry);
1661     ExReleaseResourceLite(&MupGlobalLock);
1662 
1663     ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
1664     KeepExtraRef = MasterQueryContext->Prefix->KeepExtraRef;
1665     MupDereferenceKnownPrefix(MasterQueryContext->Prefix);
1666 
1667     /* We found a provider? */
1668     if (MasterQueryContext->LatestProvider != NULL)
1669     {
1670         /* With a successful status? */
1671         if (MasterQueryContext->LatestStatus == STATUS_SUCCESS)
1672         {
1673             /* Then, it's time to reroute, someone accepted to handle the file creation request! */
1674             if (!KeepExtraRef)
1675             {
1676                 MupDereferenceKnownPrefix(MasterQueryContext->Prefix);
1677             }
1678 
1679             ExReleaseResourceLite(&MupPrefixTableLock);
1680             /* Reroute & complete :-) */
1681             Status = MupRerouteOpen(MasterQueryContext->FileObject, MasterQueryContext->LatestProvider);
1682             goto Complete;
1683         }
1684         else
1685         {
1686             MupDereferenceUncProvider(MasterQueryContext->LatestProvider);
1687         }
1688     }
1689 
1690     MupDereferenceKnownPrefix(MasterQueryContext->Prefix);
1691     ExReleaseResourceLite(&MupPrefixTableLock);
1692 
1693     /* Return the highest failed status we had */
1694     Status = MasterQueryContext->LatestStatus;
1695 
1696 Complete:
1697     /* In finally, complete the IRP for real! */
1698     MasterQueryContext->Irp->IoStatus.Status = Status;
1699     IoCompleteRequest(MasterQueryContext->Irp, IO_DISK_INCREMENT);
1700 
1701     MasterQueryContext->Irp = NULL;
1702     MupFreeMasterQueryContext(MasterQueryContext);
1703 
1704     return Status;
1705 }
1706 
1707 NTSTATUS
1708 NTAPI
1709 QueryPathCompletionRoutine(PDEVICE_OBJECT DeviceObject,
1710                            PIRP Irp,
1711                            PVOID Context)
1712 {
1713     PMUP_PFX Prefix;
1714     ULONG LatestPos, Pos;
1715     PWSTR AcceptedPrefix;
1716     PMUP_MQC MasterQueryContext;
1717     NTSTATUS Status, TableStatus;
1718     PQUERY_PATH_CONTEXT QueryContext;
1719     PQUERY_PATH_RESPONSE QueryResponse;
1720 
1721     /* Get all the data from our query to the provider */
1722     QueryContext = (PQUERY_PATH_CONTEXT)Context;
1723     QueryResponse = (PQUERY_PATH_RESPONSE)QueryContext->QueryPathRequest;
1724     MasterQueryContext = QueryContext->MasterQueryContext;
1725     Status = Irp->IoStatus.Status;
1726 
1727     DPRINT("Reply from %wZ: %u (Status: %lx)\n", &QueryContext->UncProvider->DeviceName, QueryResponse->LengthAccepted, Status);
1728 
1729     ExAcquireResourceExclusiveLite(&MasterQueryContext->QueryPathListLock, TRUE);
1730     RemoveEntryList(&QueryContext->QueryPathListEntry);
1731 
1732     /* If the driver returned a success, and an acceptance length */
1733     if (NT_SUCCESS(Status) && QueryResponse->LengthAccepted > 0)
1734     {
1735         Prefix = MasterQueryContext->Prefix;
1736 
1737         /* Check if we already found a provider from a previous iteration */
1738         if (MasterQueryContext->LatestProvider != NULL)
1739         {
1740            /* If the current provider has a lower priority (ie, a greater order), then, bailout and keep previous one */
1741             if (QueryContext->UncProvider->ProviderOrder >= MasterQueryContext->LatestProvider->ProviderOrder)
1742             {
1743                 MupDereferenceUncProvider(QueryContext->UncProvider);
1744                 goto Cleanup;
1745             }
1746 
1747             /* Otherwise, if the prefix was in the prefix table, just drop it:
1748              * we have a provider which supersedes the accepted prefix, so leave
1749              * room for the new prefix/provider
1750              */
1751             ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
1752             if (Prefix->InTable)
1753             {
1754                 RtlRemoveUnicodePrefix(&MupPrefixTable, &Prefix->PrefixTableEntry);
1755                 RemoveEntryList(&Prefix->PrefixListEntry);
1756                 Prefix->InTable = FALSE;
1757             }
1758             ExReleaseResourceLite(&MupPrefixTableLock);
1759 
1760             Prefix->KeepExtraRef = FALSE;
1761 
1762             /* Release data associated with the current prefix, if any
1763              * We'll renew them with the new accepted prefix
1764              */
1765             if (Prefix->AcceptedPrefix.Length != 0 && Prefix->AcceptedPrefix.Buffer != NULL)
1766             {
1767                 ExFreePoolWithTag(Prefix->AcceptedPrefix.Buffer, TAG_MUP);
1768                 Prefix->AcceptedPrefix.MaximumLength = 0;
1769                 Prefix->AcceptedPrefix.Length = 0;
1770                 Prefix->AcceptedPrefix.Buffer = NULL;
1771                 Prefix->ExternalAlloc = FALSE;
1772             }
1773 
1774             /* If there was also a provider, drop it, the new one
1775              * is different
1776              */
1777             if (Prefix->UncProvider != NULL)
1778             {
1779                 MupDereferenceUncProvider(Prefix->UncProvider);
1780                 Prefix->UncProvider = NULL;
1781             }
1782         }
1783 
1784         /* Now, set our information about the provider that accepted the prefix */
1785         MasterQueryContext->LatestProvider = QueryContext->UncProvider;
1786         MasterQueryContext->LatestStatus = Status;
1787 
1788         if (MasterQueryContext->FileObject->FsContext2 != (PVOID)DFS_DOWNLEVEL_OPEN_CONTEXT)
1789         {
1790             /* Allocate a buffer for the prefix */
1791             AcceptedPrefix = ExAllocatePoolWithTag(PagedPool, QueryResponse->LengthAccepted, TAG_MUP);
1792             if (AcceptedPrefix == NULL)
1793             {
1794                 Prefix->InTable = FALSE;
1795             }
1796             else
1797             {
1798                 /* Set it up to the accepted length */
1799                 RtlMoveMemory(AcceptedPrefix, MasterQueryContext->FileObject->FileName.Buffer, QueryResponse->LengthAccepted);
1800                 Prefix->UncProvider = MasterQueryContext->LatestProvider;
1801                 Prefix->AcceptedPrefix.Buffer = AcceptedPrefix;
1802                 Prefix->AcceptedPrefix.Length = QueryResponse->LengthAccepted;
1803                 Prefix->AcceptedPrefix.MaximumLength = QueryResponse->LengthAccepted;
1804                 Prefix->ExternalAlloc = TRUE;
1805 
1806                 /* Insert the accepted prefix in the table of known prefixes */
1807                 DPRINT1("%wZ accepted %wZ\n", &Prefix->UncProvider->DeviceName, &Prefix->AcceptedPrefix);
1808                 ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
1809                 if (RtlInsertUnicodePrefix(&MupPrefixTable, &Prefix->AcceptedPrefix, &Prefix->PrefixTableEntry))
1810                 {
1811                     InsertHeadList(&MupPrefixList, &Prefix->PrefixListEntry);
1812                     Prefix->InTable = TRUE;
1813                     Prefix->KeepExtraRef = TRUE;
1814                 }
1815                 else
1816                 {
1817                     Prefix->InTable = FALSE;
1818                 }
1819                 ExReleaseResourceLite(&MupPrefixTableLock);
1820             }
1821         }
1822     }
1823     else
1824     {
1825         MupDereferenceUncProvider(QueryContext->UncProvider);
1826 
1827         /* We failed and didn't find any provider over the latest iterations */
1828         if (MasterQueryContext->LatestProvider == NULL)
1829         {
1830             /* If we had a success though (broken provider?) set our failed status */
1831             if (NT_SUCCESS(MasterQueryContext->LatestStatus))
1832             {
1833                 MasterQueryContext->LatestStatus = Status;
1834             }
1835             else
1836             {
1837                 TableStatus = MupOrderedErrorList[0];
1838                 LatestPos = 0;
1839 
1840                 /* Otherwise, time to compare statuses, between the latest failed
1841                  * and the current failure.
1842                  * We have an order table of failed status: the deeper you go in the
1843                  * table, the more the error is critical.
1844                  * Our goal is to return the most critical status that was returned by
1845                  * any of the providers
1846                  */
1847 
1848                 /* Look for latest status position */
1849                 while (TableStatus != 0 && TableStatus != MasterQueryContext->LatestStatus)
1850                 {
1851                     ++LatestPos;
1852                     TableStatus = MupOrderedErrorList[LatestPos];
1853                 }
1854 
1855                 /* If at pos 0, the new status is likely more critical */
1856                 if (LatestPos == 0)
1857                 {
1858                     MasterQueryContext->LatestStatus = Status;
1859                 }
1860                 else
1861                 {
1862                     /* Otherwise, find position of the new status in the table */
1863                     Pos = 0;
1864                     do
1865                     {
1866                         if (Status == MupOrderedErrorList[Pos])
1867                         {
1868                             break;
1869                         }
1870 
1871                         ++Pos;
1872                     }
1873                     while (Pos < LatestPos);
1874 
1875                     /* If it has a higher position (more critical), return it */
1876                     if (Pos >= LatestPos)
1877                     {
1878                         MasterQueryContext->LatestStatus = Status;
1879                     }
1880                 }
1881             }
1882         }
1883     }
1884 
1885 Cleanup:
1886     ExFreePoolWithTag(QueryResponse, TAG_MUP);
1887     ExFreePoolWithTag(QueryContext, TAG_MUP);
1888     IoFreeIrp(Irp);
1889 
1890     ExReleaseResourceLite(&MasterQueryContext->QueryPathListLock);
1891     MupDereferenceMasterQueryContext(MasterQueryContext);
1892 
1893     return STATUS_MORE_PROCESSING_REQUIRED;
1894 }
1895 
1896 NTSTATUS
1897 CreateRedirectedFile(PIRP Irp,
1898                      PFILE_OBJECT FileObject,
1899                      PIO_SECURITY_CONTEXT SecurityContext)
1900 {
1901     LONG Len;
1902     WCHAR Cur;
1903     PWSTR Name;
1904     PIRP QueryIrp;
1905     NTSTATUS Status;
1906     PMUP_PFX Prefix;
1907     PLIST_ENTRY Entry;
1908     PMUP_UNC UncProvider;
1909     PIO_STACK_LOCATION Stack;
1910     LARGE_INTEGER CurrentTime;
1911     PMUP_MQC MasterQueryContext;
1912     PQUERY_PATH_CONTEXT QueryContext;
1913     PQUERY_PATH_REQUEST QueryPathRequest;
1914     PUNICODE_PREFIX_TABLE_ENTRY TableEntry;
1915     BOOLEAN Locked, Referenced, BreakOnFirst;
1916 
1917     /* We cannot open a file without a name */
1918     if (FileObject->FileName.Length == 0)
1919     {
1920         Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
1921         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1922 
1923         return STATUS_INVALID_DEVICE_REQUEST;
1924     }
1925 
1926     DPRINT1("Request for opening: %wZ\n", &FileObject->FileName);
1927 
1928     Referenced = FALSE;
1929     BreakOnFirst = TRUE;
1930     Status = STATUS_BAD_NETWORK_PATH;
1931 
1932     ExAcquireResourceExclusiveLite(&MupPrefixTableLock, TRUE);
1933     /* First, try to see if that's a prefix we already know */
1934     TableEntry = RtlFindUnicodePrefix(&MupPrefixTable, &FileObject->FileName, 1);
1935     if (TableEntry != NULL)
1936     {
1937         Prefix = CONTAINING_RECORD(TableEntry, MUP_PFX, PrefixTableEntry);
1938 
1939         DPRINT("Matching prefix found: %wZ\n", &Prefix->AcceptedPrefix);
1940 
1941         /* If so, check whether the prefix is still valid */
1942         KeQuerySystemTime(&CurrentTime);
1943         if (Prefix->ValidityTimeout.QuadPart < CurrentTime.QuadPart)
1944         {
1945             /* It is: so, update its validity period and reroute file opening */
1946             MupCalculateTimeout(&Prefix->ValidityTimeout);
1947             Status = MupRerouteOpen(FileObject, Prefix->UncProvider);
1948             ExReleaseResourceLite(&MupPrefixTableLock);
1949 
1950             if (Status == STATUS_REPARSE)
1951             {
1952                 Irp->IoStatus.Information = FILE_SUPERSEDED;
1953             }
1954 
1955             Irp->IoStatus.Status = Status;
1956             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1957 
1958             return Status;
1959         }
1960 
1961         /* When here, we found a matching prefix, but expired, remove it from the table
1962          * We'll redo a full search
1963          */
1964         if (Prefix->InTable)
1965         {
1966             MupRemoveKnownPrefixEntry(Prefix);
1967         }
1968     }
1969     ExReleaseResourceLite(&MupPrefixTableLock);
1970 
1971     Stack = IoGetCurrentIrpStackLocation(Irp);
1972     /* First of all, start looking for a mailslot */
1973     if (FileObject->FileName.Buffer[0] == L'\\' && Stack->MajorFunction != IRP_MJ_CREATE)
1974     {
1975         Name = &FileObject->FileName.Buffer[1];
1976         Len = FileObject->FileName.Length;
1977 
1978         /* Skip the remote destination name */
1979         do
1980         {
1981             Len -= sizeof(WCHAR);
1982             if (Len <= 0)
1983             {
1984                 break;
1985             }
1986 
1987             Cur = *Name;
1988             ++Name;
1989         } while (Cur != L'\\');
1990         Len -= sizeof(WCHAR);
1991 
1992         /* If we still have room for "Mailslot" to fit */
1993         if (Len >= (sizeof(L"Mailslot") - sizeof(UNICODE_NULL)))
1994         {
1995             /* Get the len in terms of chars count */
1996             Len /= sizeof(WCHAR);
1997             if (Len > ((sizeof(L"Mailslot") - sizeof(UNICODE_NULL)) / sizeof(WCHAR)))
1998             {
1999                 Len = (sizeof(L"Mailslot") - sizeof(UNICODE_NULL)) / sizeof(WCHAR);
2000             }
2001 
2002             /* It's indeed a mailslot opening! */
2003             if (_wcsnicmp(Name, L"Mailslot", Len) == 0)
2004             {
2005                 /* Broadcast open */
2006                 Status = BroadcastOpen(Irp);
2007                 if (Status == STATUS_REPARSE)
2008                 {
2009                     Irp->IoStatus.Information = FILE_SUPERSEDED;
2010                 }
2011 
2012                 Irp->IoStatus.Status = Status;
2013                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2014 
2015                 return Status;
2016             }
2017         }
2018     }
2019 
2020     /* Ok, at that point, that's a regular MUP opening (if no DFS) */
2021     if (!MupEnableDfs || FileObject->FsContext2 == (PVOID)DFS_DOWNLEVEL_OPEN_CONTEXT)
2022     {
2023         /* We won't complete immediately */
2024         IoMarkIrpPending(Irp);
2025 
2026         /* Allocate a new prefix for our search */
2027         Prefix = MupAllocatePrefixEntry(0);
2028         if (Prefix == NULL)
2029         {
2030             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
2031             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2032 
2033             return STATUS_PENDING;
2034         }
2035 
2036         /* Allocate a context for our search */
2037         MasterQueryContext = MupAllocateMasterQueryContext();
2038         if (MasterQueryContext == NULL)
2039         {
2040             MupFreeNode(Prefix);
2041 
2042             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
2043             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2044 
2045             return STATUS_PENDING;
2046         }
2047 
2048         MasterQueryContext->Irp = Irp;
2049         MasterQueryContext->FileObject = FileObject;
2050         MasterQueryContext->LatestProvider = NULL;
2051         MasterQueryContext->Prefix = Prefix;
2052         MasterQueryContext->LatestStatus = STATUS_BAD_NETWORK_PATH;
2053         ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2054         InsertTailList(&MupMasterQueryList, &MasterQueryContext->MQCListEntry);
2055         ++Prefix->NodeReferences;
2056         ExReleaseResourceLite(&MupGlobalLock);
2057 
2058         _SEH2_TRY
2059         {
2060             ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2061             Locked = TRUE;
2062 
2063             /* Now, we will browse all the providers we know, to ask for their accepted prefix regarding the path */
2064             for (Entry = MupProviderList.Flink; Entry != &MupProviderList; Entry = Entry->Flink)
2065             {
2066                 UncProvider = CONTAINING_RECORD(Entry, MUP_UNC, ProviderListEntry);
2067 
2068                 ++UncProvider->NodeReferences;
2069                 Referenced = TRUE;
2070 
2071                 ExReleaseResourceLite(&MupGlobalLock);
2072                 Locked = FALSE;
2073 
2074                 /* We will obviously only query registered providers */
2075                 if (UncProvider->Registered)
2076                 {
2077                     /* We will issue an IOCTL_REDIR_QUERY_PATH, so allocate input buffer */
2078                     QueryPathRequest = ExAllocatePoolWithTag(PagedPool, FileObject->FileName.Length + sizeof(QUERY_PATH_REQUEST), TAG_MUP);
2079                     if (QueryPathRequest == NULL)
2080                     {
2081                         ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
2082                     }
2083 
2084                     /* Allocate a context for IRP completion routine
2085                      * In case a prefix matches the path, the reroute will happen
2086                      * in the completion routine, when we have return from the provider
2087                      */
2088                     QueryContext = ExAllocatePoolWithTag(PagedPool, sizeof(QUERY_PATH_CONTEXT), TAG_MUP);
2089                     if (QueryContext == NULL)
2090                     {
2091                         ExFreePoolWithTag(QueryPathRequest, TAG_MUP);
2092                         ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
2093                     }
2094 
2095                     InitializeListHead(&QueryContext->QueryPathListEntry);
2096                     QueryContext->MasterQueryContext = MasterQueryContext;
2097                     QueryContext->QueryPathRequest = QueryPathRequest;
2098                     QueryPathRequest->PathNameLength = FileObject->FileName.Length;
2099                     QueryPathRequest->SecurityContext = SecurityContext;
2100                     RtlMoveMemory(QueryPathRequest->FilePathName, FileObject->FileName.Buffer, FileObject->FileName.Length);
2101 
2102                     /* Build our IRP for the query */
2103                     QueryIrp = MupBuildIoControlRequest(UncProvider->FileObject,
2104                                                         QueryContext,
2105                                                         IRP_MJ_DEVICE_CONTROL,
2106                                                         IOCTL_REDIR_QUERY_PATH,
2107                                                         QueryPathRequest,
2108                                                         FileObject->FileName.Length + sizeof(QUERY_PATH_REQUEST),
2109                                                         QueryPathRequest,
2110                                                         sizeof(QUERY_PATH_RESPONSE),
2111                                                         QueryPathCompletionRoutine);
2112                     if (QueryIrp == NULL)
2113                     {
2114                         ExFreePoolWithTag(QueryContext, TAG_MUP);
2115                         ExFreePoolWithTag(QueryPathRequest, TAG_MUP);
2116                         ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
2117                     }
2118 
2119                     QueryIrp->RequestorMode = KernelMode;
2120                     QueryContext->UncProvider = UncProvider;
2121                     QueryContext->Irp = QueryIrp;
2122 
2123                     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2124                     ++UncProvider->NodeReferences;
2125                     ++MasterQueryContext->NodeReferences;
2126                     ExReleaseResourceLite(&MupGlobalLock);
2127 
2128                     ExAcquireResourceExclusiveLite(&MasterQueryContext->QueryPathListLock, TRUE);
2129                     InsertTailList(&MasterQueryContext->QueryPathList, &QueryContext->QueryPathListEntry);
2130                     ExReleaseResourceLite(&MasterQueryContext->QueryPathListLock);
2131 
2132                     /* Query the provider !*/
2133                     DPRINT1("Requesting UNC provider: %wZ\n", &UncProvider->DeviceName);
2134                     DPRINT("Calling: %wZ\n", &UncProvider->DeviceObject->DriverObject->DriverName);
2135                     Status = IoCallDriver(UncProvider->DeviceObject, QueryIrp);
2136                 }
2137 
2138                 ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2139                 Locked = TRUE;
2140 
2141                 /* We're done with that provider */
2142                 MupDereferenceUncProvider(UncProvider);
2143                 Referenced = FALSE;
2144 
2145                 /* If query went fine on the first request, just break and leave */
2146                 if (BreakOnFirst && Status == STATUS_SUCCESS)
2147                 {
2148                     break;
2149                 }
2150 
2151                 BreakOnFirst = FALSE;
2152             }
2153         }
2154         _SEH2_FINALLY
2155         {
2156             if (_SEH2_AbnormalTermination())
2157             {
2158                 MasterQueryContext->LatestStatus = STATUS_INSUFFICIENT_RESOURCES;
2159             }
2160 
2161             if (Referenced)
2162             {
2163                 MupDereferenceUncProvider(UncProvider);
2164             }
2165 
2166             if (Locked)
2167             {
2168                 ExReleaseResourceLite(&MupGlobalLock);
2169             }
2170 
2171             MupDereferenceMasterQueryContext(MasterQueryContext);
2172 
2173             Status = STATUS_PENDING;
2174         }
2175         _SEH2_END;
2176     }
2177     else
2178     {
2179         UNIMPLEMENTED;
2180         Status = STATUS_NOT_IMPLEMENTED;
2181     }
2182 
2183     return Status;
2184 }
2185 
2186 NTSTATUS
2187 OpenMupFileSystem(PMUP_VCB Vcb,
2188                   PFILE_OBJECT FileObject,
2189                   ACCESS_MASK DesiredAccess,
2190                   USHORT ShareAccess)
2191 {
2192     NTSTATUS Status;
2193 
2194     DPRINT1("Opening MUP\n");
2195 
2196     ExAcquireResourceExclusiveLite(&MupVcbLock, TRUE);
2197     _SEH2_TRY
2198     {
2199         /* Update share access, increase reference count, and associated VCB to the FO, that's it! */
2200         Status = IoCheckShareAccess(DesiredAccess, ShareAccess, FileObject, &Vcb->ShareAccess, TRUE);
2201         if (NT_SUCCESS(Status))
2202         {
2203             ++Vcb->NodeReferences;
2204             MupSetFileObject(FileObject, (PMUP_FCB)Vcb, NULL);
2205             Status = STATUS_SUCCESS;
2206         }
2207     }
2208     _SEH2_FINALLY
2209     {
2210         ExReleaseResourceLite(&MupVcbLock);
2211     }
2212     _SEH2_END;
2213 
2214     return Status;
2215 }
2216 
2217 NTSTATUS
2218 NTAPI
2219 MupCreate(PDEVICE_OBJECT DeviceObject,
2220           PIRP Irp)
2221 {
2222     NTSTATUS Status;
2223     PIO_STACK_LOCATION Stack;
2224     PFILE_OBJECT FileObject, RelatedFileObject;
2225 
2226     FsRtlEnterFileSystem();
2227 
2228     _SEH2_TRY
2229     {
2230         /* If DFS is enabled, check if that's for DFS and is so relay */
2231         if (MupEnableDfs && (DeviceObject->DeviceType == FILE_DEVICE_DFS || DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM))
2232         {
2233             Status = DfsFsdCreate(DeviceObject, Irp);
2234         }
2235         else
2236         {
2237             Stack = IoGetCurrentIrpStackLocation(Irp);
2238             FileObject = Stack->FileObject;
2239             RelatedFileObject = FileObject->RelatedFileObject;
2240 
2241             /* 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 */
2242             if (FileObject->FileName.Length != 0 || (RelatedFileObject != NULL && ((PMUP_FCB)(RelatedFileObject->FsContext))->NodeType != NODE_TYPE_VCB))
2243             {
2244                 Status = CreateRedirectedFile(Irp, FileObject, Stack->Parameters.Create.SecurityContext);
2245             }
2246             /* Otherwise, it's just a volume open */
2247             else
2248             {
2249                 Status = OpenMupFileSystem(DeviceObject->DeviceExtension,
2250                                            FileObject,
2251                                            Stack->Parameters.Create.SecurityContext->DesiredAccess,
2252                                            Stack->Parameters.Create.ShareAccess);
2253 
2254                 Irp->IoStatus.Information = FILE_OPENED;
2255                 Irp->IoStatus.Status = Status;
2256                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2257             }
2258         }
2259     }
2260     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2261     {
2262         Status = _SEH2_GetExceptionCode();
2263 
2264         Irp->IoStatus.Status = Status;
2265         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2266     }
2267     _SEH2_END;
2268 
2269     FsRtlExitFileSystem();
2270 
2271     return Status;
2272 }
2273 
2274 VOID
2275 MupCloseUncProvider(PMUP_UNC UncProvider)
2276 {
2277     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2278 
2279     /* If the node was still valid, reregister the UNC provider */
2280     if (UncProvider->NodeStatus == NODE_STATUS_HEALTHY)
2281     {
2282         UncProvider->NodeStatus = NODE_STATUS_CLEANUP;
2283         UncProvider->Registered = FALSE;
2284         ExReleaseResourceLite(&MupGlobalLock);
2285 
2286         if (UncProvider->FileObject != NULL)
2287         {
2288             ZwClose(UncProvider->DeviceHandle);
2289             ObDereferenceObject(UncProvider->FileObject);
2290         }
2291     }
2292     else
2293     {
2294         ExReleaseResourceLite(&MupGlobalLock);
2295     }
2296 }
2297 
2298 NTSTATUS
2299 NTAPI
2300 MupCleanup(PDEVICE_OBJECT DeviceObject,
2301            PIRP Irp)
2302 {
2303     ULONG Type;
2304     PMUP_FCB Fcb;
2305     PMUP_CCB Ccb;
2306     NTSTATUS Status;
2307     PIO_STACK_LOCATION Stack;
2308 
2309     /* If DFS is enabled, check if that's for DFS and is so relay */
2310     if (MupEnableDfs)
2311     {
2312         if (DeviceObject->DeviceType == FILE_DEVICE_DFS || DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM)
2313         {
2314             return DfsFsdCleanup(DeviceObject, Irp);
2315         }
2316     }
2317 
2318     FsRtlEnterFileSystem();
2319 
2320     _SEH2_TRY
2321     {
2322         Stack = IoGetCurrentIrpStackLocation(Irp);
2323         Type = MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb);
2324         switch (Type)
2325         {
2326             case NODE_TYPE_VCB:
2327                 /* If we got a VCB, clean it up */
2328                 MupCleanupVcb(DeviceObject, Irp, (PMUP_VCB)Fcb);
2329 
2330                 Irp->IoStatus.Status = STATUS_SUCCESS;
2331                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2332 
2333                 MupDereferenceVcb((PMUP_VCB)Fcb);
2334 
2335                 /* If Ccb is not null, then, it's a UNC provider node */
2336                 if (Ccb)
2337                 {
2338                     /* Close it, and dereference */
2339                     MupCloseUncProvider((PMUP_UNC)Ccb);
2340                     MupDereferenceUncProvider((PMUP_UNC)Ccb);
2341                     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2342                     --MupProviderCount;
2343                     ExReleaseResourceLite(&MupGlobalLock);
2344                 }
2345 
2346                 Status = STATUS_SUCCESS;
2347                 break;
2348 
2349             case NODE_TYPE_FCB:
2350                 /* If the node wasn't already cleaned, do it */
2351                 if (Fcb->NodeStatus == NODE_STATUS_HEALTHY)
2352                 {
2353                     MupCleanupFcb(DeviceObject, Irp, Fcb);
2354                     Status = STATUS_SUCCESS;
2355                 }
2356                 else
2357                 {
2358                     Status = STATUS_INVALID_HANDLE;
2359                 }
2360 
2361                 Irp->IoStatus.Status = STATUS_SUCCESS;
2362                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2363 
2364                 MupDereferenceFcb(Fcb);
2365                 break;
2366 
2367             default:
2368                 Status = STATUS_INVALID_HANDLE;
2369 
2370                 Irp->IoStatus.Status = Status;
2371                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2372 
2373                 break;
2374         }
2375     }
2376     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2377     {
2378         Status = _SEH2_GetExceptionCode();
2379     }
2380     _SEH2_END;
2381 
2382     FsRtlExitFileSystem();
2383 
2384     return Status;
2385 }
2386 
2387 NTSTATUS
2388 MupCloseVcb(PDEVICE_OBJECT DeviceObject,
2389             PIRP Irp,
2390             PMUP_VCB Vcb,
2391             PFILE_OBJECT FileObject)
2392 {
2393     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2394 
2395     /* Remove FCB, UNC from FO */
2396     MupSetFileObject(FileObject, NULL, NULL);
2397     MupDereferenceVcb(Vcb);
2398 
2399     ExReleaseResourceLite(&MupGlobalLock);
2400 
2401     return STATUS_SUCCESS;
2402 }
2403 
2404 NTSTATUS
2405 MupCloseFcb(PDEVICE_OBJECT DeviceObject,
2406             PIRP Irp,
2407             PMUP_FCB Fcb,
2408             PFILE_OBJECT FileObject)
2409 {
2410     ExAcquireResourceExclusiveLite(&MupGlobalLock, TRUE);
2411 
2412     /* Remove FCB, CCB from FO */
2413     MupSetFileObject(FileObject, NULL, NULL);
2414     MupDereferenceFcb(Fcb);
2415 
2416     ExReleaseResourceLite(&MupGlobalLock);
2417 
2418     return STATUS_SUCCESS;
2419 }
2420 
2421 NTSTATUS
2422 NTAPI
2423 MupClose(PDEVICE_OBJECT DeviceObject,
2424          PIRP Irp)
2425 {
2426     PMUP_FCB Fcb;
2427     PMUP_CCB Ccb;
2428     NTSTATUS Status;
2429     PIO_STACK_LOCATION Stack;
2430 
2431     /* If DFS is enabled, check if that's for DFS and is so relay */
2432     if (MupEnableDfs)
2433     {
2434         if (DeviceObject->DeviceType == FILE_DEVICE_DFS || DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM)
2435         {
2436             return DfsFsdClose(DeviceObject, Irp);
2437         }
2438     }
2439 
2440     FsRtlEnterFileSystem();
2441 
2442     _SEH2_TRY
2443     {
2444         /* Get our internal structures from FO */
2445         Stack = IoGetCurrentIrpStackLocation(Irp);
2446         MupDecodeFileObject(Stack->FileObject, &Fcb, &Ccb);
2447         if (Fcb == NULL)
2448         {
2449             Status = STATUS_INVALID_HANDLE;
2450 
2451             Irp->IoStatus.Status = Status;
2452             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2453 
2454             _SEH2_LEAVE;
2455         }
2456 
2457         /* If we got the VCB, that's a volume close */
2458         if (Fcb->NodeType == NODE_TYPE_VCB)
2459         {
2460             Status = MupCloseVcb(DeviceObject, Irp, (PMUP_VCB)Fcb, Stack->FileObject);
2461         }
2462         /* Otherwise close the FCB */
2463         else if (Fcb->NodeType == NODE_TYPE_FCB)
2464         {
2465             MupDereferenceFcb(Fcb);
2466             Status = MupCloseFcb(DeviceObject, Irp, Fcb, Stack->FileObject);
2467         }
2468         else
2469         {
2470             Status = STATUS_INVALID_HANDLE;
2471 
2472             Irp->IoStatus.Status = Status;
2473             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2474 
2475             _SEH2_LEAVE;
2476         }
2477 
2478         Irp->IoStatus.Status = STATUS_SUCCESS;
2479         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2480     }
2481     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2482     {
2483         Status = _SEH2_GetExceptionCode();
2484     }
2485     _SEH2_END;
2486 
2487     FsRtlExitFileSystem();
2488 
2489     return Status;
2490 }
2491 
2492 VOID
2493 NTAPI
2494 MupUnload(PDRIVER_OBJECT DriverObject)
2495 {
2496     IoDeleteDevice(mupDeviceObject);
2497 
2498     if (MupEnableDfs)
2499     {
2500         DfsUnload(DriverObject);
2501     }
2502 
2503     MupUninitializeData();
2504 }
2505 
2506 /*
2507  * FUNCTION: Called by the system to initialize the driver
2508  * ARGUMENTS:
2509  *           DriverObject = object describing this driver
2510  *           RegistryPath = path to our configuration entries
2511  * RETURNS: Success or failure
2512  */
2513 CODE_SEG("INIT")
2514 NTSTATUS
2515 NTAPI
2516 DriverEntry(PDRIVER_OBJECT DriverObject,
2517             PUNICODE_STRING RegistryPath)
2518 {
2519     NTSTATUS Status;
2520     UNICODE_STRING MupString;
2521     PDEVICE_OBJECT DeviceObject;
2522 
2523     /* Only initialize global state of the driver
2524      * Other inits will happen when required
2525      */
2526     MupInitializeData();
2527 
2528     /* Check if DFS is disabled */
2529     MupEnableDfs = MuppIsDfsEnabled();
2530     /* If it's not disabled but when cannot init, disable it */
2531     if (MupEnableDfs && !NT_SUCCESS(DfsDriverEntry(DriverObject, RegistryPath)))
2532     {
2533         MupEnableDfs = FALSE;
2534     }
2535 
2536     /* Create the MUP device */
2537     RtlInitUnicodeString(&MupString, L"\\Device\\Mup");
2538     Status = IoCreateDevice(DriverObject, sizeof(MUP_VCB), &MupString, FILE_DEVICE_MULTI_UNC_PROVIDER, 0, FALSE, &DeviceObject);
2539     if (!NT_SUCCESS(Status))
2540     {
2541         if (MupEnableDfs)
2542         {
2543             DfsUnload(DriverObject);
2544         }
2545 
2546         MupUninitializeData();
2547 
2548         return Status;
2549     }
2550 
2551     /* Set our MJ */
2552     DriverObject->DriverUnload = MupUnload;
2553     DriverObject->MajorFunction[IRP_MJ_CREATE] = MupCreate;
2554     DriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = MupCreate;
2555     DriverObject->MajorFunction[IRP_MJ_CREATE_MAILSLOT] = MupCreate;
2556     DriverObject->MajorFunction[IRP_MJ_WRITE] = MupForwardIoRequest;
2557     DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = MupFsControl;
2558     DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MupCleanup;
2559     DriverObject->MajorFunction[IRP_MJ_CLOSE] = MupClose;
2560 
2561     /* And finish init */
2562     mupDeviceObject = DeviceObject;
2563     MupInitializeVcb(DeviceObject->DeviceExtension);
2564 
2565     return STATUS_SUCCESS;
2566 }
2567