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