mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 00:13:01 -05:00
405 lines
12 KiB
C
405 lines
12 KiB
C
/**
|
|
* @file sys/silo.c
|
|
*
|
|
* @copyright 2015-2024 Bill Zissimopoulos
|
|
*/
|
|
/*
|
|
* This file is part of WinFsp.
|
|
*
|
|
* You can redistribute it and/or modify it under the terms of the GNU
|
|
* General Public License version 3 as published by the Free Software
|
|
* Foundation.
|
|
*
|
|
* Licensees holding a valid commercial license may use this software
|
|
* in accordance with the commercial license agreement provided in
|
|
* conjunction with the software. The terms and conditions of any such
|
|
* commercial license agreement shall govern, supersede, and render
|
|
* ineffective any application of the GPLv3 license to this software,
|
|
* notwithstanding of any reference thereto in the software or
|
|
* associated repository.
|
|
*/
|
|
|
|
#include <sys/driver.h>
|
|
|
|
NTSTATUS FspSiloInitialize(FSP_SILO_INIT_CALLBACK Init, FSP_SILO_FINI_CALLBACK Fini);
|
|
NTSTATUS FspSiloPostInitialize(VOID);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, FspSiloInitialize)
|
|
#pragma alloc_text(INIT, FspSiloPostInitialize)
|
|
#endif
|
|
|
|
typedef PEJOB FSP_PESILO;
|
|
typedef PVOID FSP_PSILO_MONITOR;
|
|
|
|
typedef NTSTATUS (NTAPI *FSP_SILO_MONITOR_CREATE_CALLBACK)(FSP_PESILO Silo);
|
|
typedef VOID (NTAPI *FSP_SILO_MONITOR_TERMINATE_CALLBACK)(FSP_PESILO Silo);
|
|
typedef VOID (NTAPI *FSP_SILO_CONTEXT_CLEANUP_CALLBACK)(PVOID SiloContext);
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4201) /* nameless struct/union */
|
|
typedef struct _FSP_SILO_MONITOR_REGISTRATION
|
|
{
|
|
UCHAR Version;
|
|
BOOLEAN MonitorHost;
|
|
BOOLEAN MonitorExistingSilos;
|
|
UCHAR Reserved[5];
|
|
union
|
|
{
|
|
PUNICODE_STRING DriverObjectName;
|
|
PUNICODE_STRING ComponentName;
|
|
};
|
|
FSP_SILO_MONITOR_CREATE_CALLBACK CreateCallback;
|
|
FSP_SILO_MONITOR_TERMINATE_CALLBACK TerminateCallback;
|
|
} FSP_SILO_MONITOR_REGISTRATION;
|
|
#pragma warning(pop)
|
|
|
|
typedef NTSTATUS FSP_SILO_PsRegisterSiloMonitor(
|
|
FSP_SILO_MONITOR_REGISTRATION *Registration,
|
|
FSP_PSILO_MONITOR *ReturnedMonitor);
|
|
typedef NTSTATUS FSP_SILO_PsStartSiloMonitor(
|
|
FSP_PSILO_MONITOR Monitor);
|
|
typedef VOID FSP_SILO_PsUnregisterSiloMonitor(
|
|
FSP_PSILO_MONITOR Monitor);
|
|
typedef ULONG FSP_SILO_PsGetSiloMonitorContextSlot(
|
|
FSP_PSILO_MONITOR Monitor);
|
|
typedef NTSTATUS FSP_SILO_PsCreateSiloContext(
|
|
FSP_PESILO Silo,
|
|
ULONG Size,
|
|
POOL_TYPE PoolType,
|
|
FSP_SILO_CONTEXT_CLEANUP_CALLBACK ContextCleanupCallback,
|
|
PVOID *ReturnedSiloContext);
|
|
typedef VOID FSP_SILO_PsDereferenceSiloContext(
|
|
PVOID SiloContext);
|
|
typedef NTSTATUS FSP_SILO_PsInsertSiloContext(
|
|
FSP_PESILO Silo,
|
|
ULONG ContextSlot,
|
|
PVOID SiloContext);
|
|
typedef NTSTATUS FSP_SILO_PsRemoveSiloContext(
|
|
FSP_PESILO Silo,
|
|
ULONG ContextSlot,
|
|
PVOID *RemovedSiloContext);
|
|
typedef NTSTATUS FSP_SILO_PsGetSiloContext(
|
|
FSP_PESILO Silo,
|
|
ULONG ContextSlot,
|
|
PVOID *ReturnedSiloContext);
|
|
typedef FSP_PESILO FSP_SILO_PsAttachSiloToCurrentThread(
|
|
FSP_PESILO Silo);
|
|
typedef VOID FSP_SILO_PsDetachSiloFromCurrentThread(
|
|
FSP_PESILO PreviousSilo);
|
|
typedef FSP_PESILO FSP_SILO_PsGetCurrentServerSilo(
|
|
VOID);
|
|
typedef GUID* FSP_SILO_PsGetSiloContainerId(
|
|
FSP_PESILO Silo);
|
|
|
|
static FSP_SILO_PsRegisterSiloMonitor *FspSiloPsRegisterSiloMonitor;
|
|
static FSP_SILO_PsStartSiloMonitor *FspSiloPsStartSiloMonitor;
|
|
static FSP_SILO_PsUnregisterSiloMonitor *FspSiloPsUnregisterSiloMonitor;
|
|
static FSP_SILO_PsGetSiloMonitorContextSlot *FspSiloPsGetSiloMonitorContextSlot;
|
|
static FSP_SILO_PsCreateSiloContext *FspSiloPsCreateSiloContext;
|
|
static FSP_SILO_PsDereferenceSiloContext *FspSiloPsDereferenceSiloContext;
|
|
static FSP_SILO_PsInsertSiloContext *FspSiloPsInsertSiloContext;
|
|
static FSP_SILO_PsRemoveSiloContext *FspSiloPsRemoveSiloContext;
|
|
static FSP_SILO_PsGetSiloContext *FspSiloPsGetSiloContext;
|
|
static FSP_SILO_PsAttachSiloToCurrentThread *FspSiloPsAttachSiloToCurrentThread;
|
|
static FSP_SILO_PsDetachSiloFromCurrentThread *FspSiloPsDetachSiloFromCurrentThread;
|
|
static FSP_SILO_PsGetCurrentServerSilo *FspSiloPsGetCurrentServerSilo;
|
|
static FSP_SILO_PsGetSiloContainerId *FspSiloPsGetSiloContainerId;
|
|
static FSP_PSILO_MONITOR FspSiloMonitor;
|
|
static FSP_SILO_INIT_CALLBACK FspSiloInitCallback;
|
|
static FSP_SILO_FINI_CALLBACK FspSiloFiniCallback;
|
|
static BOOLEAN FspSiloInitDone = FALSE;
|
|
|
|
static FAST_MUTEX FspSiloListMutex;
|
|
static LIST_ENTRY FspSiloList;
|
|
|
|
static FSP_SILO_GLOBALS FspSiloHostGlobals;
|
|
|
|
#define FSP_SILO_MONITOR_REGISTRATION_VERSION 1
|
|
|
|
#define LOAD(n) \
|
|
{ \
|
|
UNICODE_STRING Name; \
|
|
RtlInitUnicodeString(&Name, L"" #n);\
|
|
FspSilo ## n = \
|
|
(FSP_SILO_ ## n *)(UINT_PTR)MmGetSystemRoutineAddress(&Name);\
|
|
if (0 == FspSilo ## n) \
|
|
Fail++; \
|
|
}
|
|
#define CALL(n) (FspSilo ## n)
|
|
|
|
BOOLEAN FspSiloIsHost(VOID)
|
|
{
|
|
return !FspSiloInitDone || 0 == CALL(PsGetCurrentServerSilo)();
|
|
}
|
|
|
|
NTSTATUS FspSiloGetGlobals(FSP_SILO_GLOBALS **PGlobals)
|
|
{
|
|
FSP_PESILO Silo;
|
|
ULONG ContextSlot;
|
|
FSP_SILO_GLOBALS *Globals;
|
|
NTSTATUS Result;
|
|
|
|
if (!FspSiloInitDone ||
|
|
0 == (Silo = CALL(PsGetCurrentServerSilo)()))
|
|
{
|
|
*PGlobals = &FspSiloHostGlobals;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ContextSlot = CALL(PsGetSiloMonitorContextSlot)(FspSiloMonitor);
|
|
|
|
Result = CALL(PsGetSiloContext)(Silo, ContextSlot, &Globals);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
*PGlobals = 0;
|
|
return Result;
|
|
}
|
|
|
|
*PGlobals = Globals;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID FspSiloDereferenceGlobals(FSP_SILO_GLOBALS *Globals)
|
|
{
|
|
if (&FspSiloHostGlobals == Globals)
|
|
return;
|
|
|
|
CALL(PsDereferenceSiloContext)(Globals);
|
|
}
|
|
|
|
VOID FspSiloGetContainerId(GUID *ContainerId)
|
|
{
|
|
FSP_PESILO Silo;
|
|
GUID *Guid;
|
|
|
|
if (!FspSiloInitDone ||
|
|
0 == (Silo = CALL(PsGetCurrentServerSilo)()) ||
|
|
0 == (Guid = CALL(PsGetSiloContainerId)(Silo)))
|
|
{
|
|
RtlZeroMemory(ContainerId, sizeof *ContainerId);
|
|
return;
|
|
}
|
|
|
|
RtlCopyMemory(ContainerId, Guid, sizeof *ContainerId);
|
|
}
|
|
|
|
static NTSTATUS NTAPI FspSiloMonitorCreateCallback(FSP_PESILO Silo)
|
|
{
|
|
ULONG ContextSlot;
|
|
FSP_SILO_GLOBALS *Globals = 0;
|
|
BOOLEAN Inserted = FALSE;
|
|
NTSTATUS Result;
|
|
|
|
ASSERT(0 != Silo);
|
|
|
|
ContextSlot = CALL(PsGetSiloMonitorContextSlot)(FspSiloMonitor);
|
|
|
|
Result = CALL(PsCreateSiloContext)(Silo, sizeof(FSP_SILO_GLOBALS), NonPagedPoolNx, 0, &Globals);
|
|
if (!NT_SUCCESS(Result))
|
|
goto exit;
|
|
RtlZeroMemory(Globals, sizeof(FSP_SILO_GLOBALS));
|
|
Globals->Silo = Silo;
|
|
|
|
/* PsInsertSiloContext adds reference to Globals */
|
|
Result = CALL(PsInsertSiloContext)(Silo, ContextSlot, Globals);
|
|
if (!NT_SUCCESS(Result))
|
|
goto exit;
|
|
Inserted = TRUE;
|
|
|
|
FsRtlEnterFileSystem();
|
|
ExAcquireFastMutexUnsafe(&FspSiloListMutex);
|
|
|
|
if (0 != FspSiloInitCallback)
|
|
{
|
|
FSP_PESILO PreviousSilo = CALL(PsAttachSiloToCurrentThread)(Silo);
|
|
Result = FspSiloInitCallback();
|
|
CALL(PsDetachSiloFromCurrentThread)(PreviousSilo);
|
|
}
|
|
|
|
if (NT_SUCCESS(Result))
|
|
InsertTailList(&FspSiloList, &Globals->ListEntry);
|
|
|
|
ExReleaseFastMutexUnsafe(&FspSiloListMutex);
|
|
FsRtlExitFileSystem();
|
|
|
|
exit:
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
if (Inserted)
|
|
CALL(PsRemoveSiloContext)(Silo, ContextSlot, 0);
|
|
}
|
|
|
|
if (0 != Globals)
|
|
CALL(PsDereferenceSiloContext)(Globals);
|
|
|
|
/*
|
|
* Ignore errors and return success. There are two reasons for this:
|
|
*
|
|
* - Returning an error will disallow container creation.
|
|
* - In some cases returning an error will crash Windows with an
|
|
* unexpected page fault in wcifs.sys.
|
|
*/
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static VOID NTAPI FspSiloMonitorTerminateCallback(FSP_PESILO Silo)
|
|
{
|
|
ULONG ContextSlot;
|
|
FSP_SILO_GLOBALS *Globals;
|
|
NTSTATUS Result;
|
|
|
|
ASSERT(0 != Silo);
|
|
|
|
ContextSlot = CALL(PsGetSiloMonitorContextSlot)(FspSiloMonitor);
|
|
|
|
/* if we cannot get the Globals, it must mean that we failed in Create callback */
|
|
Result = CALL(PsGetSiloContext)(Silo, ContextSlot, &Globals);
|
|
if (!NT_SUCCESS(Result))
|
|
return;
|
|
|
|
FsRtlEnterFileSystem();
|
|
ExAcquireFastMutexUnsafe(&FspSiloListMutex);
|
|
|
|
RemoveEntryList(&Globals->ListEntry);
|
|
CALL(PsDereferenceSiloContext)(Globals);
|
|
Globals = 0;
|
|
|
|
if (0 != FspSiloFiniCallback)
|
|
{
|
|
FSP_PESILO PreviousSilo = CALL(PsAttachSiloToCurrentThread)(Silo);
|
|
FspSiloFiniCallback();
|
|
CALL(PsDetachSiloFromCurrentThread)(PreviousSilo);
|
|
}
|
|
|
|
ExReleaseFastMutexUnsafe(&FspSiloListMutex);
|
|
FsRtlExitFileSystem();
|
|
|
|
/* PsRemoveSiloContext removes reference to Globals (possibly freeing it) */
|
|
CALL(PsRemoveSiloContext)(Silo, ContextSlot, 0);
|
|
}
|
|
|
|
NTSTATUS FspSiloInitialize(FSP_SILO_INIT_CALLBACK Init, FSP_SILO_FINI_CALLBACK Fini)
|
|
{
|
|
NTSTATUS Result = STATUS_SUCCESS;
|
|
|
|
ExInitializeFastMutex(&FspSiloListMutex);
|
|
InitializeListHead(&FspSiloList);
|
|
|
|
if (FspIsNtDdiVersionAvailable(NTDDI_WIN10_RS5))
|
|
{
|
|
ULONG Fail = 0;
|
|
|
|
LOAD(PsRegisterSiloMonitor);
|
|
LOAD(PsStartSiloMonitor);
|
|
LOAD(PsUnregisterSiloMonitor);
|
|
LOAD(PsGetSiloMonitorContextSlot);
|
|
LOAD(PsCreateSiloContext);
|
|
LOAD(PsDereferenceSiloContext);
|
|
LOAD(PsInsertSiloContext);
|
|
LOAD(PsRemoveSiloContext);
|
|
LOAD(PsGetSiloContext);
|
|
LOAD(PsAttachSiloToCurrentThread);
|
|
LOAD(PsDetachSiloFromCurrentThread);
|
|
LOAD(PsGetCurrentServerSilo);
|
|
LOAD(PsGetSiloContainerId);
|
|
|
|
if (0 == Fail)
|
|
{
|
|
FSP_SILO_MONITOR_REGISTRATION Registration =
|
|
{
|
|
.Version = FSP_SILO_MONITOR_REGISTRATION_VERSION,
|
|
.MonitorHost = FALSE,
|
|
.MonitorExistingSilos = TRUE,
|
|
.DriverObjectName = 0,
|
|
.CreateCallback = FspSiloMonitorCreateCallback,
|
|
.TerminateCallback = FspSiloMonitorTerminateCallback,
|
|
};
|
|
FSP_PSILO_MONITOR Monitor = 0;
|
|
UNICODE_STRING DriverName;
|
|
|
|
RtlInitUnicodeString(&DriverName, L"" DRIVER_NAME);
|
|
Registration.DriverObjectName = &DriverName;
|
|
Result = CALL(PsRegisterSiloMonitor)(&Registration, &Monitor);
|
|
if (!NT_SUCCESS(Result))
|
|
goto exit;
|
|
|
|
FspSiloMonitor = Monitor;
|
|
FspSiloInitCallback = Init;
|
|
FspSiloFiniCallback = Fini;
|
|
FspSiloInitDone = TRUE;
|
|
|
|
Result = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
if (0 != Monitor)
|
|
CALL(PsUnregisterSiloMonitor)(Monitor);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
NTSTATUS FspSiloPostInitialize(VOID)
|
|
{
|
|
if (!FspSiloInitDone)
|
|
return STATUS_SUCCESS;
|
|
|
|
return CALL(PsStartSiloMonitor)(FspSiloMonitor);
|
|
}
|
|
|
|
VOID FspSiloFinalize(VOID)
|
|
{
|
|
if (!FspSiloInitDone)
|
|
return;
|
|
|
|
CALL(PsUnregisterSiloMonitor)(FspSiloMonitor);
|
|
|
|
#if DBG
|
|
FsRtlEnterFileSystem();
|
|
ExAcquireFastMutexUnsafe(&FspSiloListMutex);
|
|
ASSERT(IsListEmpty(&FspSiloList));
|
|
ExReleaseFastMutexUnsafe(&FspSiloListMutex);
|
|
FsRtlExitFileSystem();
|
|
#endif
|
|
|
|
FspSiloMonitor = 0;
|
|
FspSiloInitCallback = 0;
|
|
FspSiloFiniCallback = 0;
|
|
FspSiloInitDone = FALSE;
|
|
}
|
|
|
|
VOID FspSiloEnumerate(FSP_SILO_ENUM_CALLBACK EnumFn)
|
|
{
|
|
KAPC_STATE ApcState;
|
|
PLIST_ENTRY ListEntry;
|
|
FSP_SILO_GLOBALS *Globals;
|
|
|
|
FsRtlEnterFileSystem();
|
|
ExAcquireFastMutexUnsafe(&FspSiloListMutex);
|
|
|
|
if (!IsListEmpty(&FspSiloList))
|
|
{
|
|
KeStackAttachProcess(PsInitialSystemProcess, &ApcState);
|
|
|
|
for (ListEntry = FspSiloList.Flink;
|
|
&FspSiloList != ListEntry;
|
|
ListEntry = ListEntry->Flink)
|
|
{
|
|
Globals = CONTAINING_RECORD(ListEntry, FSP_SILO_GLOBALS, ListEntry);
|
|
|
|
FSP_PESILO PreviousSilo = CALL(PsAttachSiloToCurrentThread)(Globals->Silo);
|
|
EnumFn();
|
|
CALL(PsDetachSiloFromCurrentThread)(PreviousSilo);
|
|
}
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
}
|
|
|
|
ExReleaseFastMutexUnsafe(&FspSiloListMutex);
|
|
FsRtlExitFileSystem();
|
|
}
|