From db319bc3c1978e072e4e9ba02b3d43ae36e54ab9 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 24 Feb 2023 12:02:03 +0000 Subject: [PATCH] sys,dll: mount improvements - sys: FspFsvolFileSystemControl: FSCTL_IS_VOLUME_MOUNTED - dll: mount: Transact0, FspMountNotifyShellDriveChange --- src/dll/mount.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++-- src/sys/fsctl.c | 1 + 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/dll/mount.c b/src/dll/mount.c index 5e63e8e8..3a3b7388 100644 --- a/src/dll/mount.c +++ b/src/dll/mount.c @@ -21,6 +21,7 @@ #include #include +#include static INIT_ONCE FspMountInitOnce = INIT_ONCE_STATIC_INIT; static NTSTATUS (NTAPI *FspNtOpenSymbolicLinkObject)( @@ -350,6 +351,19 @@ exit: return Result; } +static VOID FspMountNotifyShellDriveChange(VOID) +{ + HRESULT HResult; + LPITEMIDLIST Pidl; + + HResult = SHGetKnownFolderIDList(&FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, 0, &Pidl); + if (SUCCEEDED(HResult)) + { + SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, Pidl, 0); + CoTaskMemFree(Pidl); + } +} + static NTSTATUS FspMountSet_Drive(PWSTR VolumeName, PWSTR MountPoint, PHANDLE PMountHandle) { NTSTATUS Result; @@ -568,7 +582,7 @@ static NTSTATUS FspMountRemove_Directory(HANDLE MountHandle) return CloseHandle(MountHandle) ? STATUS_SUCCESS : FspNtStatusFromWin32(GetLastError()); } -FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc) +NTSTATUS FspMountSet_Internal(FSP_MOUNT_DESC *Desc) { InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0); @@ -609,7 +623,7 @@ FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc) &Desc->MountHandle); } -FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc) +NTSTATUS FspMountRemove_Internal(FSP_MOUNT_DESC *Desc) { InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0); @@ -623,3 +637,81 @@ FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc) else return FspMountRemove_Directory(Desc->MountHandle); } + +FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc) +{ + NTSTATUS Result; + BOOLEAN IsDrive; + + IsDrive = + (L'*' == Desc->MountPoint[0] && ':' == Desc->MountPoint[1] && L'\0' == Desc->MountPoint[2]) || + FspPathIsMountmgrDrive(Desc->MountPoint) || + FspPathIsDrive(Desc->MountPoint); + +#if defined(FSP_CFG_REJECT_EARLY_IRP) + /* + * In the original WinFsp design the FSD could accept incoming file system requests + * immediately after the in-kernel file system instance was created. Such requests would + * be queued in the internal FSD queues and only delivered to the user mode file system + * when its dispatcher was started and actively receiving them. + * + * At the same time the original WinFsp API design was that a user mode file system first + * calls FspFileSystemSetMountPoint to create the file system mount point and then calls + * FspFileSystemStartDispatcher to start the dispatcher. This design made sense at the time: + * creating a mount point involved the creation of a symbolic link of one kind or another, + * which could fail for a number of reasons (e.g. drive already exists), so there was no + * point to start the dispatcher if the mounting did not succeed. Compatibility with FUSE + * and the Unix mounting protocol was another consideration. + * + * Unfortunately this API design has proved problematic. The problem is that with the + * proliferation of ways to mount a file system in WinFsp more and more system components and + * third party filters may attempt to access the mount point upon its creation and before the + * file system dispatcher is ready. This can result in various consequences. + * + * In order to properly fix this problem we should probably mandate that a user mode file + * system should start its dispatcher first and then create its mountpoint. This way the user + * mode file system would be ready to handle any requests from system components or third + * party filters during mount point creation. Unfortunately we cannot easily do so because + * of backwards compatibility. + * + * This problem first appeared as an incompatibility with Avast AntiVirus (GitHub issue #221). + * In order to avoid backwards incompatible API changes the "Transact0" work around was + * devised: when the FSD first creates an in-kernel file system it remains inoperative and any + * requests posted to it will fail with STATUS_CANCELLED, until it receives a Transact0 + * message (an FSP_FSCTL_TRANSACT message with 0 buffers). In order to enable this work around + * a user mode file system would specify the RejectIrpPriorToTransact0 flag upon creation. + * + * Another instance of this problem appeared when support for directory mounting via the Mount + * Manager was added: mounting would not complete unless the RejectIrpPriorToTransact0 flag + * was set. At this point the RejectIrpPriorToTransact0 was hard coded to 1 in the FSD. + * + * However if we are creating a drive the Transact0 work around is unnecessary and perhaps + * harmful. So in this case send a Transact0 message to the FSD to allow file system requests + * to be queued rather than rejected with STATUS_CANCELLED. + */ + if (IsDrive) + FspFsctlTransact(Desc->VolumeHandle, 0, 0, 0, 0, FALSE); +#endif + + Result = FspMountSet_Internal(Desc); + + return Result; +} + +FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc) +{ + NTSTATUS Result; + BOOLEAN IsDrive; + + IsDrive = + FspPathIsMountmgrDrive(Desc->MountPoint) || + FspPathIsDrive(Desc->MountPoint); + + Result = FspMountRemove_Internal(Desc); + + if (NT_SUCCESS(Result) && IsDrive) + /* send an extra notification to remove the "ghost" drive in the shell's navigation pane */ + FspMountNotifyShellDriveChange(); + + return Result; +} diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index 8b12e8e6..9fbafebf 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -731,6 +731,7 @@ static NTSTATUS FspFsvolFileSystemControl( Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp); break; case FSP_FSCTL_QUERY_WINFSP: + case FSCTL_IS_VOLUME_MOUNTED: Irp->IoStatus.Information = 0; Result = STATUS_SUCCESS; break;