diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj
index c2dda800..9811af8b 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj
+++ b/build/VStudio/testing/winfsp-tests.vcxproj
@@ -284,6 +284,7 @@
+
diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters
index 5049319e..63460f7e 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj.filters
+++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters
@@ -112,6 +112,9 @@
Source
+
+ Source
+
diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h
index 905d182c..b814da4c 100644
--- a/inc/winfsp/fsctl.h
+++ b/inc/winfsp/fsctl.h
@@ -111,6 +111,8 @@ extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid =
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 's', METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSP_FSCTL_NOTIFY \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'n', METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSP_FSCTL_UNLOAD \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'U', METHOD_NEITHER, FILE_ANY_ACCESS)
/* fsctl internal device codes (usable only in-kernel) */
#define FSP_FSCTL_TRANSACT_INTERNAL \
@@ -694,6 +696,8 @@ FSP_API NTSTATUS FspFsctlNotify(HANDLE VolumeHandle,
FSP_API NTSTATUS FspFsctlGetVolumeList(PWSTR DevicePath,
PWCHAR VolumeListBuf, PSIZE_T PVolumeListSize);
FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath);
+FSP_API NTSTATUS FspFsctlStartService(VOID);
+FSP_API NTSTATUS FspFsctlStopService(VOID);
typedef struct
{
diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h
index dc9ce7d6..4cb599f1 100644
--- a/inc/winfsp/winfsp.h
+++ b/inc/winfsp/winfsp.h
@@ -1047,11 +1047,14 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
NTSTATUS (*Obsolete0)(VOID);
+ VOID (*DispatcherStopped)(FSP_FILE_SYSTEM *FileSystem,
+ BOOLEAN Normally);
+
/*
* This ensures that this interface will always contain 64 function pointers.
* Please update when changing the interface as it is important for future compatibility.
*/
- NTSTATUS (*Reserved[32])();
+ NTSTATUS (*Reserved[31])();
} FSP_FILE_SYSTEM_INTERFACE;
FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()),
"FSP_FILE_SYSTEM_INTERFACE must have 64 entries.");
@@ -1074,7 +1077,8 @@ typedef struct _FSP_FILE_SYSTEM
SRWLOCK OpGuardLock;
BOOLEAN UmFileContextIsUserContext2, UmFileContextIsFullContext;
UINT16 UmNoReparsePointsDirCheck:1;
- UINT16 UmReservedFlags:15;
+ UINT16 UmReservedFlags:14;
+ UINT16 DispatcherStopping:1;
} FSP_FILE_SYSTEM;
FSP_FSCTL_STATIC_ASSERT(
(4 == sizeof(PVOID) && 660 == sizeof(FSP_FILE_SYSTEM)) ||
diff --git a/src/dll/fs.c b/src/dll/fs.c
index 7df10b94..943ff670 100644
--- a/src/dll/fs.c
+++ b/src/dll/fs.c
@@ -358,6 +358,21 @@ exit:
CloseHandle(DispatcherThread);
}
+ if (GetCurrentThreadId() == GetThreadId(FileSystem->DispatcherThread))
+ {
+ if (0 != FileSystem->Interface->DispatcherStopped)
+ {
+ /* Normally = !!FileSystem->DispatcherStopping */
+ BOOLEAN Normally = !!(
+ _InterlockedOr16(
+ (PVOID)((PUINT8)&FileSystem->UmFileContextIsFullContext +
+ sizeof(FileSystem->UmFileContextIsFullContext)),
+ 0) &
+ 0x8000);
+ FileSystem->Interface->DispatcherStopped(FileSystem, Normally);
+ }
+ }
+
return Result;
}
@@ -399,6 +414,12 @@ FSP_API VOID FspFileSystemStopDispatcher(FSP_FILE_SYSTEM *FileSystem)
if (0 == FileSystem->DispatcherThread)
return;
+ /* FileSystem->DispatcherStopping = 1 */
+ _InterlockedOr16(
+ (PVOID)((PUINT8)&FileSystem->UmFileContextIsFullContext +
+ sizeof(FileSystem->UmFileContextIsFullContext)),
+ 0x8000);
+
FspFsctlStop0(FileSystem->VolumeHandle);
WaitForSingleObject(FileSystem->DispatcherThread, INFINITE);
@@ -406,6 +427,12 @@ FSP_API VOID FspFileSystemStopDispatcher(FSP_FILE_SYSTEM *FileSystem)
FileSystem->DispatcherThread = 0;
FspFsctlStop(FileSystem->VolumeHandle);
+
+ /* FileSystem->DispatcherStopping = 0 */
+ _InterlockedAnd16(
+ (PVOID)((PUINT8)&FileSystem->UmFileContextIsFullContext +
+ sizeof(FileSystem->UmFileContextIsFullContext)),
+ 0x7fff);
}
FSP_API VOID FspFileSystemSendResponse(FSP_FILE_SYSTEM *FileSystem,
diff --git a/src/dll/fsctl.c b/src/dll/fsctl.c
index a825d23b..f55fb43f 100644
--- a/src/dll/fsctl.c
+++ b/src/dll/fsctl.c
@@ -32,7 +32,6 @@ static DWORD FspFsctlTransactCode = FSP_FSCTL_TRANSACT;
static DWORD FspFsctlTransactBatchCode = FSP_FSCTL_TRANSACT_BATCH;
static VOID FspFsctlServiceVersion(PUINT32 PVersion);
-static NTSTATUS FspFsctlStartService(VOID);
FSP_API NTSTATUS FspFsctlCreateVolume(PWSTR DevicePath,
const FSP_FSCTL_VOLUME_PARAMS *VolumeParams,
@@ -298,6 +297,56 @@ FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath)
return STATUS_SUCCESS;
}
+static NTSTATUS FspFsctlUnload(PWSTR DevicePath)
+{
+ NTSTATUS Result;
+ PWSTR DeviceRoot;
+ SIZE_T DeviceRootSize, DevicePathSize;
+ WCHAR DevicePathBuf[MAX_PATH], *DevicePathPtr;
+ HANDLE VolumeHandle = INVALID_HANDLE_VALUE;
+ DWORD Bytes;
+
+ /* check lengths; everything must fit within MAX_PATH */
+ DeviceRoot = L'\\' == DevicePath[0] ? GLOBALROOT : GLOBALROOT "\\Device\\";
+ DeviceRootSize = lstrlenW(DeviceRoot) * sizeof(WCHAR);
+ DevicePathSize = lstrlenW(DevicePath) * sizeof(WCHAR);
+ if (DeviceRootSize + DevicePathSize + sizeof(WCHAR) > sizeof DevicePathBuf)
+ return STATUS_INVALID_PARAMETER;
+
+ /* prepare the device path to be opened */
+ DevicePathPtr = DevicePathBuf;
+ memcpy(DevicePathPtr, DeviceRoot, DeviceRootSize);
+ DevicePathPtr = (PVOID)((PUINT8)DevicePathPtr + DeviceRootSize);
+ memcpy(DevicePathPtr, DevicePath, DevicePathSize);
+ DevicePathPtr = (PVOID)((PUINT8)DevicePathPtr + DevicePathSize);
+ *DevicePathPtr = L'\0';
+
+ VolumeHandle = CreateFileW(DevicePathBuf,
+ 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ if (INVALID_HANDLE_VALUE == VolumeHandle)
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ if (STATUS_OBJECT_PATH_NOT_FOUND == Result ||
+ STATUS_OBJECT_NAME_NOT_FOUND == Result)
+ Result = STATUS_NO_SUCH_DEVICE;
+ goto exit;
+ }
+
+ if (!DeviceIoControl(VolumeHandle, FSP_FSCTL_UNLOAD, 0, 0, 0, 0, &Bytes, 0))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ Result = STATUS_SUCCESS;
+
+exit:
+ if (INVALID_HANDLE_VALUE != VolumeHandle)
+ CloseHandle(VolumeHandle);
+
+ return Result;
+}
+
static BOOL WINAPI FspFsctlServiceVersionInitialize(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
{
@@ -373,9 +422,24 @@ static VOID FspFsctlServiceVersion(PUINT32 PVersion)
*PVersion = FspFsctlServiceVersionValue;
}
-static NTSTATUS FspFsctlStartService(VOID)
+static SRWLOCK FspFsctlStartStopServiceLock = SRWLOCK_INIT;
+
+static BOOLEAN FspFsctlRunningInContainer(VOID)
+{
+ /* Determine if we are running inside container.
+ *
+ * See https://github.com/microsoft/perfview/blob/V1.9.65/src/TraceEvent/TraceEventSession.cs#L525
+ * See https://stackoverflow.com/a/50748300
+ */
+ return ERROR_SUCCESS == RegGetValueW(
+ HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control",
+ L"ContainerType",
+ RRF_RT_REG_DWORD, 0,
+ 0, 0);
+}
+
+FSP_API NTSTATUS FspFsctlStartService(VOID)
{
- static SRWLOCK Lock = SRWLOCK_INIT;
WCHAR DriverName[256];
SC_HANDLE ScmHandle = 0;
SC_HANDLE SvcHandle = 0;
@@ -385,19 +449,9 @@ static NTSTATUS FspFsctlStartService(VOID)
wsprintfW(DriverName, L"" FSP_FSCTL_DRIVER_NAME "%s", FspSxsSuffix());
- AcquireSRWLockExclusive(&Lock);
+ AcquireSRWLockExclusive(&FspFsctlStartStopServiceLock);
- /* Determine if we are running inside container.
- *
- * See https://github.com/microsoft/perfview/blob/V1.9.65/src/TraceEvent/TraceEventSession.cs#L525
- * See https://stackoverflow.com/a/50748300
- */
- LastError = RegGetValueW(
- HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control",
- L"ContainerType",
- RRF_RT_REG_DWORD, 0,
- 0, 0);
- if (ERROR_SUCCESS == LastError)
+ if (FspFsctlRunningInContainer())
{
Result = STATUS_SUCCESS;
goto exit;
@@ -457,7 +511,97 @@ exit:
if (0 != ScmHandle)
CloseServiceHandle(ScmHandle);
- ReleaseSRWLockExclusive(&Lock);
+ ReleaseSRWLockExclusive(&FspFsctlStartStopServiceLock);
+
+ return Result;
+}
+
+FSP_API NTSTATUS FspFsctlStopService(VOID)
+{
+ WCHAR DriverName[256];
+ HANDLE ThreadToken = 0, ProcessToken = 0;
+ BOOL DidSetThreadToken = FALSE, DidAdjustTokenPrivileges = FALSE;
+ TOKEN_PRIVILEGES Privileges, PreviousPrivileges;
+ PRIVILEGE_SET RequiredPrivileges;
+ DWORD PreviousPrivilegesLength;
+ BOOL PrivilegeCheckResult;
+ NTSTATUS Result;
+
+ wsprintfW(DriverName, L"" FSP_FSCTL_DRIVER_NAME "%s", FspSxsSuffix());
+
+ AcquireSRWLockExclusive(&FspFsctlStartStopServiceLock);
+
+ if (FspFsctlRunningInContainer())
+ {
+ Result = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ /* enable and check SeLoadDriverPrivilege required for FSP_FSCTL_UNLOAD */
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, TRUE, &ThreadToken))
+ {
+ if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &ProcessToken) ||
+ !DuplicateToken(ProcessToken, SecurityDelegation, &ThreadToken) ||
+ !SetThreadToken(0, ThreadToken))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+ DidSetThreadToken = TRUE;
+ CloseHandle(ThreadToken);
+ ThreadToken = 0;
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, TRUE, &ThreadToken))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+ }
+ if (!LookupPrivilegeValueW(0, SE_LOAD_DRIVER_NAME, &Privileges.Privileges[0].Luid))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+ Privileges.PrivilegeCount = 1;
+ Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if (!AdjustTokenPrivileges(ThreadToken, FALSE,
+ &Privileges, sizeof PreviousPrivileges, &PreviousPrivileges, &PreviousPrivilegesLength))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+ DidAdjustTokenPrivileges = 0 == GetLastError();
+ RequiredPrivileges.PrivilegeCount = 1;
+ RequiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ RequiredPrivileges.Privilege[0].Attributes = 0;
+ RequiredPrivileges.Privilege[0].Luid = Privileges.Privileges[0].Luid;
+ if (!PrivilegeCheck(ThreadToken, &RequiredPrivileges, &PrivilegeCheckResult))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+ if (!PrivilegeCheckResult)
+ {
+ Result = STATUS_PRIVILEGE_NOT_HELD;
+ goto exit;
+ }
+
+ Result = FspFsctlUnload(L"" FSP_FSCTL_DISK_DEVICE_NAME);
+ if (!NT_SUCCESS(Result) && STATUS_NO_SUCH_DEVICE != Result)
+ goto exit;
+
+ Result = STATUS_SUCCESS;
+
+exit:
+ if (DidAdjustTokenPrivileges)
+ AdjustTokenPrivileges(ThreadToken, FALSE, &PreviousPrivileges, 0, 0, 0);
+ if (DidSetThreadToken)
+ SetThreadToken(0, 0);
+ if (0 != ThreadToken)
+ CloseHandle(ThreadToken);
+ if (0 != ProcessToken)
+ CloseHandle(ProcessToken);
+
+ ReleaseSRWLockExclusive(&FspFsctlStartStopServiceLock);
return Result;
}
@@ -465,7 +609,9 @@ exit:
static NTSTATUS FspFsctlFixServiceSecurity(HANDLE SvcHandle)
{
/*
- * This function adds an ACE that allows Everyone to start a service.
+ * This function adds two ACE's:
+ * - An ACE that allows Everyone to start a service.
+ * - An ACE that denies Everyone (including Administrators) to stop a service.
*/
PSID WorldSid;
diff --git a/src/sys/device.c b/src/sys/device.c
index 9f0c5138..0611015b 100644
--- a/src/sys/device.c
+++ b/src/sys/device.c
@@ -30,6 +30,7 @@ NTSTATUS FspDeviceCreate(UINT32 Kind, ULONG ExtraSize,
PDEVICE_OBJECT *PDeviceObject);
NTSTATUS FspDeviceInitialize(PDEVICE_OBJECT DeviceObject);
VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject);
+VOID FspDeviceDoIoDeleteDevice(PDEVICE_OBJECT DeviceObject);
BOOLEAN FspDeviceReference(PDEVICE_OBJECT DeviceObject);
VOID FspDeviceDereference(PDEVICE_OBJECT DeviceObject);
_IRQL_requires_(DISPATCH_LEVEL)
@@ -67,13 +68,13 @@ NTSTATUS FspDeviceCopyList(
PDEVICE_OBJECT **PDeviceObjects, PULONG PDeviceObjectCount);
VOID FspDeviceDeleteList(
PDEVICE_OBJECT *DeviceObjects, ULONG DeviceObjectCount);
-VOID FspDeviceDeleteAll(VOID);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspDeviceCreateSecure)
#pragma alloc_text(PAGE, FspDeviceCreate)
#pragma alloc_text(PAGE, FspDeviceInitialize)
#pragma alloc_text(PAGE, FspDeviceDelete)
+#pragma alloc_text(PAGE, FspDeviceDoIoDeleteDevice)
#pragma alloc_text(PAGE, FspFsvolDeviceInit)
#pragma alloc_text(PAGE, FspFsvolDeviceFini)
#pragma alloc_text(PAGE, FspFsvolDeviceCopyContextList)
@@ -92,7 +93,6 @@ VOID FspDeviceDeleteAll(VOID);
#pragma alloc_text(PAGE, FspFsmupDeviceFini)
#pragma alloc_text(PAGE, FspDeviceCopyList)
#pragma alloc_text(PAGE, FspDeviceDeleteList)
-#pragma alloc_text(PAGE, FspDeviceDeleteAll)
#endif
NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize,
@@ -218,13 +218,23 @@ VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject)
return;
}
+ FspDeviceDoIoDeleteDevice(DeviceObject);
+
#if DBG
#pragma prefast(suppress:28175, "Debugging only: ok to access DeviceObject->Size")
RtlFillMemory(&DeviceExtension->Kind,
(PUINT8)DeviceObject + DeviceObject->Size - (PUINT8)&DeviceExtension->Kind, 0xBD);
#endif
+}
- IoDeleteDevice(DeviceObject);
+VOID FspDeviceDoIoDeleteDevice(PDEVICE_OBJECT DeviceObject)
+{
+ PAGED_CODE();
+
+ FSP_DEVICE_EXTENSION *DeviceExtension = FspDeviceExtension(DeviceObject);
+
+ if (0 == InterlockedCompareExchange(&DeviceExtension->DidIoDeleteDevice, 1, 0))
+ IoDeleteDevice(DeviceObject);
}
BOOLEAN FspDeviceReference(PDEVICE_OBJECT DeviceObject)
@@ -942,22 +952,4 @@ VOID FspDeviceDeleteList(
FspFree(DeviceObjects);
}
-VOID FspDeviceDeleteAll(VOID)
-{
- PAGED_CODE();
-
- NTSTATUS Result;
- PDEVICE_OBJECT *DeviceObjects = 0;
- ULONG DeviceObjectCount = 0;
-
- Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount);
- if (!NT_SUCCESS(Result))
- return;
-
- for (ULONG i = 0; DeviceObjectCount > i; i++)
- FspDeviceDelete(DeviceObjects[i]);
-
- FspDeviceDeleteList(DeviceObjects, DeviceObjectCount);
-}
-
FAST_MUTEX FspDeviceGlobalMutex;
diff --git a/src/sys/devtimer.c b/src/sys/devtimer.c
index 83e7db30..69a3fdf0 100644
--- a/src/sys/devtimer.c
+++ b/src/sys/devtimer.c
@@ -55,6 +55,7 @@ NTSTATUS FspDeviceInitializeAllTimers(VOID)
VOID FspDeviceFinalizeAllTimers(VOID)
{
KeCancelTimer(&FspDeviceTimer);
+ KeFlushQueuedDpcs();
#if DBG
KIRQL Irql;
diff --git a/src/sys/driver.c b/src/sys/driver.c
index 1763f9b7..33ecea99 100644
--- a/src/sys/driver.c
+++ b/src/sys/driver.c
@@ -21,27 +21,23 @@
#include
-/*
- * Define the following macro to include FspUnload and make the driver unloadable.
- */
-#define FSP_UNLOAD
-
DRIVER_INITIALIZE DriverEntry;
-#if defined(FSP_UNLOAD)
-DRIVER_UNLOAD FspUnload;
-#endif
+static DRIVER_UNLOAD DriverUnload;
static VOID FspDriverMultiVersionInitialize(VOID);
static NTSTATUS FspDriverInitializeDevices(VOID);
static VOID FspDriverFinalizeDevices(VOID);
+static VOID FspDriverFinalizeDevicesEx(BOOLEAN DeleteDevices);
+NTSTATUS FspDriverUnload(
+ PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
-#if defined(FSP_UNLOAD)
-#pragma alloc_text(PAGE, FspUnload)
-#endif
+#pragma alloc_text(PAGE, DriverUnload)
#pragma alloc_text(INIT, FspDriverMultiVersionInitialize)
#pragma alloc_text(PAGE, FspDriverInitializeDevices)
#pragma alloc_text(PAGE, FspDriverFinalizeDevices)
+#pragma alloc_text(PAGE, FspDriverFinalizeDevicesEx)
+#pragma alloc_text(PAGE, FspDriverUnload)
#endif
NTSTATUS DriverEntry(
@@ -54,9 +50,7 @@ NTSTATUS DriverEntry(
FspSxsIdentInitialize(&DriverObject->DriverName);
/* setup the driver object */
-#if defined(FSP_UNLOAD)
- DriverObject->DriverUnload = FspUnload;
-#endif
+ DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = FspCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = FspClose;
DriverObject->MajorFunction[IRP_MJ_READ] = FspRead;
@@ -144,6 +138,7 @@ NTSTATUS DriverEntry(
FspDriverObject = DriverObject;
FspDriverMultiVersionInitialize();
+ ExInitializeFastMutex(&FspDriverUnloadMutex);
ExInitializeFastMutex(&FspDeviceGlobalMutex);
Result = FspSiloInitialize(FspDriverInitializeDevices, FspDriverFinalizeDevices);
@@ -188,12 +183,13 @@ exit:
&DriverObject->DriverName, RegistryPath);
}
-#if defined(FSP_UNLOAD)
-VOID FspUnload(
+VOID DriverUnload(
PDRIVER_OBJECT DriverObject)
{
FSP_ENTER_VOID(PAGED_CODE());
+ FspDriverFinalizeDevices();
+
FspDeviceFinalizeAllTimers();
FspProcessBufferFinalize();
@@ -206,7 +202,6 @@ VOID FspUnload(
FSP_LEAVE_VOID("DriverName=\"%wZ\"",
&DriverObject->DriverName);
}
-#endif
static VOID FspDriverMultiVersionInitialize(VOID)
{
@@ -342,12 +337,25 @@ static NTSTATUS FspDriverInitializeDevices(VOID)
* as a file system; we register with the MUP instead.
*/
IoRegisterFileSystem(Globals->FsctlDiskDeviceObject);
+ Globals->InitDoneRegisterDisk = 1;
+
+ /*
+ * Reference primary device objects to allow for IoDeleteDevice during FspDriverUnload.
+ */
+ ObReferenceObject(Globals->FsctlDiskDeviceObject);
+ ObReferenceObject(Globals->FsctlNetDeviceObject);
+ ObReferenceObject(Globals->FsmupDeviceObject);
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
{
+ if (Globals->InitDoneRegisterDisk)
+ {
+ IoUnregisterFileSystem(Globals->FsctlDiskDeviceObject);
+ Globals->InitDoneRegisterDisk = 0;
+ }
if (0 != Globals->MupHandle)
{
FsRtlDeregisterUncProvider(Globals->MupHandle);
@@ -391,14 +399,24 @@ static VOID FspDriverFinalizeDevices(VOID)
{
PAGED_CODE();
+ FspDriverFinalizeDevicesEx(TRUE);
+}
+
+static VOID FspDriverFinalizeDevicesEx(BOOLEAN DeleteDevices)
+{
+ PAGED_CODE();
+
FSP_SILO_GLOBALS *Globals;
UNICODE_STRING SymlinkName;
FspSiloGetGlobals(&Globals);
ASSERT(0 != Globals);
- IoUnregisterFileSystem(Globals->FsctlDiskDeviceObject);
-
+ if (Globals->InitDoneRegisterDisk)
+ {
+ IoUnregisterFileSystem(Globals->FsctlDiskDeviceObject);
+ Globals->InitDoneRegisterDisk = 0;
+ }
if (0 != Globals->MupHandle)
{
FsRtlDeregisterUncProvider(Globals->MupHandle);
@@ -406,8 +424,14 @@ static VOID FspDriverFinalizeDevices(VOID)
}
if (0 != Globals->FsmupDeviceObject)
{
- FspDeviceDelete(Globals->FsmupDeviceObject);
- Globals->FsmupDeviceObject = 0;
+ if (DeleteDevices)
+ {
+ FspDeviceDelete(Globals->FsmupDeviceObject);
+ ObDereferenceObject(Globals->FsmupDeviceObject);
+ Globals->FsmupDeviceObject = 0;
+ }
+ else
+ FspDeviceDoIoDeleteDevice(Globals->FsmupDeviceObject);
}
if (Globals->InitDoneSymlinkNet)
{
@@ -417,8 +441,14 @@ static VOID FspDriverFinalizeDevices(VOID)
}
if (0 != Globals->FsctlNetDeviceObject)
{
- FspDeviceDelete(Globals->FsctlNetDeviceObject);
- Globals->FsctlNetDeviceObject = 0;
+ if (DeleteDevices)
+ {
+ FspDeviceDelete(Globals->FsctlNetDeviceObject);
+ ObDereferenceObject(Globals->FsctlNetDeviceObject);
+ Globals->FsctlNetDeviceObject = 0;
+ }
+ else
+ FspDeviceDoIoDeleteDevice(Globals->FsctlNetDeviceObject);
}
if (Globals->InitDoneSymlinkDisk)
{
@@ -428,16 +458,84 @@ static VOID FspDriverFinalizeDevices(VOID)
}
if (0 != Globals->FsctlDiskDeviceObject)
{
- FspDeviceDelete(Globals->FsctlDiskDeviceObject);
- Globals->FsctlDiskDeviceObject = 0;
+ if (DeleteDevices)
+ {
+ FspDeviceDelete(Globals->FsctlDiskDeviceObject);
+ ObDereferenceObject(Globals->FsctlDiskDeviceObject);
+ Globals->FsctlDiskDeviceObject = 0;
+ }
+ else
+ FspDeviceDoIoDeleteDevice(Globals->FsctlDiskDeviceObject);
}
FspSiloDereferenceGlobals(Globals);
}
+NTSTATUS FspDriverUnload(
+ PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
+{
+ PAGED_CODE();
+
+ ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction);
+ ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction);
+ ASSERT(FSP_FSCTL_UNLOAD == IrpSp->Parameters.FileSystemControl.FsControlCode);
+
+ NTSTATUS Result;
+ UNICODE_STRING DriverServiceName, DriverName, Remain;
+ WCHAR DriverServiceNameBuf[64 + 256];
+ PDEVICE_OBJECT *DeviceObjects = 0;
+ ULONG DeviceObjectCount = 0;
+
+ if (!FspSiloIsHost())
+ return STATUS_INVALID_DEVICE_REQUEST;
+
+ if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_LOAD_DRIVER_PRIVILEGE), UserMode))
+ return STATUS_PRIVILEGE_NOT_HELD;
+
+ ExAcquireFastMutexUnsafe(&FspDriverUnloadMutex);
+
+ if (!FspDriverUnloadDone)
+ {
+ FspFileNameSuffix(&FspDriverObject->DriverName, &Remain, &DriverName);
+ RtlInitEmptyUnicodeString(&DriverServiceName, DriverServiceNameBuf, sizeof DriverServiceNameBuf);
+ Result = RtlUnicodeStringPrintf(&DriverServiceName,
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\%wZ", &DriverName);
+ if (!NT_SUCCESS(Result))
+ goto exit;
+ Result = ZwUnloadDriver(&DriverServiceName);
+ if (!NT_SUCCESS(Result))
+ goto exit;
+
+ FspDriverFinalizeDevicesEx(FALSE);
+
+ Result = FspDeviceCopyList(&DeviceObjects, &DeviceObjectCount);
+ if (NT_SUCCESS(Result))
+ {
+ for (ULONG I = 0; DeviceObjectCount > I; I++)
+ {
+ FSP_DEVICE_EXTENSION *DeviceExtension = FspDeviceExtension(DeviceObjects[I]);
+ if (FspFsvolDeviceExtensionKind == DeviceExtension->Kind)
+ FspIoqStop(((FSP_FSVOL_DEVICE_EXTENSION *)DeviceExtension)->Ioq, FALSE);
+ }
+ FspDeviceDeleteList(DeviceObjects, DeviceObjectCount);
+ }
+
+ FspDriverUnloadDone = TRUE;
+ }
+
+ Result = STATUS_SUCCESS;
+
+exit:
+ ExReleaseFastMutexUnsafe(&FspDriverUnloadMutex);
+
+ return Result;
+}
+
PDRIVER_OBJECT FspDriverObject;
FAST_IO_DISPATCH FspFastIoDispatch;
CACHE_MANAGER_CALLBACKS FspCacheManagerCallbacks;
+FAST_MUTEX FspDriverUnloadMutex;
+BOOLEAN FspDriverUnloadDone;
ULONG FspProcessorCount;
FSP_MV_CcCoherencyFlushAndPurgeCache *FspMvCcCoherencyFlushAndPurgeCache;
diff --git a/src/sys/driver.h b/src/sys/driver.h
index 07888662..1b9e69b5 100644
--- a/src/sys/driver.h
+++ b/src/sys/driver.h
@@ -364,6 +364,10 @@ VOID FspTraceNtStatus(const char *file, int line, const char *func, NTSTATUS Sta
/* missing typedef */
typedef const void *PCVOID;
+/* driver unload */
+NTSTATUS FspDriverUnload(
+ PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
+
/* driver major functions */
_Function_class_(DRIVER_DISPATCH)
_IRQL_requires_max_(APC_LEVEL)
@@ -752,10 +756,11 @@ typedef struct
PDEVICE_OBJECT FsmupDeviceObject;
HANDLE MupHandle;
WCHAR FsmupDeviceNameBuf[128];
- UINT32 InitDoneSymlinkDisk:1, InitDoneSymlinkNet:1;
+ UINT32 InitDoneSymlinkDisk:1, InitDoneSymlinkNet:1, InitDoneRegisterDisk:1;
} FSP_SILO_GLOBALS;
typedef NTSTATUS (*FSP_SILO_INIT_CALLBACK)(VOID);
typedef VOID (*FSP_SILO_FINI_CALLBACK)(VOID);
+BOOLEAN FspSiloIsHost(VOID);
NTSTATUS FspSiloGetGlobals(FSP_SILO_GLOBALS **PGlobals);
VOID FspSiloDereferenceGlobals(FSP_SILO_GLOBALS *Globals);
VOID FspSiloGetContainerId(GUID *ContainerId);
@@ -1188,8 +1193,8 @@ typedef struct
LONG RefCount;
UINT32 Kind;
GUID SiloContainerId;
- /* IoTimer emulation */
- FSP_DEVICE_TIMER DeviceTimer;
+ FSP_DEVICE_TIMER DeviceTimer; /* IoTimer emulation */
+ LONG DidIoDeleteDevice;
} FSP_DEVICE_EXTENSION;
typedef struct
{
@@ -1291,6 +1296,7 @@ NTSTATUS FspDeviceCreate(UINT32 Kind, ULONG ExtraSize,
PDEVICE_OBJECT *PDeviceObject);
NTSTATUS FspDeviceInitialize(PDEVICE_OBJECT DeviceObject);
VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject);
+VOID FspDeviceDoIoDeleteDevice(PDEVICE_OBJECT DeviceObject);
BOOLEAN FspDeviceReference(PDEVICE_OBJECT DeviceObject);
VOID FspDeviceDereference(PDEVICE_OBJECT DeviceObject);
static inline
@@ -1473,7 +1479,6 @@ NTSTATUS FspDeviceCopyList(
PDEVICE_OBJECT **PDeviceObjects, PULONG PDeviceObjectCount);
VOID FspDeviceDeleteList(
PDEVICE_OBJECT *DeviceObjects, ULONG DeviceObjectCount);
-VOID FspDeviceDeleteAll(VOID);
NTSTATUS FspDeviceInitializeAllTimers(VOID);
VOID FspDeviceFinalizeAllTimers(VOID);
NTSTATUS FspDeviceInitializeTimer(PDEVICE_OBJECT DeviceObject,
@@ -2005,6 +2010,8 @@ FSP_MV_CcCoherencyFlushAndPurgeCache(
extern PDRIVER_OBJECT FspDriverObject;
extern FAST_IO_DISPATCH FspFastIoDispatch;
extern CACHE_MANAGER_CALLBACKS FspCacheManagerCallbacks;
+extern FAST_MUTEX FspDriverUnloadMutex;
+extern BOOLEAN FspDriverUnloadDone;
extern FSP_IOPREP_DISPATCH *FspIopPrepareFunction[];
extern FSP_IOCMPL_DISPATCH *FspIopCompleteFunction[];
extern FAST_MUTEX FspDeviceGlobalMutex;
diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c
index cc526512..8b12e8e6 100644
--- a/src/sys/fsctl.c
+++ b/src/sys/fsctl.c
@@ -128,6 +128,9 @@ static NTSTATUS FspFsctlFileSystemControl(
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeNotify(FsctlDeviceObject, Irp, IrpSp);
break;
+ case FSP_FSCTL_UNLOAD:
+ Result = FspDriverUnload(FsctlDeviceObject, Irp, IrpSp);
+ break;
default:
if (CTL_CODE(0, 0xC00, 0, 0) ==
(IrpSp->Parameters.FileSystemControl.FsControlCode & CTL_CODE(0, 0xC00, 0, 0)))
diff --git a/src/sys/psbuffer.c b/src/sys/psbuffer.c
index f151cf19..cd13cd67 100644
--- a/src/sys/psbuffer.c
+++ b/src/sys/psbuffer.c
@@ -123,6 +123,27 @@ NTSTATUS FspProcessBufferInitialize(VOID)
VOID FspProcessBufferFinalize(VOID)
{
PsSetCreateProcessNotifyRoutine(FspProcessBufferNotifyRoutine, TRUE);
+
+ /*
+ * Free any items in our process hash table.
+ * Any virtual memory was released when the corresponding processes went away.
+ */
+ for (ULONG HashIndex = 0; ProcessBufferBucketCount > HashIndex; HashIndex++)
+ {
+ for (FSP_PROCESS_BUFFER_ITEM *Item = ProcessBufferBuckets[HashIndex], *DictNext; Item; Item = DictNext)
+ {
+ for (FSP_PROCESS_BUFFER_LIST_ENTRY *P = Item->BufferList, *Next; P; P = Next)
+ {
+ Next = P->Next;
+ FspFree(P);
+ }
+
+ DictNext = Item->DictNext;
+ FspFree(Item);
+ }
+
+ ProcessBufferBuckets[HashIndex] = 0;
+ }
}
static VOID FspProcessBufferNotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
diff --git a/src/sys/silo.c b/src/sys/silo.c
index 7af2ea7a..9ad9d033 100644
--- a/src/sys/silo.c
+++ b/src/sys/silo.c
@@ -123,6 +123,11 @@ static FSP_SILO_GLOBALS FspSiloHostGlobals;
}
#define CALL(n) (FspSilo ## n)
+BOOLEAN FspSiloIsHost(VOID)
+{
+ return !FspSiloInitDone || 0 == CALL(PsGetCurrentServerSilo)();
+}
+
NTSTATUS FspSiloGetGlobals(FSP_SILO_GLOBALS **PGlobals)
{
FSP_PESILO Silo;
diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp
index 87e44a50..5bed0407 100644
--- a/tst/memfs/memfs.cpp
+++ b/tst/memfs/memfs.cpp
@@ -40,6 +40,11 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH,
*/
//#define MEMFS_STANDALONE
+/*
+ * Define the MEMFS_DISPATCHER_STOPPED macro to include DispatcherStopped support.
+ */
+#define MEMFS_DISPATCHER_STOPPED
+
/*
* Define the MEMFS_NAME_NORMALIZATION macro to include name normalization support.
*/
@@ -2269,6 +2274,14 @@ static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
}
#endif
+#if defined(MEMFS_DISPATCHER_STOPPED)
+static void DispatcherStopped(FSP_FILE_SYSTEM *FileSystem,
+ BOOLEAN Normally)
+{
+ //FspDebugLog(__FUNCTION__ ": Normally=%d\n", Normally);
+}
+#endif
+
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
{
GetVolumeInfo,
@@ -2336,6 +2349,12 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
0,
0,
0,
+#endif
+ 0,
+#if defined(MEMFS_DISPATCHER_STOPPED)
+ DispatcherStopped,
+#else
+ 0,
#endif
};
diff --git a/tst/winfsp-tests/loadun-test.c b/tst/winfsp-tests/loadun-test.c
new file mode 100644
index 00000000..7ef18514
--- /dev/null
+++ b/tst/winfsp-tests/loadun-test.c
@@ -0,0 +1,44 @@
+/**
+ * @file loadun-test.c
+ *
+ * @copyright 2015-2022 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
+#include
+
+#include "winfsp-tests.h"
+
+static void load_unload_test(void)
+{
+ /* this is not a real test! */
+
+ FspFsctlStartService();
+ FspFsctlStartService();
+
+ FspFsctlStopService();
+ FspFsctlStopService();
+}
+
+void load_unload_tests(void)
+{
+ if (OptExternal)
+ return;
+
+ TEST_OPT(load_unload_test);
+}
diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c
index 9dd97134..8f633bc3 100644
--- a/tst/winfsp-tests/winfsp-tests.c
+++ b/tst/winfsp-tests/winfsp-tests.c
@@ -220,6 +220,7 @@ LONG WINAPI UnhandledExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
argv[argc] = 0
int main(int argc, char *argv[])
{
+ TESTSUITE(load_unload_tests);
TESTSUITE(fuse_opt_tests);
TESTSUITE(fuse_tests);
TESTSUITE(posix_tests);