sys,dll: mount improvements

- sys: FspFsvolFileSystemControl: FSCTL_IS_VOLUME_MOUNTED
- dll: mount: Transact0, FspMountNotifyShellDriveChange
This commit is contained in:
Bill Zissimopoulos 2023-02-24 12:02:03 +00:00
parent c04e3d9534
commit db319bc3c1
2 changed files with 95 additions and 2 deletions

View File

@ -21,6 +21,7 @@
#include <dll/library.h> #include <dll/library.h>
#include <dbt.h> #include <dbt.h>
#include <shlobj.h>
static INIT_ONCE FspMountInitOnce = INIT_ONCE_STATIC_INIT; static INIT_ONCE FspMountInitOnce = INIT_ONCE_STATIC_INIT;
static NTSTATUS (NTAPI *FspNtOpenSymbolicLinkObject)( static NTSTATUS (NTAPI *FspNtOpenSymbolicLinkObject)(
@ -350,6 +351,19 @@ exit:
return Result; 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) static NTSTATUS FspMountSet_Drive(PWSTR VolumeName, PWSTR MountPoint, PHANDLE PMountHandle)
{ {
NTSTATUS Result; NTSTATUS Result;
@ -568,7 +582,7 @@ static NTSTATUS FspMountRemove_Directory(HANDLE MountHandle)
return CloseHandle(MountHandle) ? STATUS_SUCCESS : FspNtStatusFromWin32(GetLastError()); 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); InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0);
@ -609,7 +623,7 @@ FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc)
&Desc->MountHandle); &Desc->MountHandle);
} }
FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc) NTSTATUS FspMountRemove_Internal(FSP_MOUNT_DESC *Desc)
{ {
InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0); InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0);
@ -623,3 +637,81 @@ FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc)
else else
return FspMountRemove_Directory(Desc->MountHandle); 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;
}

View File

@ -731,6 +731,7 @@ static NTSTATUS FspFsvolFileSystemControl(
Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp); Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp);
break; break;
case FSP_FSCTL_QUERY_WINFSP: case FSP_FSCTL_QUERY_WINFSP:
case FSCTL_IS_VOLUME_MOUNTED:
Irp->IoStatus.Information = 0; Irp->IoStatus.Information = 0;
Result = STATUS_SUCCESS; Result = STATUS_SUCCESS;
break; break;