mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 01:12:58 -05:00
Compare commits
27 Commits
v1.1B2
...
v1.1.17192
Author | SHA1 | Date | |
---|---|---|---|
637f461a65 | |||
b35bf204db | |||
3073646f29 | |||
7f9f55de24 | |||
bb3f8d37f2 | |||
c72a9f2a05 | |||
9b4ab190e0 | |||
010ed909ec | |||
2b4549a50d | |||
98a329e81b | |||
8090b7c666 | |||
c7d720eaa0 | |||
8320160d73 | |||
ce057b49b8 | |||
a60c989089 | |||
0f6371f0d8 | |||
1a4bbbe09a | |||
4e891dc2a8 | |||
18a77d63c3 | |||
4ea9c6e362 | |||
9d77c192a8 | |||
6d5401d911 | |||
330d6e79f8 | |||
ed58b7a63c | |||
f6853114c1 | |||
8ec7285d32 | |||
c183c0fe89 |
@ -5,6 +5,22 @@ v1.1 (2017.1)::
|
|||||||
|
|
||||||
This release brings some major new components and improvements.
|
This release brings some major new components and improvements.
|
||||||
|
|
||||||
|
- A .NET layer that allows the creation of file systems in managed mode. This is contained in the new `winfsp-msil.dll`. The new .NET layer is being tested with the WinFsp test suites and Microsoft's ifstest.
|
||||||
|
- FUSE for Cygwin is now included with the installer.
|
||||||
|
- FUSE now has a `-ovolname=VOLNAME` parameter that allows setting the volume label. Thanks @samkelly.
|
||||||
|
- A number of other FUSE improvements have been made (see issue #85).
|
||||||
|
|
||||||
|
NOTE: The C++ layer included in the v1.1 beta releases is not part of this release as it is still work in progress. It can be found in `inc/winfsp/winfsp.hpp` in the WinFsp source repository.
|
||||||
|
|
||||||
|
|
||||||
|
v1.1B3 (2017.1 B3)::
|
||||||
|
|
||||||
|
v1.1B2 (2017.1 B2)::
|
||||||
|
|
||||||
|
v1.1B1 (2017.1 BETA)::
|
||||||
|
|
||||||
|
This release brings some major new components and improvements.
|
||||||
|
|
||||||
- A .NET layer that allows the creation of file systems in managed mode. This is contained in the new `winfsp-msil.dll`. The new .NET layer is being tested with the WinFsp test suites and Microsoft's ifstest.
|
- A .NET layer that allows the creation of file systems in managed mode. This is contained in the new `winfsp-msil.dll`. The new .NET layer is being tested with the WinFsp test suites and Microsoft's ifstest.
|
||||||
- A simple C++ layer can be found in `inc/winfsp/winfsp.hpp`.
|
- A simple C++ layer can be found in `inc/winfsp/winfsp.hpp`.
|
||||||
- FUSE for Cygwin is now included with the installer.
|
- FUSE for Cygwin is now included with the installer.
|
||||||
|
@ -252,9 +252,9 @@
|
|||||||
<Component Id="C.winfsp.h">
|
<Component Id="C.winfsp.h">
|
||||||
<File Name="winfsp.h" KeyPath="yes" />
|
<File Name="winfsp.h" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
<Component Id="C.winfsp.hpp">
|
<!--Component Id="C.winfsp.hpp">
|
||||||
<File Name="winfsp.hpp" KeyPath="yes" />
|
<File Name="winfsp.hpp" KeyPath="yes" />
|
||||||
</Component>
|
</Component-->
|
||||||
</Directory>
|
</Directory>
|
||||||
<Directory Id="INCDIR.fuse" Name="fuse">
|
<Directory Id="INCDIR.fuse" Name="fuse">
|
||||||
<Component Id="C.fuse.h">
|
<Component Id="C.fuse.h">
|
||||||
@ -301,9 +301,16 @@
|
|||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
<DirectoryRef Id="OPTDIR">
|
<DirectoryRef Id="OPTDIR">
|
||||||
<Directory Id="OPTDIR.cygfuse" Name="cygfuse" FileSource="..\..\..\opt\cygfuse\dist">
|
<Directory Id="OPTDIR.cygfuse" Name="cygfuse" FileSource="..\..\..\opt\cygfuse\dist">
|
||||||
<Component Id="C.fuse.tar.xz">
|
<Directory Id="OPTDIR.cygfuse.x64" Name="x64">
|
||||||
<File Name="fuse-2.8-4.tar.xz" KeyPath="yes" />
|
<Component Id="C.fuse.tar.xz.x64">
|
||||||
|
<File Id="FILE.fuse.tar.xz.x64" Name="fuse-2.8-5.tar.xz" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="OPTDIR.cygfuse.x86" Name="x86">
|
||||||
|
<Component Id="C.fuse.tar.xz.x86">
|
||||||
|
<File Id="FILE.fuse.tar.xz.x86" Name="fuse-2.8-5.tar.xz" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
<Component Id="C.fuse.install.sh">
|
<Component Id="C.fuse.install.sh">
|
||||||
<File Name="install.sh" KeyPath="yes" />
|
<File Name="install.sh" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
@ -343,7 +350,7 @@
|
|||||||
<File Name="passthrough.vcxproj.filters" KeyPath="yes" />
|
<File Name="passthrough.vcxproj.filters" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</Directory>
|
</Directory>
|
||||||
<Directory Id="SMPDIR.passthrough_cpp" Name="passthrough-cpp">
|
<!--Directory Id="SMPDIR.passthrough_cpp" Name="passthrough-cpp">
|
||||||
<Component Id="C.passthrough_cpp.cpp">
|
<Component Id="C.passthrough_cpp.cpp">
|
||||||
<File Name="passthrough-cpp.cpp" KeyPath="yes" />
|
<File Name="passthrough-cpp.cpp" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
@ -356,7 +363,7 @@
|
|||||||
<Component Id="C.passthrough_cpp.vcxproj.filters">
|
<Component Id="C.passthrough_cpp.vcxproj.filters">
|
||||||
<File Name="passthrough-cpp.vcxproj.filters" KeyPath="yes" />
|
<File Name="passthrough-cpp.vcxproj.filters" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</Directory>
|
</Directory-->
|
||||||
<Directory Id="SMPDIR.passthrough_fuse" Name="passthrough-fuse">
|
<Directory Id="SMPDIR.passthrough_fuse" Name="passthrough-fuse">
|
||||||
<Component Id="C.passthrough_fuse.c">
|
<Component Id="C.passthrough_fuse.c">
|
||||||
<File Name="passthrough-fuse.c" KeyPath="yes" />
|
<File Name="passthrough-fuse.c" KeyPath="yes" />
|
||||||
@ -447,7 +454,7 @@
|
|||||||
<ComponentGroup Id="C.WinFsp.inc">
|
<ComponentGroup Id="C.WinFsp.inc">
|
||||||
<ComponentRef Id="C.fsctl.h" />
|
<ComponentRef Id="C.fsctl.h" />
|
||||||
<ComponentRef Id="C.winfsp.h" />
|
<ComponentRef Id="C.winfsp.h" />
|
||||||
<ComponentRef Id="C.winfsp.hpp" />
|
<!--ComponentRef Id="C.winfsp.hpp" /-->
|
||||||
<ComponentRef Id="C.fuse.h" />
|
<ComponentRef Id="C.fuse.h" />
|
||||||
<ComponentRef Id="C.fuse_common.h" />
|
<ComponentRef Id="C.fuse_common.h" />
|
||||||
<ComponentRef Id="C.fuse_opt.h" />
|
<ComponentRef Id="C.fuse_opt.h" />
|
||||||
@ -460,7 +467,8 @@
|
|||||||
<ComponentRef Id="C.fuse_x86.pc" />
|
<ComponentRef Id="C.fuse_x86.pc" />
|
||||||
</ComponentGroup>
|
</ComponentGroup>
|
||||||
<ComponentGroup Id="C.WinFsp.opt.fuse">
|
<ComponentGroup Id="C.WinFsp.opt.fuse">
|
||||||
<ComponentRef Id="C.fuse.tar.xz" />
|
<ComponentRef Id="C.fuse.tar.xz.x64" />
|
||||||
|
<ComponentRef Id="C.fuse.tar.xz.x86" />
|
||||||
<ComponentRef Id="C.fuse.install.sh" />
|
<ComponentRef Id="C.fuse.install.sh" />
|
||||||
<ComponentRef Id="C.fuse.uninstall.sh" />
|
<ComponentRef Id="C.fuse.uninstall.sh" />
|
||||||
</ComponentGroup>
|
</ComponentGroup>
|
||||||
@ -474,10 +482,10 @@
|
|||||||
<ComponentRef Id="C.passthrough.sln" />
|
<ComponentRef Id="C.passthrough.sln" />
|
||||||
<ComponentRef Id="C.passthrough.vcxproj" />
|
<ComponentRef Id="C.passthrough.vcxproj" />
|
||||||
<ComponentRef Id="C.passthrough.vcxproj.filters" />
|
<ComponentRef Id="C.passthrough.vcxproj.filters" />
|
||||||
<ComponentRef Id="C.passthrough_cpp.cpp" />
|
<!--ComponentRef Id="C.passthrough_cpp.cpp" /-->
|
||||||
<ComponentRef Id="C.passthrough_cpp.sln" />
|
<!--ComponentRef Id="C.passthrough_cpp.sln" /-->
|
||||||
<ComponentRef Id="C.passthrough_cpp.vcxproj" />
|
<!--ComponentRef Id="C.passthrough_cpp.vcxproj" /-->
|
||||||
<ComponentRef Id="C.passthrough_cpp.vcxproj.filters" />
|
<!--ComponentRef Id="C.passthrough_cpp.vcxproj.filters" /-->
|
||||||
<ComponentRef Id="C.passthrough_fuse.c" />
|
<ComponentRef Id="C.passthrough_fuse.c" />
|
||||||
<ComponentRef Id="C.passthrough_fuse.winposix.c" />
|
<ComponentRef Id="C.passthrough_fuse.winposix.c" />
|
||||||
<ComponentRef Id="C.passthrough_fuse.winposix.h" />
|
<ComponentRef Id="C.passthrough_fuse.winposix.h" />
|
||||||
@ -568,7 +576,7 @@
|
|||||||
Id="F.Cygfuse"
|
Id="F.Cygfuse"
|
||||||
Level="1000"
|
Level="1000"
|
||||||
Title="FUSE for Cygwin"
|
Title="FUSE for Cygwin"
|
||||||
Description="From a Cygwin prompt change to $InstallDir/opt/cygfuse and run install.sh."
|
Description="From a Cygwin prompt change to <InstallDir>/opt/cygfuse and run install.sh."
|
||||||
AllowAdvertise="no"
|
AllowAdvertise="no"
|
||||||
InstallDefault="local"
|
InstallDefault="local"
|
||||||
Absent="allow">
|
Absent="allow">
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
<MyCanonicalVersion>1.1</MyCanonicalVersion>
|
<MyCanonicalVersion>1.1</MyCanonicalVersion>
|
||||||
|
|
||||||
<MyProductVersion>2017.1 B2</MyProductVersion>
|
<MyProductVersion>2017.1</MyProductVersion>
|
||||||
<MyProductStage>Beta</MyProductStage>
|
<MyProductStage>Gold</MyProductStage>
|
||||||
|
|
||||||
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
||||||
<MyVersionWithCommas>$(MyVersion.Replace('.',',')),0</MyVersionWithCommas>
|
<MyVersionWithCommas>$(MyVersion.Replace('.',',')),0</MyVersionWithCommas>
|
||||||
|
@ -10,6 +10,11 @@ I am running Windows 7 and I am finding that the installed driver is not signed.
|
|||||||
https://technet.microsoft.com/en-us/library/security/3033929.aspx
|
https://technet.microsoft.com/en-us/library/security/3033929.aspx
|
||||||
|
|
||||||
|
|
||||||
|
Disconnecting (unmapping) a network drive does not work. [@carlreinke]::
|
||||||
|
|
||||||
|
You may have Dokany installed. Dokany installs its own Network Provider DLL that unfortunately interferes with the WinFsp handling of network drives. The solution is to change your system's Network Provider order and ensure that the WinFsp Network Provider runs before the Dokany one. Instructions on how to change the Network Provider order can be found in this http://blogs.interfacett.com/changing-the-network-provider-order-in-windows-10[article].
|
||||||
|
|
||||||
|
|
||||||
Why is the DLL not installed in the Windows system directories? [@netheril96]::
|
Why is the DLL not installed in the Windows system directories? [@netheril96]::
|
||||||
|
|
||||||
It is true that this would make it convenient to load the DLL, because the Windows loader looks into the Windows system directories when it loads DLL's. However, in the opinion of the WinFsp author, software that does not ship with the OS should not be installing components in the system directories.
|
It is true that this would make it convenient to load the DLL, because the Windows loader looks into the Windows system directories when it loads DLL's. However, in the opinion of the WinFsp author, software that does not ship with the OS should not be installing components in the system directories.
|
||||||
|
@ -17,6 +17,7 @@ The documentation available here discusses various aspects of WinFsp.
|
|||||||
|
|
||||||
- The [[Design|WinFsp-Design]] document describes the high-level design of WinFsp.
|
- The [[Design|WinFsp-Design]] document describes the high-level design of WinFsp.
|
||||||
- The [[IPC|WinFsp-as-an-IPC-Mechanism]] document offers insights into the WinFsp Inter-Process Communication mechanism.
|
- The [[IPC|WinFsp-as-an-IPC-Mechanism]] document offers insights into the WinFsp Inter-Process Communication mechanism.
|
||||||
|
- The [[Queued Events|Queued-Events]] document discusses a low-level synchronization primitive that is largely responsible for the excellent performance of the WinFsp IPC mechanism.
|
||||||
- The [[Service Architecture|WinFsp-Service-Architecture]] document discusses how to intergrate a file system into Windows as a service and the reasons to do so.
|
- The [[Service Architecture|WinFsp-Service-Architecture]] document discusses how to intergrate a file system into Windows as a service and the reasons to do so.
|
||||||
- The [[SSHFS Port Case Study|SSHFS-Port-Case-Study]] document chronicles the creation of the WinFsp-FUSE compatibility layer and the decisions that led to its design.
|
- The [[SSHFS Port Case Study|SSHFS-Port-Case-Study]] document chronicles the creation of the WinFsp-FUSE compatibility layer and the decisions that led to its design.
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ This document contains a list of known file systems and file system libraries th
|
|||||||
== File Systems
|
== File Systems
|
||||||
|
|
||||||
- https://github.com/billziss-gh/nfs-win[nfs-win] - NFS for Windows
|
- https://github.com/billziss-gh/nfs-win[nfs-win] - NFS for Windows
|
||||||
|
- https://github.com/ncw/rclone[rclone] - rsync for cloud storage
|
||||||
|
- https://github.com/hasse69/rar2fs[rar2fs] - FUSE file system for reading RAR archives
|
||||||
- https://github.com/billziss-gh/redditfs[redditfs] - ls -l /r/programming
|
- https://github.com/billziss-gh/redditfs[redditfs] - ls -l /r/programming
|
||||||
- https://github.com/netheril96/securefs[securefs] - Filesystem in userspace (FUSE) with transparent authenticated encryption
|
- https://github.com/netheril96/securefs[securefs] - Filesystem in userspace (FUSE) with transparent authenticated encryption
|
||||||
- https://github.com/billziss-gh/sshfs-win[sshfs-win] - SSHFS for Windows
|
- https://github.com/billziss-gh/sshfs-win[sshfs-win] - SSHFS for Windows
|
||||||
|
105
doc/Queued-Events.asciidoc
Normal file
105
doc/Queued-Events.asciidoc
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
= Queued Events - Windows kernel events with IOCP scheduling characteristics
|
||||||
|
|
||||||
|
In this article I am discussing _Queued Events_. _Queued Events_ are a Windows kernel synchronization mechanism that I invented for https://github.com/billziss-gh/winfsp[WinFsp - FUSE for Windows]. _Queued Events_ behave like kernel Synchronization Events (i.e. Win32 auto-reset events), but provide scheduling characteristics similar to those of I/O Completion Ports.
|
||||||
|
|
||||||
|
== The Problem
|
||||||
|
|
||||||
|
During the later stages of WinFsp development I decided to do some performance testing to understand its behavior and find opportunities for optimization. I found out that WinFsp performed very well in most tested scenarios, but there was one test that seemed to have bad performance for no particular reason.
|
||||||
|
|
||||||
|
I ended up profiling this issue using xperf (included in the https://docs.microsoft.com/en-us/windows-hardware/test/wpt/[Windows Performance Toolkit]), which allows for kernel-level profiling. I spent considerable time looking at the profiling results, but could identify no obvious issue with my code.
|
||||||
|
|
||||||
|
After a day or two of doing this and being stumped I finally had a lightbulb moment: what if the issue is not with my code, but with how the OS schedules threads? Sure enough I had xperf trace context switches and found that on good runs the OS would context switch my file system threads relatively rarely; on bad runs the OS would context switch my threads excessively.
|
||||||
|
|
||||||
|
After contemplating this issue I realized that this was happening because the OS was trying to schedule my threads in a "fair" manner. Windows in general tries to give every thread a chance to run. This can be counter-productive in server-like programs (such as file systems), where all server threads are equivalent and it is actually better to reuse the same thread (if possible) to avoid context switching and other negative effects.
|
||||||
|
|
||||||
|
== The Solution
|
||||||
|
|
||||||
|
One way of looking at WinFsp is as an IPC (Inter-Process Communication) mechanism. The Windows kernel packages file system operations (open, close, read, write) as IRP's (I/O Request Packets) which it sends to WinFsp. WinFsp places them in an _I/O Queue_; at a later time the threads in a user mode file system retrieve the IRP's from the _I/O Queue_ and service them.
|
||||||
|
|
||||||
|
The _I/O Queue_ had gone through multiple iterations, but at the time I was looking at this issue it was using Windows kernel Synchronization Event's (Win32 auto-reset events) for managing threads. The problem was that a wait on a Synchronization Event would be satisfied in a "fair" manner, thus resulting to excessive context switching and bad performance in some circumstances. I needed to find a way to convince Windows to schedule my threads in an "unfair" manner, giving preference to the same thread as much as possible.
|
||||||
|
|
||||||
|
I started considering different schemes where I would associate a different event per thread thus being able to wake up the "correct" thread myself and effectively writing my own mini-scheduler. Luckily I had another lightbulb moment: I/O completion ports already do this better than I would ever be able to!
|
||||||
|
|
||||||
|
The kernel portion of an I/O Completion Port is called a https://msdn.microsoft.com/en-us/library/windows/hardware/ff549547(v=vs.85).aspx[KQUEUE]. KQUEUE's are (unfortunately) not directly exposed to user mode, however they are at the core of the user mode I/O Completion Ports. KQUEUE's are the main reason I/O Completion Ports are so fast as they provide the following scheduling characteristics:
|
||||||
|
|
||||||
|
- They have a Last-In First-Out (LIFO) wait discipline.
|
||||||
|
- They limit the number of threads that can be satisfied concurrently.
|
||||||
|
|
||||||
|
I briefly considered the idea of building _I/O Queues_ directly on top of KQUEUE's, but soon dismissed this idea because _I/O Queues_ are not simple queues but provide additional services, such as IRP cancelation, IRP expiration, etc.
|
||||||
|
|
||||||
|
== Queued Events
|
||||||
|
|
||||||
|
In an ideal scenario I wanted to continue using my implementation of _I/O Queues_ which had undergone considerable testing and I knew it worked. But somehow I had to convince the Windows kernel to change the scheduling characteristics of Synchronization Events to mirror those of a KQUEUE.
|
||||||
|
|
||||||
|
Then I had lightbulb no 3: _Queued Events_ or how to make a queue behave like a Synchronization Event.
|
||||||
|
|
||||||
|
Here is how _Queued Events_ work. A _Queued Event_ consists of a KQUEUE and a spin lock. There is also a single dummy item that can be placed in the KQUEUE.
|
||||||
|
|
||||||
|
The KQUEUE is guaranteed to contain either 0 or 1 items. When the KQUEUE contains 0 items the _Queued Event_ is considered non-signaled. When the KQUEUE contains 1 items the _Queued Event_ is considered signaled.
|
||||||
|
|
||||||
|
ifdef::env-browser[]
|
||||||
|
[ditaa,file="Queued-Events/states.png"]
|
||||||
|
--
|
||||||
|
Non signaled Signaled
|
||||||
|
+---------------------------+ +---------------------------+
|
||||||
|
| Queued Event | | Queued Event |
|
||||||
|
+---------------------------+ +---------------------------+
|
||||||
|
| | | +---------+ |
|
||||||
|
| KQUEUE (empty) | | KQUEUE | DUMMY | |
|
||||||
|
| | | +---------+ |
|
||||||
|
+---------------------------+ +---------------------------+
|
||||||
|
--
|
||||||
|
endif::env-browser[]
|
||||||
|
ifndef::env-browser[image::Queued-Events/states.png[]]
|
||||||
|
|
||||||
|
To transition from the non-signaled to the signaled state, we acquire the spin lock and then insert the dummy item in the KQUEUE using https://msdn.microsoft.com/en-us/library/windows/hardware/ff549570(v=vs.85).aspx[KeInsertQueue]. To transition from the signaled to the non-signaled state, we simply (wait and) remove the dummy item from the KQUEUE using https://msdn.microsoft.com/en-us/library/windows/hardware/ff549605(v=vs.85).aspx[KeRemoveQueue] (without the use of the spin lock).
|
||||||
|
|
||||||
|
----
|
||||||
|
EventSet:
|
||||||
|
AcquireSpinLock
|
||||||
|
if (0 == KeReadState()) // if KQUEUE is empty
|
||||||
|
KeInsertQueue(DUMMY);
|
||||||
|
ReleaseSpinLock
|
||||||
|
|
||||||
|
EventWait:
|
||||||
|
KeRemoveQueue(); // (wait and) remove item
|
||||||
|
----
|
||||||
|
|
||||||
|
First notice that EventSet is serialized by the use of the spin lock. This guarantees that the dummy item can be only inserted ONCE in the KQUEUE and that the only possible signaled state transitions for EventSet are 0->1 and 1->1. This is how https://msdn.microsoft.com/en-us/library/windows/hardware/ff553253(v=vs.85).aspx[KeSetEvent] works for a Synchronization Event.
|
||||||
|
|
||||||
|
Second notice that EventWait is not protected by the spin lock, which means that it can happen at any time including concurrently with EventSet or another EventWait. Notice also that for EventWait the only possible transitions are 1->0 or 0->0 (0->block->0). This is how https://msdn.microsoft.com/en-us/library/windows/hardware/ff553350(v=vs.85).aspx[KeWaitForSingleObject] works for a Synchronization Event.
|
||||||
|
|
||||||
|
ifdef::env-browser[]
|
||||||
|
[ditaa,file="Queued-Events/transitions.png"]
|
||||||
|
--
|
||||||
|
Non signaled Signaled
|
||||||
|
+---------------------------+ +---------------------------+
|
||||||
|
| Queued Event | | Queued Event |
|
||||||
|
+---------------------------+ +---------------------------+
|
||||||
|
| | ---EventSet --> | +---------+ |
|
||||||
|
| KQUEUE (empty) | | KQUEUE | DUMMY | |
|
||||||
|
| | <--EventWait--- | +---------+ |
|
||||||
|
+---------------------------+ +---------------------------+
|
||||||
|
--
|
||||||
|
endif::env-browser[]
|
||||||
|
ifndef::env-browser[image::Queued-Events/transitions.png[]]
|
||||||
|
|
||||||
|
We now have to consider what happens when we have one EventSet concurrently with one or more EventWait's:
|
||||||
|
|
||||||
|
1. The EventWait(s) happen before https://msdn.microsoft.com/en-us/library/windows/hardware/ff549591(v=vs.85).aspx[KeReadState]. If the KQUEUE has an item one EventWait gets satisfied, otherwise it blocks. In this case KeReadState will read the KQUEUE's state as 0 and KeInsertQueue will insert the dummy item, which will unblock the EventWait.
|
||||||
|
2. The EventWait(s) happen after KeReadState, but before KeInsertQueue. If the dummy item was already in the KQUEUE the KeReadState test will fail and KeInsertQueue will not be executed, but EventWait will be satisfied immediately. If the dummy item was not in the KQUEUE the KeReadState will succeed and EventWait will momentarily block until KeInsertQueue releases it.
|
||||||
|
3. The EventWait(s) happen after KeInsertQueue. In this case the dummy item in is the KQUEUE already and one EventWait will be satisfied immediately.
|
||||||
|
|
||||||
|
NOTE: _Queued Events_ cannot cleanly support an EventClear operation. The obvious choice of using KeRemoveQueue with a 0 timeout is insufficient because it would associate the current thread with the KQUEUE and that is not desirable. KeRundownQueue cannot be used either because it disassociates all threads from the KQUEUE.
|
||||||
|
|
||||||
|
The complete implementation of _Queued Events_ within WinFsp can be found here: https://github.com/billziss-gh/winfsp/blob/v1.1/src/sys/driver.h#L655-L795
|
||||||
|
|
||||||
|
== Queued Events Scheduling Characteristics
|
||||||
|
|
||||||
|
Queued Events encapsulate KQUEUE's and therefore inherit their scheduling characteristics:
|
||||||
|
|
||||||
|
- They have a Last-In First-Out (LIFO) wait discipline.
|
||||||
|
- They limit the number of threads that can be satisfied concurrently.
|
||||||
|
|
||||||
|
These characteristics are desirable because they reduce the number of context switches thus speeding up the WinFsp IPC implementation. Performance testing immediately after the incorporation of _Queued Events_ into WinFsp showed significant performance improvements; profiling with xperf showed that context switches among file system threads were now a relatively rare event!
|
||||||
|
|
BIN
doc/Queued-Events/states.png
Normal file
BIN
doc/Queued-Events/states.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
doc/Queued-Events/transitions.png
Normal file
BIN
doc/Queued-Events/transitions.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@ -38,7 +38,7 @@ For example, the MEMFS sample adds the following registry entries in a 64-bit sy
|
|||||||
"Security"="D:P(A;;RPWPLC;;;WD)"
|
"Security"="D:P(A;;RPWPLC;;;WD)"
|
||||||
"JobControl"=dword:00000001
|
"JobControl"=dword:00000001
|
||||||
|
|
||||||
When the WinFsp.Launcher starts up it creates a named pipe that applications can use to start, stop, get information about and list service instances. A small command line utility (`launchctl`) can be used to issue those commands. The CallNamedPipeW API can be used as well.
|
When the WinFsp.Launcher starts up it creates a named pipe that applications can use to start, stop, get information about and list service instances. A small command line utility (`launchctl`) can be used to issue those commands. The `CallNamedPipeW` API can be used as well.
|
||||||
|
|
||||||
One final note regarding security. Notice the `Security` registry value in the example above. This registry value uses SDDL syntax to instruct WinFsp.Launcher to allow Everyone (`WD`) to start (`RP`), stop (`WP`) and get information (`LC`) about the service instance. If the `Security` registry value is missing the default is to allow only LocalSystem and Administrators to control the service instance.
|
One final note regarding security. Notice the `Security` registry value in the example above. This registry value uses SDDL syntax to instruct WinFsp.Launcher to allow Everyone (`WD`) to start (`RP`), stop (`WP`) and get information (`LC`) about the service instance. If the `Security` registry value is missing the default is to allow only LocalSystem and Administrators to control the service instance.
|
||||||
|
|
||||||
@ -47,3 +47,13 @@ One final note regarding security. Notice the `Security` registry value in the e
|
|||||||
WinFsp includes a Network Provider that integrates with Windows and can be used to start and stop user mode file systems from the Windows shell. To achieve this the Network Provider (implemented as part of the WinFsp DLL) works closely with the WinFsp.Launcher service.
|
WinFsp includes a Network Provider that integrates with Windows and can be used to start and stop user mode file systems from the Windows shell. To achieve this the Network Provider (implemented as part of the WinFsp DLL) works closely with the WinFsp.Launcher service.
|
||||||
|
|
||||||
For example, if a user uses the Windows Explorer to map `\\memfs64\share` to the `Z:` drive, the Network Provider will instruct the WinFsp.Launcher to start an instance of the memfs64 service with command line `-i -F NTFS -n 65536 -s 67108864 -u \\memfs64\share -m Z:`. When the user disconnects the `Z:` drive, the Network Provider will instruct the WinFsp.Launcher to stop the previously started instance of the memfs64 service.
|
For example, if a user uses the Windows Explorer to map `\\memfs64\share` to the `Z:` drive, the Network Provider will instruct the WinFsp.Launcher to start an instance of the memfs64 service with command line `-i -F NTFS -n 65536 -s 67108864 -u \\memfs64\share -m Z:`. When the user disconnects the `Z:` drive, the Network Provider will instruct the WinFsp.Launcher to stop the previously started instance of the memfs64 service.
|
||||||
|
|
||||||
|
== File System Credential Support
|
||||||
|
|
||||||
|
Some file systems require credentials in order to allow access and be mounted. Such file systems must add a registry value `Credentials`:
|
||||||
|
|
||||||
|
"Credentials"=dword:00000001
|
||||||
|
|
||||||
|
This will instruct the WinFsp Network Provider to request a password from the user prior to starting the file system. This password will then be securely passed to the WinFsp Launcher which in turn will pass it to the user mode file system on its standard input. The user mode file system must respond `OK` if the password is correct and allows access to the user mode file system. Any other response from the user mode file system (including a timeout without a response) is interpreted as an authentication failure.
|
||||||
|
|
||||||
|
NOTE: During password entry the user may also choose to "remember" the password in which case it will be saved in the Windows Credential Manager.
|
@ -118,6 +118,8 @@ FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop_mt)(struct fsp_fuse_env *env,
|
|||||||
struct fuse *f);
|
struct fuse *f);
|
||||||
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_exit)(struct fsp_fuse_env *env,
|
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_exit)(struct fsp_fuse_env *env,
|
||||||
struct fuse *f);
|
struct fuse *f);
|
||||||
|
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_exited)(struct fsp_fuse_env *env,
|
||||||
|
struct fuse *f);
|
||||||
FSP_FUSE_API struct fuse_context *FSP_FUSE_API_NAME(fsp_fuse_get_context)(struct fsp_fuse_env *env);
|
FSP_FUSE_API struct fuse_context *FSP_FUSE_API_NAME(fsp_fuse_get_context)(struct fsp_fuse_env *env);
|
||||||
|
|
||||||
FSP_FUSE_SYM(
|
FSP_FUSE_SYM(
|
||||||
@ -171,6 +173,13 @@ void fuse_exit(struct fuse *f),
|
|||||||
(fsp_fuse_env(), f);
|
(fsp_fuse_env(), f);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
FSP_FUSE_SYM(
|
||||||
|
int fuse_exited(struct fuse *f),
|
||||||
|
{
|
||||||
|
return FSP_FUSE_API_CALL(fsp_fuse_exited)
|
||||||
|
(fsp_fuse_env(), f);
|
||||||
|
})
|
||||||
|
|
||||||
FSP_FUSE_SYM(
|
FSP_FUSE_SYM(
|
||||||
struct fuse_context *fuse_get_context(void),
|
struct fuse_context *fuse_get_context(void),
|
||||||
{
|
{
|
||||||
|
@ -178,6 +178,7 @@ struct fuse_flock
|
|||||||
fsp_fuse_daemonize, \
|
fsp_fuse_daemonize, \
|
||||||
fsp_fuse_set_signal_handlers, \
|
fsp_fuse_set_signal_handlers, \
|
||||||
0/*conv_to_win_path*/, \
|
0/*conv_to_win_path*/, \
|
||||||
|
{ 0 }, \
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define FSP_FUSE_ENV_INIT \
|
#define FSP_FUSE_ENV_INIT \
|
||||||
@ -187,6 +188,7 @@ struct fuse_flock
|
|||||||
fsp_fuse_daemonize, \
|
fsp_fuse_daemonize, \
|
||||||
fsp_fuse_set_signal_handlers, \
|
fsp_fuse_set_signal_handlers, \
|
||||||
0/*conv_to_win_path*/, \
|
0/*conv_to_win_path*/, \
|
||||||
|
{ 0 }, \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -229,6 +231,7 @@ struct fuse_flock
|
|||||||
fsp_fuse_daemonize, \
|
fsp_fuse_daemonize, \
|
||||||
fsp_fuse_set_signal_handlers, \
|
fsp_fuse_set_signal_handlers, \
|
||||||
fsp_fuse_conv_to_win_path, \
|
fsp_fuse_conv_to_win_path, \
|
||||||
|
{ 0 }, \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -18,3 +18,11 @@ cygport:
|
|||||||
> opt/cygfuse/winfsp-work.tar.gz\
|
> opt/cygfuse/winfsp-work.tar.gz\
|
||||||
)
|
)
|
||||||
CYGPORT_SRC_URI=winfsp-work.tar.gz CYGPORT_SRC_DIR=winfsp-work cygport fuse.cygport download prep compile install package
|
CYGPORT_SRC_URI=winfsp-work.tar.gz CYGPORT_SRC_DIR=winfsp-work cygport fuse.cygport download prep compile install package
|
||||||
|
|
||||||
|
dist: cygport
|
||||||
|
case $(shell uname -m) in \
|
||||||
|
x86_64)\
|
||||||
|
cp fuse-*/dist/fuse/fuse-*[0-9].tar.xz dist/x64 ;;\
|
||||||
|
*)\
|
||||||
|
cp fuse-*/dist/fuse/fuse-*[0-9].tar.xz dist/x86 ;;\
|
||||||
|
esac
|
||||||
|
@ -17,22 +17,39 @@
|
|||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/cygwin.h>
|
#include <sys/cygwin.h>
|
||||||
|
|
||||||
|
static void *cygfuse_init_slow(int force);
|
||||||
static void *cygfuse_init_winfsp();
|
static void *cygfuse_init_winfsp();
|
||||||
static void *cygfuse_init_fail();
|
|
||||||
|
|
||||||
static pthread_mutex_t cygfuse_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t cygfuse_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static void *cygfuse_handle = 0;
|
static void *cygfuse_handle = 0;
|
||||||
|
|
||||||
static inline void cygfuse_init(int force)
|
static inline void *cygfuse_init_fast(void)
|
||||||
{
|
{
|
||||||
|
void *handle = cygfuse_handle;
|
||||||
|
__sync_synchronize(); /* memory barrier */
|
||||||
|
if (0 == handle)
|
||||||
|
handle = cygfuse_init_slow(0);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *cygfuse_init_slow(int force)
|
||||||
|
{
|
||||||
|
void *handle;
|
||||||
pthread_mutex_lock(&cygfuse_mutex);
|
pthread_mutex_lock(&cygfuse_mutex);
|
||||||
if (force || 0 == cygfuse_handle)
|
handle = cygfuse_handle;
|
||||||
cygfuse_handle = cygfuse_init_winfsp();
|
if (force || 0 == handle)
|
||||||
|
{
|
||||||
|
handle = cygfuse_init_winfsp();
|
||||||
|
__sync_synchronize(); /* memory barrier */
|
||||||
|
cygfuse_handle = handle;
|
||||||
|
}
|
||||||
pthread_mutex_unlock(&cygfuse_mutex);
|
pthread_mutex_unlock(&cygfuse_mutex);
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -50,7 +67,7 @@ static inline int cygfuse_daemon(int nochdir, int noclose)
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* force reload of WinFsp DLL to workaround fork() problems */
|
/* force reload of WinFsp DLL to workaround fork() problems */
|
||||||
cygfuse_init(1);
|
cygfuse_init_slow(1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -58,7 +75,7 @@ static inline int cygfuse_daemon(int nochdir, int noclose)
|
|||||||
|
|
||||||
#define FSP_FUSE_API static
|
#define FSP_FUSE_API static
|
||||||
#define FSP_FUSE_API_NAME(api) (* pfn_ ## api)
|
#define FSP_FUSE_API_NAME(api) (* pfn_ ## api)
|
||||||
#define FSP_FUSE_API_CALL(api) (cygfuse_init(0), pfn_ ## api)
|
#define FSP_FUSE_API_CALL(api) (cygfuse_init_fast(), pfn_ ## api)
|
||||||
#define FSP_FUSE_SYM(proto, ...) __attribute__ ((visibility("default"))) proto { __VA_ARGS__ }
|
#define FSP_FUSE_SYM(proto, ...) __attribute__ ((visibility("default"))) proto { __VA_ARGS__ }
|
||||||
#include <fuse_common.h>
|
#include <fuse_common.h>
|
||||||
#include <fuse.h>
|
#include <fuse.h>
|
||||||
@ -74,6 +91,7 @@ static inline int cygfuse_daemon(int nochdir, int noclose)
|
|||||||
if (0 == (*(void **)&(pfn_ ## n) = dlsym(h, #n)))\
|
if (0 == (*(void **)&(pfn_ ## n) = dlsym(h, #n)))\
|
||||||
return cygfuse_init_fail();
|
return cygfuse_init_fail();
|
||||||
|
|
||||||
|
static void *cygfuse_init_fail();
|
||||||
static void *cygfuse_init_winfsp()
|
static void *cygfuse_init_winfsp()
|
||||||
{
|
{
|
||||||
void *h;
|
void *h;
|
||||||
@ -125,6 +143,7 @@ static void *cygfuse_init_winfsp()
|
|||||||
CYGFUSE_GET_API(h, fsp_fuse_loop);
|
CYGFUSE_GET_API(h, fsp_fuse_loop);
|
||||||
CYGFUSE_GET_API(h, fsp_fuse_loop_mt);
|
CYGFUSE_GET_API(h, fsp_fuse_loop_mt);
|
||||||
CYGFUSE_GET_API(h, fsp_fuse_exit);
|
CYGFUSE_GET_API(h, fsp_fuse_exit);
|
||||||
|
CYGFUSE_GET_API(h, fsp_fuse_exited);
|
||||||
CYGFUSE_GET_API(h, fsp_fuse_get_context);
|
CYGFUSE_GET_API(h, fsp_fuse_get_context);
|
||||||
|
|
||||||
/* fuse_opt.h */
|
/* fuse_opt.h */
|
||||||
@ -141,6 +160,7 @@ static void *cygfuse_init_winfsp()
|
|||||||
|
|
||||||
static void *cygfuse_init_fail()
|
static void *cygfuse_init_fail()
|
||||||
{
|
{
|
||||||
abort();
|
fprintf(stderr, "cygfuse: initialization failed: " CYGFUSE_WINFSP_NAME " not found\n");
|
||||||
|
exit(1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
BIN
opt/cygfuse/dist/fuse-2.8-4.tar.xz
vendored
BIN
opt/cygfuse/dist/fuse-2.8-4.tar.xz
vendored
Binary file not shown.
9
opt/cygfuse/dist/install.sh
vendored
9
opt/cygfuse/dist/install.sh
vendored
@ -1 +1,8 @@
|
|||||||
tar -C/ -xaf fuse-2.8-*.tar.xz
|
cd "$(dirname "$0")"
|
||||||
|
case $(uname -m) in
|
||||||
|
x86_64)
|
||||||
|
tar -C/ -xaf x64/fuse-2.8-*.tar.xz ;;
|
||||||
|
*)
|
||||||
|
tar -C/ -xaf x86/fuse-2.8-*.tar.xz ;;
|
||||||
|
esac
|
||||||
|
echo FUSE for Cygwin installed.
|
||||||
|
9
opt/cygfuse/dist/uninstall.sh
vendored
9
opt/cygfuse/dist/uninstall.sh
vendored
@ -1 +1,8 @@
|
|||||||
tar -taf fuse-2.8-*.tar.xz | sed -e '/\/$/d' -e 's/.*/\/&/' | xargs rm -f
|
cd "$(dirname "$0")"
|
||||||
|
case $(uname -m) in
|
||||||
|
x86_64)
|
||||||
|
tar -taf x64/fuse-2.8-*.tar.xz | sed -e '/\/$/d' -e 's/.*/\/&/' | xargs rm -f ;;
|
||||||
|
*)
|
||||||
|
tar -taf x86/fuse-2.8-*.tar.xz | sed -e '/\/$/d' -e 's/.*/\/&/' | xargs rm -f ;;
|
||||||
|
esac
|
||||||
|
echo FUSE for Cygwin uninstalled.
|
||||||
|
BIN
opt/cygfuse/dist/x64/fuse-2.8-5.tar.xz
vendored
Normal file
BIN
opt/cygfuse/dist/x64/fuse-2.8-5.tar.xz
vendored
Normal file
Binary file not shown.
BIN
opt/cygfuse/dist/x86/fuse-2.8-5.tar.xz
vendored
Normal file
BIN
opt/cygfuse/dist/x86/fuse-2.8-5.tar.xz
vendored
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
NAME="fuse"
|
NAME="fuse"
|
||||||
VERSION=2.8
|
VERSION=2.8
|
||||||
RELEASE=4
|
RELEASE=5
|
||||||
CATEGORY="Utils"
|
CATEGORY="Utils"
|
||||||
SUMMARY="WinFsp-FUSE compatibility layer"
|
SUMMARY="WinFsp-FUSE compatibility layer"
|
||||||
DESCRIPTION="WinFsp-FUSE enables FUSE file systems to be run on Cygwin."
|
DESCRIPTION="WinFsp-FUSE enables FUSE file systems to be run on Cygwin."
|
||||||
|
@ -96,8 +96,11 @@ static struct fuse_opt fsp_fuse_core_opts[] =
|
|||||||
FSP_FUSE_CORE_OPT("VolumeSerialNumber=%lx", VolumeParams.VolumeSerialNumber, 0),
|
FSP_FUSE_CORE_OPT("VolumeSerialNumber=%lx", VolumeParams.VolumeSerialNumber, 0),
|
||||||
FSP_FUSE_CORE_OPT("FileInfoTimeout=", set_FileInfoTimeout, 1),
|
FSP_FUSE_CORE_OPT("FileInfoTimeout=", set_FileInfoTimeout, 1),
|
||||||
FSP_FUSE_CORE_OPT("FileInfoTimeout=%d", VolumeParams.FileInfoTimeout, 0),
|
FSP_FUSE_CORE_OPT("FileInfoTimeout=%d", VolumeParams.FileInfoTimeout, 0),
|
||||||
|
FUSE_OPT_KEY("UNC=", 'U'),
|
||||||
FUSE_OPT_KEY("--UNC=", 'U'),
|
FUSE_OPT_KEY("--UNC=", 'U'),
|
||||||
|
FUSE_OPT_KEY("VolumePrefix=", 'U'),
|
||||||
FUSE_OPT_KEY("--VolumePrefix=", 'U'),
|
FUSE_OPT_KEY("--VolumePrefix=", 'U'),
|
||||||
|
FUSE_OPT_KEY("FileSystemName=", 'F'),
|
||||||
FUSE_OPT_KEY("--FileSystemName=", 'F'),
|
FUSE_OPT_KEY("--FileSystemName=", 'F'),
|
||||||
|
|
||||||
FUSE_OPT_END,
|
FUSE_OPT_END,
|
||||||
@ -457,17 +460,27 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
|||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
case 'h':
|
case 'h':
|
||||||
|
/* Note: The limit on FspServiceLog messages is 1024 bytes. This is getting close. */
|
||||||
FspServiceLog(EVENTLOG_ERROR_TYPE, L""
|
FspServiceLog(EVENTLOG_ERROR_TYPE, L""
|
||||||
FSP_FUSE_LIBRARY_NAME " options:\n"
|
FSP_FUSE_LIBRARY_NAME " options:\n"
|
||||||
" -o DebugLog=FILE debug log file (deflt: stderr)\n"
|
" -o umask=MASK set file permissions (octal)\n"
|
||||||
" -o SectorSize=N sector size for Windows (512-4096, deflt: 4096)\n"
|
" -o uid=N set file owner (-1 for mounting user id)\n"
|
||||||
" -o SectorsPerAllocationUnit=N sectors per allocation unit (deflt: 1)\n"
|
" -o gid=N set file group (-1 for mounting user group)\n"
|
||||||
" -o MaxComponentLength=N max file name component length (deflt: 255)\n"
|
" -o rellinks interpret absolute symlinks as volume relative\n"
|
||||||
" -o VolumeCreationTime=T volume creation time (FILETIME hex format)\n"
|
" -o volname=NAME set volume label\n"
|
||||||
" -o VolumeSerialNumber=N 32-bit wide\n"
|
" -o VolumePrefix=UNC set UNC prefix (/Server/Share)\n"
|
||||||
" -o FileInfoTimeout=N FileInfo/Security/VolumeInfo timeout (millisec)\n"
|
" --VolumePrefix=UNC set UNC prefix (\\Server\\Share)\n"
|
||||||
" --UNC=U --VolumePrefix=U UNC prefix (\\Server\\Share)\n"
|
" -o FileSystemName=NAME set file system name\n"
|
||||||
" --FileSystemName=FSN Name of user mode file system\n");
|
" -o DebugLog=FILE debug log file (requires -d)\n"
|
||||||
|
"\n"
|
||||||
|
FSP_FUSE_LIBRARY_NAME " advanced options:\n"
|
||||||
|
" -o FileInfoTimeout=N metadata timeout (millis, -1 for data caching)\n"
|
||||||
|
" -o SectorSize=N (512-4096, deflt: 4096)\n"
|
||||||
|
" -o SectorsPerAllocationUnit=N (deflt: 1)\n"
|
||||||
|
" -o MaxComponentLength=N (deflt: 255)\n"
|
||||||
|
" -o VolumeCreationTime=T (FILETIME hex format)\n"
|
||||||
|
" -o VolumeSerialNumber=N (32-bit wide)\n"
|
||||||
|
);
|
||||||
opt_data->help = 1;
|
opt_data->help = 1;
|
||||||
return 1;
|
return 1;
|
||||||
case 'V':
|
case 'V':
|
||||||
@ -488,8 +501,12 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
|||||||
0);
|
0);
|
||||||
return 0;
|
return 0;
|
||||||
case 'U':
|
case 'U':
|
||||||
if ('U' == arg[2])
|
if ('U' == arg[0])
|
||||||
|
arg += sizeof "UNC=" - 1;
|
||||||
|
else if ('U' == arg[2])
|
||||||
arg += sizeof "--UNC=" - 1;
|
arg += sizeof "--UNC=" - 1;
|
||||||
|
else if ('V' == arg[0])
|
||||||
|
arg += sizeof "VolumePrefix=" - 1;
|
||||||
else if ('V' == arg[2])
|
else if ('V' == arg[2])
|
||||||
arg += sizeof "--VolumePrefix=" - 1;
|
arg += sizeof "--VolumePrefix=" - 1;
|
||||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||||
@ -497,10 +514,15 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
|||||||
return -1;
|
return -1;
|
||||||
opt_data->VolumeParams.Prefix
|
opt_data->VolumeParams.Prefix
|
||||||
[sizeof opt_data->VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0';
|
[sizeof opt_data->VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0';
|
||||||
|
for (PWSTR P = opt_data->VolumeParams.Prefix; *P; P++)
|
||||||
|
if (L'/' == *P)
|
||||||
|
*P = '\\';
|
||||||
return 0;
|
return 0;
|
||||||
case 'F':
|
case 'F':
|
||||||
if ('f' == arg[0])
|
if ('f' == arg[0])
|
||||||
arg += sizeof "fstypename=" - 1;
|
arg += sizeof "fstypename=" - 1;
|
||||||
|
else if ('F' == arg[0])
|
||||||
|
arg += sizeof "FileSystemName=" - 1;
|
||||||
else if ('F' == arg[2])
|
else if ('F' == arg[2])
|
||||||
arg += sizeof "--FileSystemName=" - 1;
|
arg += sizeof "--FileSystemName=" - 1;
|
||||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||||
@ -687,6 +709,13 @@ FSP_FUSE_API void fsp_fuse_exit(struct fsp_fuse_env *env,
|
|||||||
{
|
{
|
||||||
if (0 != f->Service)
|
if (0 != f->Service)
|
||||||
FspServiceStop(f->Service);
|
FspServiceStop(f->Service);
|
||||||
|
f->exited = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_exited)(struct fsp_fuse_env *env,
|
||||||
|
struct fuse *f)
|
||||||
|
{
|
||||||
|
return f->exited;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSP_FUSE_API struct fuse_context *fsp_fuse_get_context(struct fsp_fuse_env *env)
|
FSP_FUSE_API struct fuse_context *fsp_fuse_get_context(struct fsp_fuse_env *env)
|
||||||
|
@ -50,6 +50,7 @@ struct fuse
|
|||||||
PWSTR MountPoint;
|
PWSTR MountPoint;
|
||||||
FSP_FILE_SYSTEM *FileSystem;
|
FSP_FILE_SYSTEM *FileSystem;
|
||||||
FSP_SERVICE *Service; /* weak */
|
FSP_SERVICE *Service; /* weak */
|
||||||
|
volatile int exited;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fsp_fuse_context_header
|
struct fsp_fuse_context_header
|
||||||
|
@ -1098,7 +1098,44 @@ static NTSTATUS FspFsvolCreateTryOpen(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Re
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.Create.Opened.FileInfo,
|
/*
|
||||||
|
* FspFileNodeTrySetFileInfoOnOpen sets the FileNode's metadata to values reported
|
||||||
|
* by the user mode file system. It does so only if the file is not already open; the
|
||||||
|
* reason is that there is a subtle race condition otherwise.
|
||||||
|
*
|
||||||
|
* Consider that a file is already open and (for example) being appended to. The appends
|
||||||
|
* appear as WRITE IRP's and they are all synchronized by acquiring the FileNode resources
|
||||||
|
* exclusive. Part of the WRITE protocol involves returning an updated FileInfo for the
|
||||||
|
* written FileNode, which the FSD uses to update its view of the file metadata.
|
||||||
|
*
|
||||||
|
* Now consider that a file OPEN comes in. Because the FSD does not know yet which FileNode
|
||||||
|
* will be opened it does not acquire any FileNode. [Note that while it is possible to
|
||||||
|
* uniquely identify a FileNode by FileName currently, this would no longer be the case
|
||||||
|
* when we implement hard links.] The FSD posts the OPEN request to the user mode file system.
|
||||||
|
* The user mode file system processes the OPEN request successfully and responds with the
|
||||||
|
* opened file and its FileInfo.
|
||||||
|
*
|
||||||
|
* Prior to the FSD processing the OPEN response additional WRITE IRP's come through the
|
||||||
|
* original file handle. These WRITE's update the FileSize as understood by the user mode
|
||||||
|
* file system and the corresponding FileInfo gets reported back to the FSD. Eventually the
|
||||||
|
* delayed OPEN response gets processed, which now clobbers the FileInfo as understood by
|
||||||
|
* the FSD.
|
||||||
|
*
|
||||||
|
* The problem here is that OPEN requests have no way of locking the FileNode while the OPEN
|
||||||
|
* request is in transit to the user mode file system. In all other cases the user mode file
|
||||||
|
* system and the FSD update their view of the file's metadata in an atomic fashion. Not so
|
||||||
|
* in the case of OPEN.
|
||||||
|
*
|
||||||
|
* While this is a subtle race condition it can nevertheless creates a real problem. For
|
||||||
|
* example, Explorer often opens files to get information about them and may inappropriately
|
||||||
|
* update the FSD view of the file size during WRITE's.
|
||||||
|
*
|
||||||
|
* FspFileNodeTrySetFileInfoOnOpen attempts to mitigate this problem by only updating the
|
||||||
|
* FileInfo if the file is not already open. This avoids placing stale information in the
|
||||||
|
* FileNode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
FspFileNodeTrySetFileInfoOnOpen(FileNode, FileObject, &Response->Rsp.Create.Opened.FileInfo,
|
||||||
FILE_CREATED == Response->IoStatus.Information);
|
FILE_CREATED == Response->IoStatus.Information);
|
||||||
|
|
||||||
if (FlushImage)
|
if (FlushImage)
|
||||||
|
@ -1350,6 +1350,8 @@ VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileIn
|
|||||||
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
||||||
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose);
|
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose);
|
||||||
|
BOOLEAN FspFileNodeTrySetFileInfoOnOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
|
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose);
|
||||||
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber);
|
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber);
|
||||||
VOID FspFileNodeInvalidateFileInfo(FSP_FILE_NODE *FileNode);
|
VOID FspFileNodeInvalidateFileInfo(FSP_FILE_NODE *FileNode);
|
||||||
|
@ -58,6 +58,8 @@ VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileIn
|
|||||||
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
||||||
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose);
|
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose);
|
||||||
|
BOOLEAN FspFileNodeTrySetFileInfoOnOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
|
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose);
|
||||||
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber);
|
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber);
|
||||||
VOID FspFileNodeInvalidateFileInfo(FSP_FILE_NODE *FileNode);
|
VOID FspFileNodeInvalidateFileInfo(FSP_FILE_NODE *FileNode);
|
||||||
@ -129,6 +131,7 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
|||||||
#pragma alloc_text(PAGE, FspFileNodeGetFileInfo)
|
#pragma alloc_text(PAGE, FspFileNodeGetFileInfo)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeTryGetFileInfo)
|
#pragma alloc_text(PAGE, FspFileNodeTryGetFileInfo)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeSetFileInfo)
|
#pragma alloc_text(PAGE, FspFileNodeSetFileInfo)
|
||||||
|
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfoOnOpen)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfo)
|
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfo)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeInvalidateFileInfo)
|
#pragma alloc_text(PAGE, FspFileNodeInvalidateFileInfo)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeReferenceSecurity)
|
#pragma alloc_text(PAGE, FspFileNodeReferenceSecurity)
|
||||||
@ -1660,6 +1663,43 @@ VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOLEAN FspFileNodeTrySetFileInfoOnOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
|
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
BOOLEAN EarlyExit;
|
||||||
|
|
||||||
|
FspFsvolDeviceLockContextTable(FileNode->FsvolDeviceObject);
|
||||||
|
EarlyExit = 1 < FileNode->OpenCount;
|
||||||
|
FspFsvolDeviceUnlockContextTable(FileNode->FsvolDeviceObject);
|
||||||
|
|
||||||
|
if (EarlyExit)
|
||||||
|
{
|
||||||
|
if (TruncateOnClose)
|
||||||
|
{
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||||
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
||||||
|
UINT64 AllocationSize = FileInfo->AllocationSize > FileInfo->FileSize ?
|
||||||
|
FileInfo->AllocationSize : FileInfo->FileSize;
|
||||||
|
UINT64 AllocationUnit;
|
||||||
|
|
||||||
|
AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize *
|
||||||
|
FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit;
|
||||||
|
AllocationSize = (AllocationSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit;
|
||||||
|
|
||||||
|
if ((UINT64)FileNode->Header.AllocationSize.QuadPart != AllocationSize ||
|
||||||
|
(UINT64)FileNode->Header.FileSize.QuadPart != FileInfo->FileSize)
|
||||||
|
FileNode->TruncateOnClose = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
FspFileNodeSetFileInfo(FileNode, CcFileObject, FileInfo, TruncateOnClose);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
||||||
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber)
|
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber)
|
||||||
{
|
{
|
||||||
|
@ -62,15 +62,17 @@ set dfl_tests=^
|
|||||||
winfstest-memfs-x86-net ^
|
winfstest-memfs-x86-net ^
|
||||||
fscrash-x86 ^
|
fscrash-x86 ^
|
||||||
winfsp-tests-dotnet-external ^
|
winfsp-tests-dotnet-external ^
|
||||||
winfsp-tests-dotnet-external-share
|
winfsp-tests-dotnet-external-share ^
|
||||||
|
fsx-memfs-dotnet-disk ^
|
||||||
|
fsx-memfs-dotnet-net ^
|
||||||
|
winfstest-memfs-dotnet-disk ^
|
||||||
|
winfstest-memfs-dotnet-net
|
||||||
set opt_tests=^
|
set opt_tests=^
|
||||||
ifstest-memfs-x64-disk ^
|
ifstest-memfs-x64-disk ^
|
||||||
ifstest-memfs-x86-disk ^
|
ifstest-memfs-x86-disk ^
|
||||||
ifstest-memfs-dotnet-disk ^
|
ifstest-memfs-dotnet-disk ^
|
||||||
sample-passthrough-x64 ^
|
sample-passthrough-x64 ^
|
||||||
sample-passthrough-x86 ^
|
sample-passthrough-x86 ^
|
||||||
sample-passthrough-cpp-x64 ^
|
|
||||||
sample-passthrough-cpp-x86 ^
|
|
||||||
sample-passthrough-fuse-x64 ^
|
sample-passthrough-fuse-x64 ^
|
||||||
sample-passthrough-fuse-x86 ^
|
sample-passthrough-fuse-x86 ^
|
||||||
sample-passthrough-dotnet
|
sample-passthrough-dotnet
|
||||||
@ -387,6 +389,34 @@ Q:
|
|||||||
if !ERRORLEVEL! neq 0 goto fail
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
exit /b 0
|
exit /b 0
|
||||||
|
|
||||||
|
:fsx-memfs-dotnet-disk
|
||||||
|
Q:
|
||||||
|
"%ProjRoot%\ext\test\fstools\src\fsx\fsx.exe" -N 5000 test xxxxxx
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
"%ProjRoot%\ext\test\fstools\src\fsx\fsx.exe" -f foo -N 5000 test xxxxxx
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:fsx-memfs-dotnet-net
|
||||||
|
R:
|
||||||
|
"%ProjRoot%\ext\test\fstools\src\fsx\fsx.exe" -N 5000 test xxxxxx
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
"%ProjRoot%\ext\test\fstools\src\fsx\fsx.exe" -f foo -N 5000 test xxxxxx
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:winfstest-memfs-dotnet-disk
|
||||||
|
Q:
|
||||||
|
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat"
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:winfstest-memfs-dotnet-net
|
||||||
|
R:
|
||||||
|
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat"
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
:ifstest-memfs-x64-disk
|
:ifstest-memfs-x64-disk
|
||||||
call :__ifstest-memfs M: \Device\WinFsp.Disk C:
|
call :__ifstest-memfs M: \Device\WinFsp.Disk C:
|
||||||
if !ERRORLEVEL! neq 0 goto fail
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
@ -192,7 +192,7 @@ typedef struct _MEMFS_FILE_NODE
|
|||||||
SIZE_T ReparseDataSize;
|
SIZE_T ReparseDataSize;
|
||||||
PVOID ReparseData;
|
PVOID ReparseData;
|
||||||
#endif
|
#endif
|
||||||
ULONG RefCount;
|
volatile LONG RefCount;
|
||||||
#if defined(MEMFS_NAMED_STREAMS)
|
#if defined(MEMFS_NAMED_STREAMS)
|
||||||
struct _MEMFS_FILE_NODE *MainFileNode;
|
struct _MEMFS_FILE_NODE *MainFileNode;
|
||||||
#endif
|
#endif
|
||||||
@ -260,13 +260,13 @@ VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode)
|
|||||||
static inline
|
static inline
|
||||||
VOID MemfsFileNodeReference(MEMFS_FILE_NODE *FileNode)
|
VOID MemfsFileNodeReference(MEMFS_FILE_NODE *FileNode)
|
||||||
{
|
{
|
||||||
FileNode->RefCount++;
|
InterlockedIncrement(&FileNode->RefCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
VOID MemfsFileNodeDereference(MEMFS_FILE_NODE *FileNode)
|
VOID MemfsFileNodeDereference(MEMFS_FILE_NODE *FileNode)
|
||||||
{
|
{
|
||||||
if (0 == --FileNode->RefCount)
|
if (0 == InterlockedDecrement(&FileNode->RefCount))
|
||||||
MemfsFileNodeDelete(FileNode);
|
MemfsFileNodeDelete(FileNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,14 +850,18 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
|
|
||||||
#if defined(MEMFS_NAMED_STREAMS)
|
#if defined(MEMFS_NAMED_STREAMS)
|
||||||
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
|
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { TRUE };
|
||||||
ULONG Index;
|
ULONG Index;
|
||||||
|
|
||||||
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
|
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
|
||||||
MemfsFileNodeMapEnumerateFn, &Context);
|
MemfsFileNodeMapEnumerateFn, &Context);
|
||||||
for (Index = 0; Context.Count > Index; Index++)
|
for (Index = 0; Context.Count > Index; Index++)
|
||||||
if (1 >= Context.FileNodes[Index]->RefCount)
|
{
|
||||||
|
LONG RefCount = Context.FileNodes[Index]->RefCount;
|
||||||
|
MemoryBarrier();
|
||||||
|
if (2 >= RefCount)
|
||||||
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
|
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
|
||||||
|
}
|
||||||
MemfsFileNodeMapEnumerateFree(&Context);
|
MemfsFileNodeMapEnumerateFree(&Context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user