dll: FspFileSystemStopServiceIfNecessary

This commit is contained in:
Bill Zissimopoulos 2023-02-01 17:42:11 +00:00
parent da3a8aa229
commit 3aadaee511
10 changed files with 196 additions and 3 deletions

View File

@ -1047,6 +1047,41 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
NTSTATUS (*Obsolete0)(VOID); NTSTATUS (*Obsolete0)(VOID);
/**
* Inform the file system that its dispatcher has been stopped.
*
* Prior to WinFsp v2.0 the FSD would never unmount a file system volume unless
* the user mode file system requested the unmount. Since WinFsp v2.0 it is possible
* for the FSD to unmount a file system volume without an explicit user mode file system
* request. For example, this happens when the FSD is being uninstalled.
*
* A user mode file system can use this operation to determine when its dispatcher
* has been stopped. The Normally parameter can be used to determine why the dispatcher
* was stopped: it is TRUE when the file system is being stopped via
* FspFileSystemStopDispatcher and FALSE otherwise.
*
* When the file system receives a request with Normally == TRUE it need not take any
* extra steps. This case is the same as for pre-v2.0 versions: since the file system
* stopped the dispatcher via FspFileSystemStopDispatcher, it will likely exit its
* process soon.
*
* When the file system receives a request with Normally == FALSE it may need to take
* extra steps to exit its process as this is not done by default.
*
* A file system that uses the FspService infrastructure may use the
* FspFileSystemStopServiceIfNecessary API to correctly handle all cases.
*
* This operation is the last one that a file system will receive.
*
* @param FileSystem
* The file system on which this request is posted.
* @param Normally
* TRUE if the file system is being stopped via FspFileSystemStopDispatcher.
* FALSE if the file system is being stopped because of another reason such
* as driver unload/uninstall.
* @see
* FspFileSystemStopServiceIfNecessary
*/
VOID (*DispatcherStopped)(FSP_FILE_SYSTEM *FileSystem, VOID (*DispatcherStopped)(FSP_FILE_SYSTEM *FileSystem,
BOOLEAN Normally); BOOLEAN Normally);
@ -1112,7 +1147,7 @@ FSP_API NTSTATUS FspFileSystemPreflight(PWSTR DevicePath,
* @param VolumeParams * @param VolumeParams
* Volume parameters for the newly created file system. * Volume parameters for the newly created file system.
* @param Interface * @param Interface
* A pointer to the actual operations that actually implement this user mode file system. * A pointer to the operations that implement this user mode file system.
* @param PFileSystem [out] * @param PFileSystem [out]
* Pointer that will receive the file system object created on successful return from this * Pointer that will receive the file system object created on successful return from this
* call. * call.
@ -1750,6 +1785,23 @@ UINT32 FspFileSystemGetEaPackedSize(PFILE_FULL_EA_INFORMATION SingleEa)
*/ */
FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo, FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo,
PVOID Buffer, ULONG Length, PULONG PBytesTransferred); PVOID Buffer, ULONG Length, PULONG PBytesTransferred);
/**
* Stop a file system service, if any.
*
* This is a helper for implementing the DispatcherStopped operation, but only for file systems
* that use the FspService infrastructure.
*
* @param FileSystem
* The file system object.
* @param Normally
* TRUE if the file system is being stopped via FspFileSystemStopDispatcher.
* FALSE if the file system is being stopped because of another reason such
* as driver unload/uninstall.
* @see
* DispatcherStopped
*/
FSP_API VOID FspFileSystemStopServiceIfNecessary(FSP_FILE_SYSTEM *FileSystem,
BOOLEAN Normally);
/* /*
* Directory buffering * Directory buffering
@ -2047,6 +2099,8 @@ FSP_API ULONG FspServiceGetExitCode(FSP_SERVICE *Service);
* to connect the service process to the Service Control Manager. If the Service Control Manager is * to connect the service process to the Service Control Manager. If the Service Control Manager is
* not available (and console mode is allowed) it will enter console mode. * not available (and console mode is allowed) it will enter console mode.
* *
* This function should be called once per process.
*
* @param Service * @param Service
* The service object. * The service object.
* @return * @return

View File

@ -1883,3 +1883,12 @@ FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo,
{ {
return FspFileSystemAddXxxInfo(NotifyInfo, Buffer, Length, PBytesTransferred); return FspFileSystemAddXxxInfo(NotifyInfo, Buffer, Length, PBytesTransferred);
} }
FSP_API VOID FspFileSystemStopServiceIfNecessary(FSP_FILE_SYSTEM *FileSystem,
BOOLEAN Normally)
{
/* NOTE: .NET calls us with a zero FileSystem pointer! */
if (Normally)
return;
FspServiceStopLoop();
}

View File

@ -2635,6 +2635,17 @@ static NTSTATUS fsp_fuse_intf_SetEa(FSP_FILE_SYSTEM *FileSystem,
&Uid, &Gid, &Mode, FileInfo); &Uid, &Gid, &Mode, FileInfo);
} }
static VOID fsp_fuse_intf_DispatcherStopped(FSP_FILE_SYSTEM *FileSystem,
BOOLEAN Normally)
{
if (Normally)
return;
struct fuse *f = FileSystem->UserContext;
fsp_fuse_exit(f->env, f);
}
FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf = FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
{ {
fsp_fuse_intf_GetVolumeInfo, fsp_fuse_intf_GetVolumeInfo,
@ -2668,6 +2679,8 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
fsp_fuse_intf_Overwrite, fsp_fuse_intf_Overwrite,
fsp_fuse_intf_GetEa, fsp_fuse_intf_GetEa,
fsp_fuse_intf_SetEa, fsp_fuse_intf_SetEa,
0,
fsp_fuse_intf_DispatcherStopped,
}; };
/* /*

View File

@ -110,6 +110,7 @@ NTSTATUS FspGetModuleFileName(
VOID FspFileSystemPeekInDirectoryBuffer(PVOID *PDirBuffer, VOID FspFileSystemPeekInDirectoryBuffer(PVOID *PDirBuffer,
PUINT8 *PBuffer, PULONG *PIndex, PULONG PCount); PUINT8 *PBuffer, PULONG *PIndex, PULONG PCount);
VOID FspServiceStopLoop(VOID);
BOOL WINAPI FspServiceConsoleCtrlHandler(DWORD CtrlType); BOOL WINAPI FspServiceConsoleCtrlHandler(DWORD CtrlType);
static inline ULONG FspPathSuffixIndex(PWSTR FileName) static inline ULONG FspPathSuffixIndex(PWSTR FileName)

View File

@ -40,6 +40,8 @@ enum
GetStatus_WaitHint = 0x4000, GetStatus_WaitHint = 0x4000,
}; };
static SRWLOCK FspServiceLoopLock = SRWLOCK_INIT;
static SRWLOCK FspServiceTableLock = SRWLOCK_INIT;
static SERVICE_TABLE_ENTRYW *FspServiceTable; static SERVICE_TABLE_ENTRYW *FspServiceTable;
static HANDLE FspServiceConsoleModeEvent; static HANDLE FspServiceConsoleModeEvent;
static UINT32 FspServiceConsoleCtrlHandlerDisabled; static UINT32 FspServiceConsoleCtrlHandlerDisabled;
@ -220,6 +222,14 @@ FSP_API ULONG FspServiceGetExitCode(FSP_SERVICE *Service)
FSP_API NTSTATUS FspServiceLoop(FSP_SERVICE *Service) FSP_API NTSTATUS FspServiceLoop(FSP_SERVICE *Service)
{ {
/*
* FspServiceLoop can only be called once per process, because of StartServiceCtrlDispatcherW
* (which returns ERROR_SERVICE_ALREADY_RUNNING if called more than once). Unfortunately this
* limitation was never documented and there may be users of FspServiceLoop out there that call
* it more than once per process.
*/
AcquireSRWLockExclusive(&FspServiceLoopLock);
NTSTATUS Result; NTSTATUS Result;
SERVICE_TABLE_ENTRYW ServiceTable[2]; SERVICE_TABLE_ENTRYW ServiceTable[2];
@ -236,7 +246,9 @@ FSP_API NTSTATUS FspServiceLoop(FSP_SERVICE *Service)
ServiceTable[0].lpServiceProc = FspServiceEntry; ServiceTable[0].lpServiceProc = FspServiceEntry;
ServiceTable[1].lpServiceName = 0; ServiceTable[1].lpServiceName = 0;
ServiceTable[1].lpServiceProc = 0; ServiceTable[1].lpServiceProc = 0;
AcquireSRWLockExclusive(&FspServiceTableLock);
FspServiceTable = ServiceTable; FspServiceTable = ServiceTable;
ReleaseSRWLockExclusive(&FspServiceTableLock);
if (!StartServiceCtrlDispatcherW(ServiceTable)) if (!StartServiceCtrlDispatcherW(ServiceTable))
{ {
@ -331,11 +343,42 @@ FSP_API NTSTATUS FspServiceLoop(FSP_SERVICE *Service)
Result = STATUS_SUCCESS; Result = STATUS_SUCCESS;
exit: exit:
AcquireSRWLockExclusive(&FspServiceTableLock);
FspServiceTable = 0; FspServiceTable = 0;
ReleaseSRWLockExclusive(&FspServiceTableLock);
ReleaseSRWLockExclusive(&FspServiceLoopLock);
return Result; return Result;
} }
static DWORD WINAPI FspServiceStopLoopThread(PVOID Context);
VOID FspServiceStopLoop(VOID)
{
BOOLEAN HasService;
HANDLE Thread;
AcquireSRWLockShared(&FspServiceTableLock);
HasService = 0 != FspServiceFromTable();
ReleaseSRWLockShared(&FspServiceTableLock);
if (HasService)
{
Thread = CreateThread(0, 0, FspServiceStopLoopThread, 0, 0, 0);
if (0 != Thread)
CloseHandle(Thread);
}
}
static DWORD WINAPI FspServiceStopLoopThread(PVOID Context)
{
AcquireSRWLockShared(&FspServiceTableLock);
FSP_SERVICE *Service = FspServiceFromTable();
if (0 != Service)
FspServiceStop(Service);
ReleaseSRWLockShared(&FspServiceTableLock);
return 0;
}
FSP_API VOID FspServiceStop(FSP_SERVICE *Service) FSP_API VOID FspServiceStop(FSP_SERVICE *Service)
{ {
SERVICE_STATUS ServiceStatus; SERVICE_STATUS ServiceStatus;
@ -393,6 +436,7 @@ static VOID WINAPI FspServiceEntry(DWORD Argc, PWSTR *Argv)
FSP_SERVICE *Service; FSP_SERVICE *Service;
Service = FspServiceFromTable(); Service = FspServiceFromTable();
/* we are subordinate to FspServiceLoop; no need to protect this access with FspServiceTableLock */
if (0 == Service) if (0 == Service)
{ {
FspServiceLog(EVENTLOG_ERROR_TYPE, FspServiceLog(EVENTLOG_ERROR_TYPE,
@ -501,6 +545,7 @@ static DWORD WINAPI FspServiceConsoleModeThread(PVOID Context)
; ;
Service = FspServiceFromTable(); Service = FspServiceFromTable();
/* we are subordinate to FspServiceLoop; no need to protect this access with FspServiceTableLock */
if (0 == Service) if (0 == Service)
FspServiceLog(EVENTLOG_ERROR_TYPE, FspServiceLog(EVENTLOG_ERROR_TYPE,
L"" __FUNCTION__ ": internal error: FspServiceFromTable = 0"); L"" __FUNCTION__ ": internal error: FspServiceFromTable = 0");

View File

@ -1188,6 +1188,39 @@ namespace Fsp
{ {
return STATUS_INVALID_DEVICE_REQUEST; return STATUS_INVALID_DEVICE_REQUEST;
} }
/// <summary>
/// Inform the file system that its dispatcher has been stopped.
/// </summary>
/// <remarks>
/// <para>
/// Prior to WinFsp v2.0 the FSD would never unmount a file system volume unless
/// the user mode file system requested the unmount. Since WinFsp v2.0 it is possible
/// for the FSD to unmount a file system volume without an explicit user mode file system
/// request. For example, this happens when the FSD is being uninstalled.
/// </para><para>
/// A user mode file system can use this operation to determine when its dispatcher
/// has been stopped. The Normally parameter can be used to determine why the dispatcher
/// was stopped: it is TRUE when the file system is being stopped normally (i.e. via the
/// native FspFileSystemStopDispatcher) and FALSE otherwise.
/// </para><para>
/// A file system that uses the Service class infrastructure may use the
/// StopServiceIfNecessary method to correctly handle all cases. The base implementation
/// of this method calls the StopServiceIfNecessary method.
/// </para><para>
/// This operation is the last one that a file system will receive.
/// </para>
/// </remarks>
/// <param name="Normally">
/// TRUE if the file system is being stopped via the native FspFileSystemStopDispatcher.
/// FALSE if the file system is being stopped because of another reason such
/// as driver unload/uninstall.
/// </param>
/// <seealso cref="StopServiceIfNecessary"/>
public virtual void DispatcherStopped(
Boolean Normally)
{
StopServiceIfNecessary(Normally);
}
/* helpers */ /* helpers */
/// <summary> /// <summary>
@ -1483,6 +1516,10 @@ namespace Fsp
{ {
return FullEaInformation.PackedSize(EaName, EaValue, NeedEa); return FullEaInformation.PackedSize(EaName, EaValue, NeedEa);
} }
public void StopServiceIfNecessary(Boolean Normally)
{
Api.FspFileSystemStopServiceIfNecessary(IntPtr.Zero, Normally);
}
} }
} }

View File

@ -1426,6 +1426,21 @@ namespace Fsp
} }
} }
private static void DispatcherStopped(
IntPtr FileSystemPtr,
Boolean Normally)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
FileSystem.DispatcherStopped(Normally);
}
catch (Exception ex)
{
ExceptionHandler(FileSystem, ex);
}
}
static FileSystemHost() static FileSystemHost()
{ {
_FileSystemInterface.GetVolumeInfo = GetVolumeInfo; _FileSystemInterface.GetVolumeInfo = GetVolumeInfo;
@ -1456,6 +1471,7 @@ namespace Fsp
_FileSystemInterface.SetDelete = SetDelete; _FileSystemInterface.SetDelete = SetDelete;
_FileSystemInterface.GetEa = GetEa; _FileSystemInterface.GetEa = GetEa;
_FileSystemInterface.SetEa = SetEa; _FileSystemInterface.SetEa = SetEa;
_FileSystemInterface.DispatcherStopped = DispatcherStopped;
_FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size); _FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size);
/* Marshal.AllocHGlobal does not zero memory; we must do it ourselves! */ /* Marshal.AllocHGlobal does not zero memory; we must do it ourselves! */

View File

@ -745,6 +745,10 @@ namespace Fsp.Interop
out FileInfo FileInfo); out FileInfo FileInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 Obsolete0(); internal delegate Int32 Obsolete0();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void DispatcherStopped(
IntPtr FileSystem,
[MarshalAs(UnmanagedType.U1)] Boolean Normally);
} }
internal static int Size = IntPtr.Size * 64; internal static int Size = IntPtr.Size * 64;
@ -781,6 +785,7 @@ namespace Fsp.Interop
internal Proto.GetEa GetEa; internal Proto.GetEa GetEa;
internal Proto.SetEa SetEa; internal Proto.SetEa SetEa;
internal Proto.Obsolete0 Obsolete0; internal Proto.Obsolete0 Obsolete0;
internal Proto.DispatcherStopped DispatcherStopped;
/* NTSTATUS (*Reserved[33])(); */ /* NTSTATUS (*Reserved[33])(); */
} }
@ -907,6 +912,10 @@ namespace Fsp.Interop
UInt32 Length, UInt32 Length,
out UInt32 PBytesTransferred); out UInt32 PBytesTransferred);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FspFileSystemStopServiceIfNecessary(
IntPtr FileSystem,
[MarshalAs(UnmanagedType.U1)] Boolean Normally);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)] [return: MarshalAs(UnmanagedType.U1)]
internal delegate Boolean FspFileSystemAcquireDirectoryBuffer( internal delegate Boolean FspFileSystemAcquireDirectoryBuffer(
ref IntPtr PDirBuffer, ref IntPtr PDirBuffer,
@ -1048,6 +1057,7 @@ namespace Fsp.Interop
internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo; internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo;
internal static Proto.FspFileSystemAddEa _FspFileSystemAddEa; internal static Proto.FspFileSystemAddEa _FspFileSystemAddEa;
internal static Proto.FspFileSystemAddNotifyInfo _FspFileSystemAddNotifyInfo; internal static Proto.FspFileSystemAddNotifyInfo _FspFileSystemAddNotifyInfo;
internal static Proto.FspFileSystemStopServiceIfNecessary FspFileSystemStopServiceIfNecessary;
internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer; internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer;
internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer; internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer;
internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer; internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer;
@ -1506,6 +1516,7 @@ namespace Fsp.Interop
_FspFileSystemAddStreamInfo = GetEntryPoint<Proto.FspFileSystemAddStreamInfo>(Module); _FspFileSystemAddStreamInfo = GetEntryPoint<Proto.FspFileSystemAddStreamInfo>(Module);
_FspFileSystemAddEa = GetEntryPoint<Proto.FspFileSystemAddEa>(Module); _FspFileSystemAddEa = GetEntryPoint<Proto.FspFileSystemAddEa>(Module);
_FspFileSystemAddNotifyInfo = GetEntryPoint<Proto.FspFileSystemAddNotifyInfo>(Module); _FspFileSystemAddNotifyInfo = GetEntryPoint<Proto.FspFileSystemAddNotifyInfo>(Module);
FspFileSystemStopServiceIfNecessary = GetEntryPoint<Proto.FspFileSystemStopServiceIfNecessary>(Module);
FspFileSystemAcquireDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemAcquireDirectoryBuffer>(Module); FspFileSystemAcquireDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemAcquireDirectoryBuffer>(Module);
FspFileSystemFillDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemFillDirectoryBuffer>(Module); FspFileSystemFillDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemFillDirectoryBuffer>(Module);
FspFileSystemReleaseDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemReleaseDirectoryBuffer>(Module); FspFileSystemReleaseDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemReleaseDirectoryBuffer>(Module);

View File

@ -2275,10 +2275,10 @@ static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
#endif #endif
#if defined(MEMFS_DISPATCHER_STOPPED) #if defined(MEMFS_DISPATCHER_STOPPED)
static void DispatcherStopped(FSP_FILE_SYSTEM *FileSystem, static VOID DispatcherStopped(FSP_FILE_SYSTEM *FileSystem,
BOOLEAN Normally) BOOLEAN Normally)
{ {
//FspDebugLog(__FUNCTION__ ": Normally=%d\n", Normally); FspFileSystemStopServiceIfNecessary(FileSystem, Normally);
} }
#endif #endif

View File

@ -1143,6 +1143,12 @@ exit:
return Result; return Result;
} }
static VOID DispatcherStopped(FSP_FILE_SYSTEM *FileSystem,
BOOLEAN Normally)
{
FspFileSystemStopServiceIfNecessary(FileSystem, Normally);
}
static FSP_FILE_SYSTEM_INTERFACE PtfsInterface = static FSP_FILE_SYSTEM_INTERFACE PtfsInterface =
{ {
.GetVolumeInfo = GetVolumeInfo, .GetVolumeInfo = GetVolumeInfo,
@ -1171,6 +1177,7 @@ static FSP_FILE_SYSTEM_INTERFACE PtfsInterface =
.GetStreamInfo = GetStreamInfo, .GetStreamInfo = GetStreamInfo,
.GetEa = GetEa, .GetEa = GetEa,
.SetEa = SetEa, .SetEa = SetEa,
.DispatcherStopped = DispatcherStopped,
}; };
NTSTATUS PtfsCreate( NTSTATUS PtfsCreate(