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