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