Compare commits

...

71 Commits

Author SHA1 Message Date
637f461a65 sys: FspFileNodeTrySetFileInfoOnOpen 2017-07-11 15:23:03 -07:00
b35bf204db sys: FspFileNodeTrySetFileInfoOnOpen 2017-07-11 14:17:17 -07:00
3073646f29 doc: add link to queued events document 2017-06-21 14:32:18 -07:00
7f9f55de24 add queued events document 2017-06-21 14:19:56 -07:00
bb3f8d37f2 tools: run-tests: disable passthrough-cpp that is no longer included in installer 2017-06-13 11:13:04 -07:00
c72a9f2a05 build: update for release 1.1 2017-06-12 13:42:05 -07:00
9b4ab190e0 installer: do not install C++ layer 2017-06-12 13:33:45 -07:00
010ed909ec doc: update faq 2017-06-12 12:54:06 -07:00
2b4549a50d doc: update service arch doc with information about credentials 2017-06-12 12:40:50 -07:00
98a329e81b tst: memfs: atomically update FileNode::RefCount (issue #93) 2017-06-07 22:22:28 -07:00
8090b7c666 update Changelog 2017-05-22 17:42:55 -07:00
c7d720eaa0 dll: fuse: allows slashes in -o VolumePrefix=PREFIX 2017-05-22 17:02:40 -07:00
8320160d73 build: version.properties: update to v1.1B3 2017-05-22 13:49:59 -07:00
ce057b49b8 update Changelog 2017-05-22 13:30:16 -07:00
a60c989089 update Changelog 2017-05-22 21:25:44 +01:00
0f6371f0d8 tools: run-tests: add fsx and winfstest for memfs-dotnet 2017-05-17 21:07:37 -07:00
1a4bbbe09a cygfuse: fix tabs to spaces 2017-05-17 15:19:36 -07:00
4e891dc2a8 cygfuse: add fuse_exited 2017-05-17 15:13:05 -07:00
18a77d63c3 cygfuse: improve cygfuse initialization 2017-05-17 14:59:31 -07:00
4ea9c6e362 dll: fuse: added -o options for additional WinFsp-FUSE options 2017-05-17 12:11:45 -07:00
9d77c192a8 cygfuse: cygfuse_init_fail now prints message and exits 2017-05-17 10:58:04 -07:00
6d5401d911 cygfuse: add 32-bit package 2017-05-17 00:54:26 -07:00
330d6e79f8 opt: cygfuse: bump release number to 5 2017-05-17 00:05:41 -07:00
ed58b7a63c inc: fuse: fix missing-field-initializers warning 2017-05-16 23:41:02 -07:00
f6853114c1 dll: fuse: implement fuse_exited 2017-05-16 23:26:18 -07:00
8ec7285d32 doc: add rclone to known file systems 2017-05-15 15:12:26 -07:00
c183c0fe89 doc: add rar2fs to the known file systems 2017-05-13 16:37:26 -07:00
38ad8fd27d update README 2017-05-11 13:51:56 -07:00
de85070e73 tst: winfsp-tests: disable internal tests when running with --external 2017-05-11 11:17:57 -07:00
5b8ebd6e1d src: dotnet: minor documentation fixes 2017-05-11 10:38:12 -07:00
db530cb5e5 installer: add dotnet documentation 2017-05-10 23:32:43 -07:00
7cd4d4faab src: dotnet: add documentation 2017-05-10 23:11:42 -07:00
2560a513dc update changelog 2017-05-09 23:12:45 -07:00
1ee95be5d7 src: dotnet: FileSystemBase: GetStreamInfo: bug fix 2017-05-09 23:01:31 -07:00
bd7546559c tst: memfs-dotnet: Create: fix allocation size 2017-05-09 22:02:37 -07:00
d18a2c8b75 tst: memfs-dotnet: ReadDirectoryEntry: properly handle Marker 2017-05-09 17:49:43 -07:00
0ebae0adc1 src: dotnet: FileSystemHost.GetSecurityByName: handle STATUS_REPARSE 2017-05-09 16:58:16 -07:00
d70c49ccd0 tools: run-tests: add memfs-dotnet testing 2017-05-09 00:03:01 -07:00
5846939116 tools: run-tests: add memfs-dotnet testing 2017-05-08 23:11:45 -07:00
f124e74f01 installer: add memfs-dotnet 2017-05-08 22:56:06 -07:00
05abb93e4b tst: memfs-dotnet: fix stream_getstreaminfo_test 2017-05-08 22:17:17 -07:00
5839d46b7a tst: memfs-dotnet: fix stream_create_overwrite_test 2017-05-08 21:57:45 -07:00
bce0d63f7d tst: memfs-dotnet: fix read/write offset copy 2017-05-08 21:53:49 -07:00
14b9f5affc tst: memfs-dotnet: fix rename_caseins_test 2017-05-08 21:45:30 -07:00
035a430470 tst: dotnet: SetSecurity testing 2017-05-08 21:25:25 -07:00
0af9e46e76 src: dotnet: FileSystemBase.ModifySecurityDescriptor 2017-05-08 21:25:03 -07:00
c4530f1252 tst: memfs-dotnet: testing 2017-05-07 17:40:47 -07:00
9f78a17583 tst: memfs-dotnet: reparse points testing 2017-05-07 17:12:08 -07:00
eea0b1bc79 src: dotnet: GetReparseTag 2017-05-07 17:11:39 -07:00
8338a6e066 tst: memfs-dotnet: fix exceptions in SetFileSizeInternal 2017-05-07 16:29:28 -07:00
ddba49dbea tst: memfs-dotnet: remove unnecessary OpenNodeSet 2017-05-07 16:28:49 -07:00
a6ff8a87de tst: memfs-dotnet: ReadDirectory fixes 2017-05-07 15:45:01 -07:00
bf64bcf9ba dotnet: fix problems with FullContext and GCHandle 2017-05-07 15:13:22 -07:00
8c5d9bda20 tst: memfs-dotnet: testing 2017-05-06 23:40:46 -07:00
f1ac28b0aa dotnet: log exceptions 2017-05-06 23:39:58 -07:00
ff725f931d tst: memfs-dotnet: testing 2017-05-06 23:10:40 -07:00
31519ba416 dotnet: bug fixes 2017-05-06 17:01:55 -07:00
0bca8f851c tst: memfs-dotnet: remove dead code 2017-05-06 15:06:29 -07:00
acf175da60 tst: memfs-dotnet: WIP 2017-05-06 14:49:17 -07:00
23eac24c84 dotnet: FileSystemBase.GetStreamEntry 2017-05-06 14:48:56 -07:00
0f9ef3bd51 tst: memfs-dotnet: WIP 2017-05-05 20:30:07 -07:00
2bdd54536e dotnet: reparse point WIP 2017-05-05 20:29:47 -07:00
060ebcca0d tst: memfs-dotnet: WIP 2017-05-05 18:24:17 -07:00
b38a89e485 dotnet: reparse point changes 2017-05-05 18:23:52 -07:00
b5bfeee027 dotnet: FileSystemHost: fixes 2017-05-05 16:15:29 -07:00
f36cacaf84 tst: memfs-dotnet: WIP 2017-05-05 14:47:48 -07:00
4278cec465 tst: memfs-dotnet: WIP 2017-05-05 12:08:32 -07:00
151627091b tst: memfs-dotnet: WIP 2017-05-04 23:00:53 -07:00
2ee3f02928 tst: memfs-dotnet: WIP 2017-05-04 21:56:46 -07:00
d77d3ccccf tools: version-info.bat: toggle executable bit 2017-05-04 15:19:11 -07:00
1e0c91658e doc: add cgofuse to known file systems 2017-04-28 14:52:05 -07:00
50 changed files with 2902 additions and 192 deletions

View File

@ -5,7 +5,23 @@ v1.1 (2017.1)::
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`.
- 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 simple C++ layer can be found in `inc/winfsp/winfsp.hpp`.
- FUSE for Cygwin is now included with the installer.
- FUSE now has a `-ovolname=VOLNAME` parameter that allows setting the volume label. Thanks @samkelly.

View File

@ -42,7 +42,8 @@ The project source code is organized as follows:
* src/launcher: Source code to the launcher service and the launchctl utility.
* src/sys: Source code to the WinFsp FSD.
* opt/cygfuse: Source code for the Cygwin FUSE package.
* tst/memfs: Source code to an example file system written in C++ (memfs).
* tst/memfs*: Source code to an example file system written in C/C++ (memfs) or C# (memfs-dotnet).
* tst/passthrough*: Source code to additional example file systems.
* tst/winfsp-tests: WinFsp test suite.
## Building and Running
@ -72,7 +73,7 @@ WinFsp is designed to run on Windows 7 and above. It has been tested on the foll
I am looking for help in the following areas:
* If you have a file system that runs on FUSE please consider porting it to WinFsp. WinFsp has a native API, but it also has a FUSE (high-level) API.
* If you are working with a language other than C/C++ (e.g. Delphi, C#, etc.) and you are interested in porting/wrapping WinFsp I would love to hear from you.
* If you are working with a language other than C/C++ (e.g. Delphi, Java, etc.) and you are interested in porting/wrapping WinFsp I would love to hear from you.
* There are a number of outstanding issues listed in the [GitHub repository](https://github.com/billziss-gh/winfsp/issues). Many of these require knowledge of Windows kernel-mode and an understanding of the internals of WinFsp so they are not for the faint of heart.
In all cases I can provide ideas and/or support.

View File

@ -3,3 +3,4 @@ build
*.suo
*.vcproj.*
*.vcxproj.user
*.csproj.user

View File

@ -25,6 +25,8 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>$(BaseIntermediateOutputPath)$(Configuration)\winfsp-msil.xml</DocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -36,6 +38,8 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>$(BaseIntermediateOutputPath)$(Configuration)\winfsp-msil.xml</DocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>

View File

@ -92,6 +92,9 @@
<Component Id="C.winfsp_msil.dll" Guid="0D8BA6AE-9F87-402B-AE1A-95B0AE3BE179">
<File Id="FILE.winfsp_msil.dll" Name="winfsp-msil.dll" KeyPath="yes" />
</Component>
<Component Id="C.winfsp_msil.xml" Guid="1657F707-C112-454C-91AE-0FDEBBF454AB">
<File Id="FILE.winfsp_msil.xml" Name="winfsp-msil.xml" KeyPath="yes" />
</Component>
<!--
<Component Id="C.winfsp_msil.dll.GAC" Guid="6469467D-8C90-4889-8138-4028F9DA6E85">
<File Id="FILE.winfsp_msil.dll.GAC" Name="winfsp-msil.dll" KeyPath="yes" Assembly=".net" />
@ -214,6 +217,32 @@
</RegistryKey>
</RegistryKey>
</Component>
<Component Id="C.memfs_dotnet_msil.exe">
<File Name="memfs-dotnet-msil.exe" KeyPath="yes" />
<RegistryKey
Root="HKLM"
Key="[P.LauncherRegistryKey]">
<RegistryKey
Key="memfs-dotnet">
<RegistryValue
Type="string"
Name="Executable"
Value="[BINDIR]memfs-dotnet-msil.exe" />
<RegistryValue
Type="string"
Name="CommandLine"
Value="-i -F NTFS -n 65536 -s 67108864 -u %1 -m %2" />
<RegistryValue
Type="string"
Name="Security"
Value="D:P(A;;RPWPLC;;;WD)" />
<RegistryValue
Type="integer"
Name="JobControl"
Value="1" />
</RegistryKey>
</RegistryKey>
</Component>
</DirectoryRef>
<DirectoryRef Id="INCDIR" FileSource="..\..\..\inc">
<Directory Id="INCDIR.winfsp" Name="winfsp">
@ -223,9 +252,9 @@
<Component Id="C.winfsp.h">
<File Name="winfsp.h" KeyPath="yes" />
</Component>
<Component Id="C.winfsp.hpp">
<!--Component Id="C.winfsp.hpp">
<File Name="winfsp.hpp" KeyPath="yes" />
</Component>
</Component-->
</Directory>
<Directory Id="INCDIR.fuse" Name="fuse">
<Component Id="C.fuse.h">
@ -272,9 +301,16 @@
</DirectoryRef>
<DirectoryRef Id="OPTDIR">
<Directory Id="OPTDIR.cygfuse" Name="cygfuse" FileSource="..\..\..\opt\cygfuse\dist">
<Component Id="C.fuse.tar.xz">
<File Name="fuse-2.8-4.tar.xz" KeyPath="yes" />
</Component>
<Directory Id="OPTDIR.cygfuse.x64" Name="x64">
<Component Id="C.fuse.tar.xz.x64">
<File Id="FILE.fuse.tar.xz.x64" Name="fuse-2.8-5.tar.xz" KeyPath="yes" />
</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">
<File Name="install.sh" KeyPath="yes" />
</Component>
@ -295,6 +331,11 @@
<File Name="memfs-main.c" KeyPath="yes" />
</Component>
</Directory>
<Directory Id="SMPDIR.memfs_dotnet" Name="memfs-dotnet">
<Component Id="C.memfs_dotnet.Program.cs">
<File Id="FILE.memfs_dotnet.Program.cs" Name="Program.cs" KeyPath="yes" />
</Component>
</Directory>
<Directory Id="SMPDIR.passthrough" Name="passthrough">
<Component Id="C.passthrough.c">
<File Name="passthrough.c" KeyPath="yes" />
@ -309,7 +350,7 @@
<File Name="passthrough.vcxproj.filters" KeyPath="yes" />
</Component>
</Directory>
<Directory Id="SMPDIR.passthrough_cpp" Name="passthrough-cpp">
<!--Directory Id="SMPDIR.passthrough_cpp" Name="passthrough-cpp">
<Component Id="C.passthrough_cpp.cpp">
<File Name="passthrough-cpp.cpp" KeyPath="yes" />
</Component>
@ -322,7 +363,7 @@
<Component Id="C.passthrough_cpp.vcxproj.filters">
<File Name="passthrough-cpp.vcxproj.filters" KeyPath="yes" />
</Component>
</Directory>
</Directory-->
<Directory Id="SMPDIR.passthrough_fuse" Name="passthrough-fuse">
<Component Id="C.passthrough_fuse.c">
<File Name="passthrough-fuse.c" KeyPath="yes" />
@ -351,7 +392,7 @@
</Directory>
<Directory Id="SMPDIR.passthrough_dotnet" Name="passthrough-dotnet">
<Component Id="C.passthrough_dotnet.Program.cs">
<File Name="Program.cs" KeyPath="yes" />
<File Id="FILE.passthrough_dotnet.Program.cs" Name="Program.cs" KeyPath="yes" />
</Component>
<Component Id="C.passthrough_dotnet.sln">
<File Name="passthrough-dotnet.sln" KeyPath="yes" />
@ -413,7 +454,7 @@
<ComponentGroup Id="C.WinFsp.inc">
<ComponentRef Id="C.fsctl.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_common.h" />
<ComponentRef Id="C.fuse_opt.h" />
@ -426,7 +467,8 @@
<ComponentRef Id="C.fuse_x86.pc" />
</ComponentGroup>
<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.uninstall.sh" />
</ComponentGroup>
@ -440,10 +482,10 @@
<ComponentRef Id="C.passthrough.sln" />
<ComponentRef Id="C.passthrough.vcxproj" />
<ComponentRef Id="C.passthrough.vcxproj.filters" />
<ComponentRef Id="C.passthrough_cpp.cpp" />
<ComponentRef Id="C.passthrough_cpp.sln" />
<ComponentRef Id="C.passthrough_cpp.vcxproj" />
<ComponentRef Id="C.passthrough_cpp.vcxproj.filters" />
<!--ComponentRef Id="C.passthrough_cpp.cpp" /-->
<!--ComponentRef Id="C.passthrough_cpp.sln" /-->
<!--ComponentRef Id="C.passthrough_cpp.vcxproj" /-->
<!--ComponentRef Id="C.passthrough_cpp.vcxproj.filters" /-->
<ComponentRef Id="C.passthrough_fuse.c" />
<ComponentRef Id="C.passthrough_fuse.winposix.c" />
<ComponentRef Id="C.passthrough_fuse.winposix.h" />
@ -467,12 +509,15 @@
</ComponentGroup>
<ComponentGroup Id="C.WinFsp.net">
<ComponentRef Id="C.winfsp_msil.dll" />
<ComponentRef Id="C.winfsp_msil.xml" />
<!--
<ComponentRef Id="C.winfsp_msil.dll.GAC" />
<ComponentRef Id="C.policy.winfsp_msil.dll.GAC" />
-->
</ComponentGroup>
<ComponentGroup Id="C.WinFsp.smp.net">
<ComponentRef Id="C.memfs_dotnet_msil.exe" />
<ComponentRef Id="C.memfs_dotnet.Program.cs" />
<ComponentRef Id="C.passthrough_dotnet.Program.cs" />
<ComponentRef Id="C.passthrough_dotnet.sln" />
<ComponentRef Id="C.passthrough_dotnet.csproj" />
@ -531,7 +576,7 @@
Id="F.Cygfuse"
Level="1000"
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 &lt;InstallDir&gt;/opt/cygfuse and run install.sh."
AllowAdvertise="no"
InstallDefault="local"
Absent="allow">

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4920E350-D496-4652-AE98-6C4208AEC1D8}</ProjectGuid>
<OutputType>Exe</OutputType>
<ProjectName>memfs-dotnet</ProjectName>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>memfs</RootNamespace>
<AssemblyName>memfs-dotnet-msil</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
<BaseIntermediateOutputPath>$(SolutionDir)build\$(ProjectName).build\</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
<BaseIntermediateOutputPath>$(SolutionDir)build\$(ProjectName).build\</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\tst\memfs-dotnet\Program.cs">
<Link>Program.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\dotnet\winfsp.net.csproj">
<Project>{94580219-cc8d-4fe5-a3be-437b0b3481e1}</Project>
<Name>winfsp.net</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -17,8 +17,8 @@
<MyCanonicalVersion>1.1</MyCanonicalVersion>
<MyProductVersion>2017.1 BETA</MyProductVersion>
<MyProductStage>Beta</MyProductStage>
<MyProductVersion>2017.1</MyProductVersion>
<MyProductStage>Gold</MyProductStage>
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
<MyVersionWithCommas>$(MyVersion.Replace('.',',')),0</MyVersionWithCommas>

View File

@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winfsp.net", "dotnet\winfsp
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dotnet", "dotnet", "{A998CEC4-4B34-43DC-8457-F7761228BA67}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "memfs-dotnet", "testing\memfs-dotnet.csproj", "{4920E350-D496-4652-AE98-6C4208AEC1D8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -259,6 +261,30 @@ Global
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x64.Build.0 = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x86.ActiveCfg = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x86.Build.0 = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x64.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x64.Build.0 = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x86.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x86.Build.0 = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|Any CPU.Build.0 = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x64.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x64.Build.0 = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x86.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x86.Build.0 = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|Any CPU.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|Any CPU.Build.0 = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x64.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x64.Build.0 = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x86.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x86.Build.0 = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|Any CPU.Build.0 = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x64.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x64.Build.0 = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -273,5 +299,6 @@ Global
{10757011-749D-4954-873B-AE38D8145472} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49}
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49}
{94580219-CC8D-4FE5-A3BE-437B0B3481E1} = {A998CEC4-4B34-43DC-8457-F7761228BA67}
{4920E350-D496-4652-AE98-6C4208AEC1D8} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49}
EndGlobalSection
EndGlobal

View File

@ -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
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]::
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.

View File

@ -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 [[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 [[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.

View File

@ -5,12 +5,15 @@ This document contains a list of known file systems and file system libraries th
== File Systems
- 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/netheril96/securefs[securefs] - Filesystem in userspace (FUSE) with transparent authenticated encryption
- https://github.com/billziss-gh/sshfs-win[sshfs-win] - SSHFS for Windows
== File System Libraries
- https://github.com/billziss-gh/cgofuse[cgofuse] - Cross-platform FUSE library for Go
- https://github.com/DuroSoft/fuse-bindings[fuse-bindings] - Fully maintained FUSE bindings for Node that aims to cover the entire FUSE api
- https://github.com/ui4j/fuse-jna[fuse-jna] - No-nonsense, actually-working Java bindings to FUSE using JNA
- https://github.com/billziss-gh/fusepy[fusepy] - Simple ctypes bindings for FUSE

105
doc/Queued-Events.asciidoc Normal file
View 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!

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -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)"
"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.
@ -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.
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.

View File

@ -118,6 +118,8 @@ FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop_mt)(struct fsp_fuse_env *env,
struct fuse *f);
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_exit)(struct fsp_fuse_env *env,
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_SYM(
@ -171,6 +173,13 @@ void fuse_exit(struct fuse *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(
struct fuse_context *fuse_get_context(void),
{

View File

@ -178,6 +178,7 @@ struct fuse_flock
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
0/*conv_to_win_path*/, \
{ 0 }, \
}
#else
#define FSP_FUSE_ENV_INIT \
@ -187,6 +188,7 @@ struct fuse_flock
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
0/*conv_to_win_path*/, \
{ 0 }, \
}
#endif
@ -229,6 +231,7 @@ struct fuse_flock
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
fsp_fuse_conv_to_win_path, \
{ 0 }, \
}
/*

View File

@ -18,3 +18,11 @@ cygport:
> 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
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

View File

@ -17,22 +17,39 @@
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/cygwin.h>
static void *cygfuse_init_slow(int force);
static void *cygfuse_init_winfsp();
static void *cygfuse_init_fail();
static pthread_mutex_t cygfuse_mutex = PTHREAD_MUTEX_INITIALIZER;
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);
if (force || 0 == cygfuse_handle)
cygfuse_handle = cygfuse_init_winfsp();
handle = cygfuse_handle;
if (force || 0 == handle)
{
handle = cygfuse_init_winfsp();
__sync_synchronize(); /* memory barrier */
cygfuse_handle = handle;
}
pthread_mutex_unlock(&cygfuse_mutex);
return handle;
}
/*
@ -50,7 +67,7 @@ static inline int cygfuse_daemon(int nochdir, int noclose)
return -1;
/* force reload of WinFsp DLL to workaround fork() problems */
cygfuse_init(1);
cygfuse_init_slow(1);
return 0;
}
@ -58,7 +75,7 @@ static inline int cygfuse_daemon(int nochdir, int noclose)
#define FSP_FUSE_API static
#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__ }
#include <fuse_common.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)))\
return cygfuse_init_fail();
static void *cygfuse_init_fail();
static void *cygfuse_init_winfsp()
{
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_mt);
CYGFUSE_GET_API(h, fsp_fuse_exit);
CYGFUSE_GET_API(h, fsp_fuse_exited);
CYGFUSE_GET_API(h, fsp_fuse_get_context);
/* fuse_opt.h */
@ -141,6 +160,7 @@ static void *cygfuse_init_winfsp()
static void *cygfuse_init_fail()
{
abort();
fprintf(stderr, "cygfuse: initialization failed: " CYGFUSE_WINFSP_NAME " not found\n");
exit(1);
return 0;
}

Binary file not shown.

View File

@ -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.

View File

@ -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

Binary file not shown.

BIN
opt/cygfuse/dist/x86/fuse-2.8-5.tar.xz vendored Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
NAME="fuse"
VERSION=2.8
RELEASE=4
RELEASE=5
CATEGORY="Utils"
SUMMARY="WinFsp-FUSE compatibility layer"
DESCRIPTION="WinFsp-FUSE enables FUSE file systems to be run on Cygwin."

View File

@ -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("FileInfoTimeout=", set_FileInfoTimeout, 1),
FSP_FUSE_CORE_OPT("FileInfoTimeout=%d", VolumeParams.FileInfoTimeout, 0),
FUSE_OPT_KEY("UNC=", 'U'),
FUSE_OPT_KEY("--UNC=", 'U'),
FUSE_OPT_KEY("VolumePrefix=", 'U'),
FUSE_OPT_KEY("--VolumePrefix=", 'U'),
FUSE_OPT_KEY("FileSystemName=", 'F'),
FUSE_OPT_KEY("--FileSystemName=", 'F'),
FUSE_OPT_END,
@ -457,17 +460,27 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
default:
return 1;
case 'h':
/* Note: The limit on FspServiceLog messages is 1024 bytes. This is getting close. */
FspServiceLog(EVENTLOG_ERROR_TYPE, L""
FSP_FUSE_LIBRARY_NAME " options:\n"
" -o DebugLog=FILE debug log file (deflt: stderr)\n"
" -o SectorSize=N sector size for Windows (512-4096, deflt: 4096)\n"
" -o SectorsPerAllocationUnit=N sectors per allocation unit (deflt: 1)\n"
" -o MaxComponentLength=N max file name component length (deflt: 255)\n"
" -o VolumeCreationTime=T volume creation time (FILETIME hex format)\n"
" -o VolumeSerialNumber=N 32-bit wide\n"
" -o FileInfoTimeout=N FileInfo/Security/VolumeInfo timeout (millisec)\n"
" --UNC=U --VolumePrefix=U UNC prefix (\\Server\\Share)\n"
" --FileSystemName=FSN Name of user mode file system\n");
" -o umask=MASK set file permissions (octal)\n"
" -o uid=N set file owner (-1 for mounting user id)\n"
" -o gid=N set file group (-1 for mounting user group)\n"
" -o rellinks interpret absolute symlinks as volume relative\n"
" -o volname=NAME set volume label\n"
" -o VolumePrefix=UNC set UNC prefix (/Server/Share)\n"
" --VolumePrefix=UNC set UNC prefix (\\Server\\Share)\n"
" -o FileSystemName=NAME set file system name\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;
return 1;
case 'V':
@ -488,8 +501,12 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
0);
return 0;
case 'U':
if ('U' == arg[2])
if ('U' == arg[0])
arg += sizeof "UNC=" - 1;
else if ('U' == arg[2])
arg += sizeof "--UNC=" - 1;
else if ('V' == arg[0])
arg += sizeof "VolumePrefix=" - 1;
else if ('V' == arg[2])
arg += sizeof "--VolumePrefix=" - 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;
opt_data->VolumeParams.Prefix
[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;
case 'F':
if ('f' == arg[0])
arg += sizeof "fstypename=" - 1;
else if ('F' == arg[0])
arg += sizeof "FileSystemName=" - 1;
else if ('F' == arg[2])
arg += sizeof "--FileSystemName=" - 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)
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)

View File

@ -50,6 +50,7 @@ struct fuse
PWSTR MountPoint;
FSP_FILE_SYSTEM *FileSystem;
FSP_SERVICE *Service; /* weak */
volatile int exited;
};
struct fsp_fuse_context_header

View File

@ -1,7 +1,7 @@
/**
* @file dotnet/FileSystemBase+Const.cs
/*
* dotnet/FileSystemBase+Const.cs
*
* @copyright 2015-2017 Bill Zissimopoulos
* Copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.

View File

@ -1,7 +1,7 @@
/**
* @file dotnet/FileSystemBase.cs
/*
* dotnet/FileSystemBase.cs
*
* @copyright 2015-2017 Bill Zissimopoulos
* Copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
@ -24,6 +24,9 @@ using Fsp.Interop;
namespace Fsp
{
/// <summary>
/// Provides the base class that user mode file systems must inherit from.
/// </summary>
public partial class FileSystemBase
{
/* types */
@ -48,27 +51,73 @@ namespace Fsp
}
/* operations */
/// <summary>
/// Provides a means to customize the returned status code when an exception happens.
/// </summary>
/// <param name="ex"></param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 ExceptionHandler(Exception ex)
{
Api.FspDebugLog("%s\n", ex.ToString());
return STATUS_UNEXPECTED_IO_ERROR;
}
/// <summary>
/// Occurs just before the file system is mounted.
/// File systems may override this method to configure the file system host.
/// </summary>
/// <param name="Host">
/// The file system host that is mounting this file system.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Init(Object Host)
{
return STATUS_SUCCESS;
}
/// <summary>
/// Occurs just after the file system is mounted,
/// but prior to receiving any file system operation.
/// </summary>
/// <param name="Host">
/// The file system host that is mounting this file system.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Mounted(Object Host)
{
return STATUS_SUCCESS;
}
/// <summary>
/// Occurs just after the file system is unmounted.
/// No other file system operations will be received on this file system.
/// </summary>
/// <param name="Host">
/// The file system host that is mounting this file system.
/// </param>
public virtual void Unmounted(Object Host)
{
}
/// <summary>
/// Gets the volume information.
/// </summary>
/// <param name="VolumeInfo">
/// Receives the volume information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 GetVolumeInfo(
out VolumeInfo VolumeInfo)
{
VolumeInfo = default(VolumeInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Sets the volume label.
/// </summary>
/// <param name="VolumeLabel">
/// The new label for the volume.
/// </param>
/// <param name="VolumeInfo">
/// Receives the updated volume information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 SetVolumeLabel(
String VolumeLabel,
out VolumeInfo VolumeInfo)
@ -76,6 +125,27 @@ namespace Fsp
VolumeInfo = default(VolumeInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Gets file or directory attributes and security descriptor given a file name.
/// </summary>
/// <param name="FileName">
/// The name of the file or directory to get the attributes and security descriptor for.
/// </param>
/// <param name="FileAttributes">
/// Receives the file attributes on successful return.
/// If this call returns STATUS_REPARSE, the file system may place here the index of the
/// first reparse point within FileName.
/// </param>
/// <param name="SecurityDescriptor">
/// Receives the file security descriptor. If the SecurityDescriptor parameter is null
/// on input the file system should not fill this value.
/// </param>
/// <returns>
/// STATUS_SUCCESS, STATUS_REPARSE or error code.
/// STATUS_REPARSE should be returned by file systems that support reparse points when
/// they encounter a FileName that contains reparse points anywhere but the final path
/// component.
/// </returns>
public virtual Int32 GetSecurityByName(
String FileName,
out UInt32 FileAttributes/* or ReparsePointIndex */,
@ -84,6 +154,40 @@ namespace Fsp
FileAttributes = default(UInt32);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Creates a new file or directory.
/// </summary>
/// <param name="FileName">
/// The name of the file or directory to be created.
/// </param>
/// <param name="CreateOptions">
/// Create options for this request.
/// </param>
/// <param name="GrantedAccess">
/// Determines the specific access rights that have been granted for this request.
/// </param>
/// <param name="FileAttributes">
/// File attributes to apply to the newly created file or directory.
/// </param>
/// <param name="SecurityDescriptor">
/// Security descriptor to apply to the newly created file or directory.
/// </param>
/// <param name="AllocationSize">
/// Allocation size for the newly created file.
/// </param>
/// <param name="FileNode">
/// Receives the file node for the newly created file.
/// </param>
/// <param name="FileDesc">
/// Receives the file descriptor for the newly created file.
/// </param>
/// <param name="FileInfo">
/// Receives the file information for the newly created file.
/// </param>
/// <param name="NormalizedName">
/// Receives the normalized name for the newly created file.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Create(
String FileName,
UInt32 CreateOptions,
@ -102,6 +206,31 @@ namespace Fsp
NormalizedName = default(String);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Opens a file or directory.
/// </summary>
/// <param name="FileName">
/// The name of the file or directory to be opened.
/// </param>
/// <param name="CreateOptions">
/// Create options for this request.
/// </param>
/// <param name="GrantedAccess">
/// Determines the specific access rights that have been granted for this request.
/// </param>
/// <param name="FileNode">
/// Receives the file node for the newly opened file.
/// </param>
/// <param name="FileDesc">
/// Receives the file descriptor for the newly opened file.
/// </param>
/// <param name="FileInfo">
/// Receives the file information for the newly opened file.
/// </param>
/// <param name="NormalizedName">
/// Receives the normalized name for the newly opened file.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Open(
String FileName,
UInt32 CreateOptions,
@ -117,6 +246,29 @@ namespace Fsp
NormalizedName = default(String);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Overwrites a file.
/// </summary>
/// <param name="FileNode">
/// The file node for the file to be overwritten.
/// </param>
/// <param name="FileDesc">
/// The file descriptor for the file to be overwritten.
/// </param>
/// <param name="FileAttributes">
/// File attributes to apply to the overwritten file.
/// </param>
/// <param name="ReplaceFileAttributes">
/// When true the existing file attributes should be replaced with the new ones.
/// When false the existing file attributes should be merged (or'ed) with the new ones.
/// </param>
/// <param name="AllocationSize">
/// Allocation size for the overwritten file.
/// </param>
/// <param name="FileInfo">
/// Receives the updated file information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Overwrite(
Object FileNode,
Object FileDesc,
@ -128,6 +280,64 @@ namespace Fsp
FileInfo = default(FileInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Cleans up a file or directory.
/// </summary>
/// <remarks>
/// <para>
/// When CreateFile is used to open or create a file the kernel creates a kernel mode file
/// object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
/// be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
/// file object. When all handles for a particular file object get closed (using CloseHandle)
/// the system sends a Cleanup request to the file system.
/// </para><para>
/// There will be a Cleanup operation for every Create or Open operation posted to the user mode
/// file system. However the Cleanup operation is not the final close operation on a file.
/// The file system must be ready to receive additional operations until close time. This is true
/// even when the file is being deleted!
/// </para><para>
/// The Flags parameter contains information about the cleanup operation:
/// <list>
/// <item>CleanupDelete -
/// An important function of the Cleanup operation is to complete a delete operation. Deleting
/// a file or directory in Windows is a three-stage process where the file is first opened, then
/// tested to see if the delete can proceed and if the answer is positive the file is then
/// deleted during Cleanup.
/// When this flag is set, this is the last outstanding cleanup for this particular file node.
/// </item>
/// <item>CleanupSetAllocationSize -
/// The NTFS and FAT file systems reset a file's allocation size when they receive the last
/// outstanding cleanup for a particular file node. User mode file systems that implement
/// allocation size and wish to duplicate the NTFS and FAT behavior can use this flag.
/// </item>
/// <item>CleanupSetArchiveBit -
/// File systems that support the archive bit should set the file node's archive bit when this
/// flag is set.
/// </item>
/// <item>CleanupSetLastAccessTime, CleanupSetLastWriteTime, CleanupSetChangeTime -
/// File systems should set the corresponding file time when each one of these flags is set.
/// Note that updating the last access time is expensive and a file system may choose to not
/// implement it.
/// </item>
/// </list>
/// </para><para>
/// There is no way to report failure of this operation. This is a Windows limitation.
/// </para>
/// </remarks>
/// <param name="FileNode">
/// The file node of the file or directory to cleanup.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file or directory to cleanup.
/// </param>
/// <param name="FileName">
/// The name of the file or directory to cleanup. Sent only when a Delete is requested.
/// </param>
/// <param name="Flags">
/// These flags determine whether the file was modified and whether to delete the file.
/// </param>
/// <seealso cref="CanDelete"/>
/// <seealso cref="Close"/>
public virtual void Cleanup(
Object FileNode,
Object FileDesc,
@ -135,11 +345,42 @@ namespace Fsp
UInt32 Flags)
{
}
/// <summary>
/// Closes a file or directory.
/// </summary>
/// <param name="FileNode">
/// The file node of the file or directory to close.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file or directory to close.
/// </param>
public virtual void Close(
Object FileNode,
Object FileDesc)
{
}
/// <summary>
/// Reads a file.
/// </summary>
/// <param name="FileNode">
/// The file node of the file to read.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file to read.
/// </param>
/// <param name="Buffer">
/// Pointer to a buffer that receives the results of the read operation.
/// </param>
/// <param name="Offset">
/// Offset within the file to read from.
/// </param>
/// <param name="Length">
/// Length of data to read.
/// </param>
/// <param name="BytesTransferred">
/// Receives the actual number of bytes read.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Read(
Object FileNode,
Object FileDesc,
@ -151,6 +392,38 @@ namespace Fsp
BytesTransferred = default(UInt32);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Writes a file.
/// </summary>
/// <param name="FileNode">
/// The file node of the file to write.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file to write.
/// </param>
/// <param name="Buffer">
/// Pointer to a buffer that receives the results of the write operation.
/// </param>
/// <param name="Offset">
/// Offset within the file to write to.
/// </param>
/// <param name="Length">
/// Length of data to write.
/// </param>
/// <param name="WriteToEndOfFile">
/// When true the file system must write to the current end of file. In this case the Offset
/// parameter will contain the value -1.
/// </param>
/// <param name="ConstrainedIo">
/// When true the file system must not extend the file (i.e. change the file size).
/// </param>
/// <param name="BytesTransferred">
/// Receives the actual number of bytes written.
/// </param>
/// <param name="FileInfo">
/// Receives the updated file information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Write(
Object FileNode,
Object FileDesc,
@ -166,6 +439,24 @@ namespace Fsp
FileInfo = default(FileInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Flushes a file or volume.
/// </summary>
/// <remarks>
/// Note that the FSD will also flush all file/volume caches prior to invoking this operation.
/// </remarks>
/// <param name="FileNode">
/// The file node of the file to flush.
/// When this and the FileDesc parameter are null the whole volume is being flushed.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file to flush.
/// When this and the FileNode parameter are null the whole volume is being flushed.
/// </param>
/// <param name="FileInfo">
/// Receives the updated file information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Flush(
Object FileNode,
Object FileDesc,
@ -174,6 +465,19 @@ namespace Fsp
FileInfo = default(FileInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Gets file or directory information.
/// </summary>
/// <param name="FileNode">
/// The file node of the file to get information for.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file to get information for.
/// </param>
/// <param name="FileInfo">
/// Receives the file information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 GetFileInfo(
Object FileNode,
Object FileDesc,
@ -182,6 +486,39 @@ namespace Fsp
FileInfo = default(FileInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Sets file or directory basic information.
/// </summary>
/// <param name="FileNode">
/// The file node of the file to set information for.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file to set information for.
/// </param>
/// <param name="FileAttributes">
/// File attributes to apply to the file or directory.
/// If the value -1 is sent, the file attributes should not be changed.
/// </param>
/// <param name="CreationTime">
/// Creation time to apply to the file or directory.
/// If the value 0 is sent, the creation time should not be changed.
/// </param>
/// <param name="LastAccessTime">
/// Last access time to apply to the file or directory.
/// If the value 0 is sent, the last access time should not be changed.
/// </param>
/// <param name="LastWriteTime">
/// Last write time to apply to the file or directory.
/// If the value 0 is sent, the last write time should not be changed.
/// </param>
/// <param name="ChangeTime">
/// Change time to apply to the file or directory.
/// If the value 0 is sent, the change time should not be changed.
/// </param>
/// <param name="FileInfo">
/// Receives the updated file information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 SetBasicInfo(
Object FileNode,
Object FileDesc,
@ -195,6 +532,52 @@ namespace Fsp
FileInfo = default(FileInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Sets file/allocation size.
/// </summary>
/// <remarks>
/// <para>
/// This function is used to change a file's sizes. Windows file systems maintain two kinds
/// of sizes: the file size is where the End Of File (EOF) is, and the allocation size is the
/// actual size that a file takes up on the "disk".
/// </para><para>
/// The rules regarding file/allocation size are:
/// <list>
/// <item>
/// Allocation size must always be aligned to the allocation unit boundary. The allocation
/// unit is the product SectorSize * SectorsPerAllocationUnit. The FSD will always send
/// properly aligned allocation sizes when setting the allocation size.
/// </item>
/// <item>
/// Allocation size is always greater or equal to the file size.
/// </item>
/// <item>
/// A file size of more than the current allocation size will also extend the allocation
/// size to the next allocation unit boundary.
/// </item>
/// <item>
/// An allocation size of less than the current file size should also truncate the current
/// file size.
/// </item>
/// </list>
/// </para>
/// </remarks>
/// <param name="FileNode">
/// The file node of the file to set the file/allocation size for.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file to set the file/allocation size for.
/// </param>
/// <param name="NewSize">
/// New file/allocation size to apply to the file.
/// </param>
/// <param name="SetAllocationSize">
/// If true, then the allocation size is being set. if false, then the file size is being set.
/// </param>
/// <param name="FileInfo">
/// Receives the updated file information.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 SetFileSize(
Object FileNode,
Object FileDesc,
@ -205,6 +588,20 @@ namespace Fsp
FileInfo = default(FileInfo);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Determines whether a file or directory can be deleted.
/// </summary>
/// <param name="FileNode">
/// The file node of the file or directory to test for deletion.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file or directory to test for deletion.
/// </param>
/// <param name="FileName">
/// The name of the file or directory to test for deletion.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
/// <seealso cref="Cleanup"/>
public virtual Int32 CanDelete(
Object FileNode,
Object FileDesc,
@ -212,6 +609,37 @@ namespace Fsp
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Renames a file or directory.
/// </summary>
/// <remarks>
/// The kernel mode FSD provides certain guarantees prior to posting a rename operation:
/// <list>
/// <item>
/// A file cannot be renamed if a file with the same name exists and has open handles.
/// </item>
/// <item>
/// A directory cannot be renamed if it or any of its subdirectories contains a file that
/// has open handles.
/// </item>
/// </list>
/// </remarks>
/// <param name="FileNode">
/// The file node of the file or directory to be renamed.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file or directory to be renamed.
/// </param>
/// <param name="FileName">
/// The current name of the file or directory to rename.
/// </param>
/// <param name="NewFileName">
/// The new name for the file or directory.
/// </param>
/// <param name="ReplaceIfExists">
/// Whether to replace a file that already exists at NewFileName.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 Rename(
Object FileNode,
Object FileDesc,
@ -221,6 +649,19 @@ namespace Fsp
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Gets file or directory security descriptor.
/// </summary>
/// <param name="FileNode">
/// The file node of the file or directory to get the security descriptor for.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file or directory to get the security descriptor for.
/// </param>
/// <param name="SecurityDescriptor">
/// Receives the file security descriptor.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 GetSecurity(
Object FileNode,
Object FileDesc,
@ -228,6 +669,23 @@ namespace Fsp
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Sets file or directory security descriptor.
/// </summary>
/// <param name="FileNode">
/// The file node of the file or directory to set the security descriptor for.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file or directory to set the security descriptor for.
/// </param>
/// <param name="Sections">
/// Describes what parts of the file or directory security descriptor should be modified.
/// </param>
/// <param name="SecurityDescriptor">
/// Describes the modifications to apply to the file or directory security descriptor.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
/// <seealso cref="ModifySecurityDescriptor"/>
public virtual Int32 SetSecurity(
Object FileNode,
Object FileDesc,
@ -236,6 +694,10 @@ namespace Fsp
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Reads a directory.
/// </summary>
/// <seealso cref="ReadDirectoryEntry"/>
public virtual Int32 ReadDirectory(
Object FileNode,
Object FileDesc,
@ -248,6 +710,36 @@ namespace Fsp
return SeekableReadDirectory(FileNode, FileDesc, Pattern, Marker, Buffer, Length,
out BytesTransferred);
}
/// <summary>
/// Reads a directory entry.
/// </summary>
/// <param name="FileNode">
/// The file node of the directory to be read.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the directory to be read.
/// </param>
/// <param name="Pattern">
/// The pattern to match against files in this directory. Can be null. The file system
/// can choose to ignore this parameter as the FSD will always perform its own pattern
/// matching on the returned results.
/// </param>
/// <param name="Marker">
/// A file name that marks where in the directory to start reading. Files with names
/// that are greater than (not equal to) this marker (in the directory order determined
/// by the file system) should be returned. Can be null.
/// </param>
/// <param name="Context">
/// Can be used by the file system to track the ReadDirectory operation.
/// </param>
/// <param name="FileName">
/// Receives the file name for the directory entry.
/// </param>
/// <param name="FileInfo">
/// Receives the file information for the directory entry.
/// </param>
/// <returns>True if there are additional directory entries to return. False otherwise.</returns>
/// <seealso cref="ReadDirectory"/>
public virtual Boolean ReadDirectoryEntry(
Object FileNode,
Object FileDesc,
@ -261,13 +753,16 @@ namespace Fsp
FileInfo = default(FileInfo);
return false;
}
/// <summary>
/// Resolves reparse points.
/// </summary>
public virtual Int32 ResolveReparsePoints(
String FileName,
UInt32 ReparsePointIndex,
Boolean ResolveLastPathComponent,
out IoStatusBlock IoStatus,
IntPtr Buffer,
ref UIntPtr Size)
IntPtr PSize)
{
GCHandle Handle = GCHandle.Alloc(this, GCHandleType.Normal);
try
@ -281,49 +776,108 @@ namespace Fsp
ResolveLastPathComponent,
out IoStatus,
Buffer,
ref Size);
PSize);
}
finally
{
Handle.Free();
}
}
/// <summary>
/// Gets a reparse point given a file name.
/// </summary>
/// <param name="FileName">
/// The name of the file or directory to get the reparse point for.
/// </param>
/// <param name="IsDirectory">
/// Determines whether the passed file name is assumed to be a directory.
/// </param>
/// <param name="ReparseData">
/// Receives the reparse data for the file or directory.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 GetReparsePointByName(
String FileName,
Boolean IsDirectory,
IntPtr Buffer,
ref UIntPtr Size)
ref Byte[] ReparseData)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Gets a reparse point.
/// </summary>
/// <param name="FileNode">
/// The file node of the reparse point.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the reparse point.
/// </param>
/// <param name="FileName">
/// The file name of the reparse point.
/// </param>
/// <param name="ReparseData">
/// Receives the reparse data for the reparse point.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 GetReparsePoint(
Object FileNode,
Object FileDesc,
String FileName,
IntPtr Buffer,
out UIntPtr Size)
ref Byte[] ReparseData)
{
Size = default(UIntPtr);
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Sets a reparse point.
/// </summary>
/// <param name="FileNode">
/// The file node of the reparse point.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the reparse point.
/// </param>
/// <param name="FileName">
/// The file name of the reparse point.
/// </param>
/// <param name="ReparseData">
/// The new reparse data for the reparse point.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 SetReparsePoint(
Object FileNode,
Object FileDesc,
String FileName,
IntPtr Buffer,
UIntPtr Size)
Byte[] ReparseData)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Deletes a reparse point.
/// </summary>
/// <param name="FileNode">
/// The file node of the reparse point.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the reparse point.
/// </param>
/// <param name="FileName">
/// The file name of the reparse point.
/// </param>
/// <param name="ReparseData">
/// The reparse data for the reparse point.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public virtual Int32 DeleteReparsePoint(
Object FileNode,
Object FileDesc,
String FileName,
IntPtr Buffer,
UIntPtr Size)
Byte[] ReparseData)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/// <summary>
/// Gets named streams information.
/// </summary>
public virtual Int32 GetStreamInfo(
Object FileNode,
Object FileDesc,
@ -331,19 +885,108 @@ namespace Fsp
UInt32 Length,
out UInt32 BytesTransferred)
{
Object Context = null;
String StreamName;
StreamInfo StreamInfo = default(StreamInfo);
BytesTransferred = default(UInt32);
return STATUS_INVALID_DEVICE_REQUEST;
while (GetStreamEntry(FileNode, FileDesc, ref Context,
out StreamName, out StreamInfo.StreamSize, out StreamInfo.StreamAllocationSize))
{
StreamInfo.SetStreamNameBuf(StreamName);
if (!Api.FspFileSystemAddStreamInfo(ref StreamInfo, Buffer, Length,
out BytesTransferred))
return STATUS_SUCCESS;
}
Api.FspFileSystemEndStreamInfo(Buffer, Length, out BytesTransferred);
return STATUS_SUCCESS;
}
/// <summary>
/// Gets named streams information entry.
/// </summary>
/// <param name="FileNode">
/// The file node of the file or directory to get stream information for.
/// </param>
/// <param name="FileDesc">
/// The file descriptor of the file or directory to get stream information for.
/// </param>
/// <param name="Context">
/// Can be used by the file system to track the GetStreamInfo operation.
/// </param>
/// <param name="StreamName">
/// Receives the stream name for the stream entry.
/// </param>
/// <param name="StreamSize">
/// Receives the stream size for the stream entry.
/// </param>
/// <param name="StreamAllocationSize">
/// Receives the stream allocation size for the stream entry.
/// </param>
/// <returns>True if there are additional stream entries to return. False otherwise.</returns>
public virtual Boolean GetStreamEntry(
Object FileNode,
Object FileDesc,
ref Object Context,
out String StreamName,
out UInt64 StreamSize,
out UInt64 StreamAllocationSize)
{
StreamName = default(String);
StreamSize = default(UInt64);
StreamAllocationSize = default(UInt64);
return false;
}
/* helpers */
/// <summary>
/// Converts a Win32 error code to a Windows kernel status code.
/// </summary>
public static Int32 NtStatusFromWin32(UInt32 Error)
{
return Api.FspNtStatusFromWin32(Error);
}
/// <summary>
/// Converts a Windows kernel status code to a Win32 error code.
/// </summary>
public static UInt32 Win32FromNtStatus(Int32 Status)
{
return Api.FspWin32FromNtStatus(Status);
}
/// <summary>
/// Modifies a security descriptor.
/// </summary>
/// <remarks>
/// This is a helper for implementing the SetSecurity operation.
/// </remarks>
/// <param name="SecurityDescriptor">
/// The original security descriptor.
/// </param>
/// <param name="Sections">
/// Describes what parts of the file or directory security descriptor should be modified.
/// </param>
/// <param name="ModificationDescriptor">
/// Describes the modifications to apply to the file or directory security descriptor.
/// </param>
/// <returns>The modified security descriptor.</returns>
/// <seealso cref="SetSecurity"/>
public static byte[] ModifySecurityDescriptor(
Byte[] SecurityDescriptor,
AccessControlSections Sections,
Byte[] ModificationDescriptor)
{
UInt32 SecurityInformation = 0;
if (0 != (Sections & AccessControlSections.Owner))
SecurityInformation |= 1/*OWNER_SECURITY_INFORMATION*/;
if (0 != (Sections & AccessControlSections.Group))
SecurityInformation |= 2/*GROUP_SECURITY_INFORMATION*/;
if (0 != (Sections & AccessControlSections.Access))
SecurityInformation |= 4/*DACL_SECURITY_INFORMATION*/;
if (0 != (Sections & AccessControlSections.Audit))
SecurityInformation |= 8/*SACL_SECURITY_INFORMATION*/;
return Api.ModifySecurityDescriptor(
SecurityDescriptor,
SecurityInformation,
ModificationDescriptor);
}
public Int32 SeekableReadDirectory(
Object FileNode,
Object FileDesc,
@ -363,8 +1006,9 @@ namespace Fsp
DirInfo.SetFileNameBuf(FileName);
if (!Api.FspFileSystemAddDirInfo(ref DirInfo, Buffer, Length,
out BytesTransferred))
break;
return STATUS_SUCCESS;
}
Api.FspFileSystemEndDirInfo(Buffer, Length, out BytesTransferred);
return STATUS_SUCCESS;
}
public Int32 BufferedReadDirectory(
@ -408,7 +1052,22 @@ namespace Fsp
Marker, Buffer, Length, out BytesTransferred);
return STATUS_SUCCESS;
}
public Int32 FindReparsePoint(
/// <summary>
/// Finds a reparse point in file name.
/// </summary>
/// <remarks>
/// This is a helper for implementing the GetSecurityByName operation in file systems
/// that support reparse points.
/// </remarks>
/// <param name="FileName">
/// The name of the file or directory.
/// </param>
/// <param name="ReparsePointIndex">
/// Receives the index of the first reparse point within FileName.
/// </param>
/// <returns>True if a reparse point was found, false otherwise.</returns>
/// <seealso cref="GetSecurityByName"/>
public Boolean FindReparsePoint(
String FileName,
out UInt32 ReparsePointIndex)
{
@ -427,22 +1086,62 @@ namespace Fsp
Handle.Free();
}
}
/// <summary>
/// Gets the reparse tag from reparse data.
/// </summary>
/// <param name="ReparseData">
/// The reparse data to extract the reparse tag from.
/// </param>
/// <returns>The reparse tag.</returns>
public static UInt32 GetReparseTag(
Byte[] ReparseData)
{
return BitConverter.ToUInt32(ReparseData, 0);
}
/// <summary>
/// Tests whether reparse data can be replaced.
/// </summary>
/// <remarks>
/// This is a helper for implementing the SetReparsePoint/DeleteReparsePoint operation
/// in file systems that support reparse points.
/// </remarks>
/// <param name="CurrentReparseData">
/// The current reparse data.
/// </param>
/// <param name="ReplaceReparseData">
/// The replacement reparse data.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
/// <seealso cref="SetReparsePoint"/>
/// <seealso cref="DeleteReparsePoint"/>
public static Int32 CanReplaceReparsePoint(
Byte[] CurrentReparseData,
Byte[] ReplaceReparseData)
{
return Api.FspFileSystemCanReplaceReparsePoint(CurrentReparseData, ReplaceReparseData);
}
private static Int32 GetReparsePointByName(
IntPtr FileSystem,
IntPtr Context,
String FileName,
Boolean IsDirectory,
IntPtr Buffer,
ref UIntPtr Size)
IntPtr PSize)
{
FileSystemBase self = (FileSystemBase)GCHandle.FromIntPtr(Context).Target;
try
{
return self.GetReparsePointByName(
Byte[] ReparseData;
Int32 Result;
ReparseData = null;
Result = self.GetReparsePointByName(
FileName,
IsDirectory,
Buffer,
ref Size);
ref ReparseData);
if (0 <= Result)
Result = Api.CopyReparsePoint(ReparseData, Buffer, PSize);
return Result;
}
catch (Exception ex)
{

View File

@ -1,7 +1,7 @@
/**
* @file dotnet/FileSystemHost.cs
/*
* dotnet/FileSystemHost.cs
*
* @copyright 2015-2017 Bill Zissimopoulos
* Copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
@ -24,9 +24,16 @@ using Fsp.Interop;
namespace Fsp
{
/// <summary>
/// Provides a means to host (mount) a file system.
/// </summary>
public class FileSystemHost : IDisposable
{
/* ctor/dtor */
/// <summary>
/// Creates an instance of the FileSystemHost class.
/// </summary>
/// <param name="FileSystem">The file system to host.</param>
public FileSystemHost(FileSystemBase FileSystem)
{
_VolumeParams.Flags = VolumeParams.UmFileContextIsFullContext;
@ -36,6 +43,9 @@ namespace Fsp
{
Dispose(false);
}
/// <summary>
/// Unmounts the file system and releases all associated resources.
/// </summary>
public void Dispose()
{
lock (this)
@ -56,73 +66,114 @@ namespace Fsp
{
ExceptionHandler(_FileSystem, ex);
}
Api.SetUserContext(_FileSystemPtr, null);
Api.DisposeUserContext(_FileSystemPtr);
Api.FspFileSystemDelete(_FileSystemPtr);
_FileSystemPtr = IntPtr.Zero;
}
}
/* properties */
/// <summary>
/// Gets or sets the sector size used by the file system.
/// </summary>
public UInt16 SectorSize
{
get { return _VolumeParams.SectorSize; }
set { _VolumeParams.SectorSize = value; }
}
/// <summary>
/// Gets or sets the sectors per allocation unit used by the file system.
/// </summary>
public UInt16 SectorsPerAllocationUnit
{
get { return _VolumeParams.SectorsPerAllocationUnit; }
set { _VolumeParams.SectorsPerAllocationUnit = value; }
}
/// <summary>
/// Gets or sets the maximum path component length used by the file system.
/// </summary>
public UInt16 MaxComponentLength
{
get { return _VolumeParams.MaxComponentLength; }
set { _VolumeParams.MaxComponentLength = value; }
}
/// <summary>
/// Gets or sets the volume creation time.
/// </summary>
public UInt64 VolumeCreationTime
{
get { return _VolumeParams.VolumeCreationTime; }
set { _VolumeParams.VolumeCreationTime = value; }
}
/// <summary>
/// Gets or sets the volume serial number.
/// </summary>
public UInt32 VolumeSerialNumber
{
get { return _VolumeParams.VolumeSerialNumber; }
set { _VolumeParams.VolumeSerialNumber = value; }
}
/// <summary>
/// Gets or sets the file information timeout.
/// </summary>
public UInt32 FileInfoTimeout
{
get { return _VolumeParams.FileInfoTimeout; }
set { _VolumeParams.FileInfoTimeout = value; }
}
/// <summary>
/// Gets or sets a value that determines whether the file system is case sensitive.
/// </summary>
public Boolean CaseSensitiveSearch
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.CaseSensitiveSearch); }
set { _VolumeParams.Flags |= (value ? VolumeParams.CaseSensitiveSearch : 0); }
}
/// <summary>
/// Gets or sets a value that determines whether a case insensitive file system
/// preserves case in file names.
/// </summary>
public Boolean CasePreservedNames
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.CasePreservedNames); }
set { _VolumeParams.Flags |= (value ? VolumeParams.CasePreservedNames : 0); }
}
/// <summary>
/// Gets or sets a value that determines whether file names support unicode characters.
/// </summary>
public Boolean UnicodeOnDisk
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.UnicodeOnDisk); }
set { _VolumeParams.Flags |= (value ? VolumeParams.UnicodeOnDisk : 0); }
}
/// <summary>
/// Gets or sets a value that determines whether the file system supports ACL security.
/// </summary>
public Boolean PersistentAcls
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.PersistentAcls); }
set { _VolumeParams.Flags |= (value ? VolumeParams.PersistentAcls : 0); }
}
/// <summary>
/// Gets or sets a value that determines whether the file system supports reparse points.
/// </summary>
public Boolean ReparsePoints
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.ReparsePoints); }
set { _VolumeParams.Flags |= (value ? VolumeParams.ReparsePoints : 0); }
}
/// <summary>
/// Gets or sets a value that determines whether the file system allows creation of
/// symbolic links without additional privileges.
/// </summary>
public Boolean ReparsePointsAccessCheck
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.ReparsePointsAccessCheck); }
set { _VolumeParams.Flags |= (value ? VolumeParams.ReparsePointsAccessCheck : 0); }
}
/// <summary>
/// Gets or sets a value that determines whether the file system supports named streams.
/// </summary>
public Boolean NamedStreams
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.NamedStreams); }
@ -138,11 +189,17 @@ namespace Fsp
get { return 0 != (_VolumeParams.Flags & VolumeParams.PassQueryDirectoryPattern); }
set { _VolumeParams.Flags |= (value ? VolumeParams.PassQueryDirectoryPattern : 0); }
}
/// <summary>
/// Gets or sets the prefix for a network file system.
/// </summary>
public String Prefix
{
get { return _VolumeParams.GetPrefix(); }
set { _VolumeParams.SetPrefix(value); }
}
/// <summary>
/// Gets or sets the file system name.
/// </summary>
public String FileSystemName
{
get { return _VolumeParams.GetFileSystemName(); }
@ -150,12 +207,42 @@ namespace Fsp
}
/* control */
/// <summary>
/// Checks whether mounting a file system is possible.
/// </summary>
/// <param name="MountPoint">
/// The mount point for the new file system. A value of null means that
/// the file system should use the next available drive letter counting
/// downwards from Z: as its mount point.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public Int32 Preflight(String MountPoint)
{
return Api.FspFileSystemPreflight(
_VolumeParams.IsPrefixEmpty() ? "WinFsp.Disk" : "WinFsp.Net",
MountPoint);
}
/// <summary>
/// Mounts a file system.
/// </summary>
/// <param name="MountPoint">
/// The mount point for the new file system. A value of null means that
/// the file system should use the next available drive letter counting
/// downwards from Z: as its mount point.
/// </param>
/// <param name="SecurityDescriptor">
/// Security descriptor to use if mounting on (newly created) directory.
/// A value of null means the directory should be created with default
/// security.
/// </param>
/// <param name="Synchronized">
/// If true file system operations are synchronized using an exclusive lock.
/// </param>
/// <param name="DebugLog">
/// A value of 0 disables all debug logging.
/// A value of -1 enables all debug logging.
/// </param>
/// <returns></returns>
public Int32 Mount(String MountPoint,
Byte[] SecurityDescriptor = null,
Boolean Synchronized = false,
@ -210,16 +297,23 @@ namespace Fsp
}
if (0 > Result)
{
Api.SetUserContext(_FileSystemPtr, null);
Api.DisposeUserContext(_FileSystemPtr);
Api.FspFileSystemDelete(_FileSystemPtr);
_FileSystemPtr = IntPtr.Zero;
}
return Result;
}
/// <summary>
/// Unmounts the file system and releases all associated resources.
/// </summary>
public void Unmount()
{
Dispose();
}
/// <summary>
/// Gets the file system mount point.
/// </summary>
/// <returns>The file system mount point.</returns>
public String MountPoint()
{
return IntPtr.Zero != _FileSystemPtr ?
@ -229,17 +323,28 @@ namespace Fsp
{
return _FileSystemPtr;
}
/// <summary>
/// Gets the hosted file system.
/// </summary>
/// <returns>The hosted file system.</returns>
public FileSystemBase FileSystem()
{
return _FileSystem;
}
/// <summary>
/// Sets the debug log file to use when debug logging is enabled.
/// </summary>
/// <param name="FileName">
/// The debug log file name. A value of "-" means standard error output.
/// </param>
/// <returns>STATUS_SUCCESS or error code.</returns>
public static Int32 SetDebugLogFile(String FileName)
{
return Api.SetDebugLogFile(FileName);
}
/* FSP_FILE_SYSTEM_INTERFACE */
private static Byte[] SecurityDescriptorNotNull = new Byte[0];
private static Byte[] ByteBufferNotNull = new Byte[0];
private static Int32 ExceptionHandler(
FileSystemBase FileSystem,
Exception ex)
@ -301,12 +406,12 @@ namespace Fsp
Byte[] SecurityDescriptorBytes = null;
Int32 Result;
if (IntPtr.Zero != PSecurityDescriptorSize)
SecurityDescriptorBytes = SecurityDescriptorNotNull;
SecurityDescriptorBytes = ByteBufferNotNull;
Result = FileSystem.GetSecurityByName(
FileName,
out FileAttributes,
ref SecurityDescriptorBytes);
if (0 <= Result)
if (0 <= Result && 260/*STATUS_REPARSE*/ != Result)
{
if (IntPtr.Zero != PFileAttributes)
Marshal.WriteInt32(PFileAttributes, (Int32)FileAttributes);
@ -457,7 +562,7 @@ namespace Fsp
FileSystem.Close(
FileNode,
FileDesc);
Api.SetFullContext(ref FullContext, null, null);
Api.DisposeFullContext(ref FullContext);
}
catch (Exception ex)
{
@ -680,13 +785,15 @@ namespace Fsp
Byte[] SecurityDescriptorBytes;
Int32 Result;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
SecurityDescriptorBytes = SecurityDescriptorNotNull;
SecurityDescriptorBytes = ByteBufferNotNull;
Result = FileSystem.GetSecurity(
FileNode,
FileDesc,
ref SecurityDescriptorBytes);
return Api.CopySecurityDescriptor(SecurityDescriptorBytes,
SecurityDescriptor, PSecurityDescriptorSize);
if (0 <= Result)
Result = Api.CopySecurityDescriptor(SecurityDescriptorBytes,
SecurityDescriptor, PSecurityDescriptorSize);
return Result;
}
catch (Exception ex)
{
@ -761,7 +868,7 @@ namespace Fsp
Boolean ResolveLastPathComponent,
out IoStatusBlock PIoStatus,
IntPtr Buffer,
ref UIntPtr PSize)
IntPtr PSize)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
@ -772,7 +879,7 @@ namespace Fsp
ResolveLastPathComponent,
out PIoStatus,
Buffer,
ref PSize);
PSize);
}
catch (Exception ex)
{
@ -785,23 +892,27 @@ namespace Fsp
ref FullContext FullContext,
String FileName,
IntPtr Buffer,
out UIntPtr PSize)
IntPtr PSize)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Byte[] ReparseData;
Object FileNode, FileDesc;
Int32 Result;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.GetReparsePoint(
ReparseData = null;
Result = FileSystem.GetReparsePoint(
FileNode,
FileDesc,
FileName,
Buffer,
out PSize);
ref ReparseData);
if (0 <= Result)
Result = Api.CopyReparsePoint(ReparseData, Buffer, PSize);
return Result;
}
catch (Exception ex)
{
PSize = default(UIntPtr);
return ExceptionHandler(FileSystem, ex);
}
}
@ -821,8 +932,7 @@ namespace Fsp
FileNode,
FileDesc,
FileName,
Buffer,
Size);
Api.MakeReparsePoint(Buffer, Size));
}
catch (Exception ex)
{
@ -845,8 +955,7 @@ namespace Fsp
FileNode,
FileDesc,
FileName,
Buffer,
Size);
Api.MakeReparsePoint(Buffer, Size));
}
catch (Exception ex)
{
@ -913,8 +1022,8 @@ namespace Fsp
private static FileSystemInterface _FileSystemInterface;
private static IntPtr _FileSystemInterfacePtr;
private VolumeParams _VolumeParams;
private IntPtr _FileSystemPtr;
private FileSystemBase _FileSystem;
private IntPtr _FileSystemPtr;
}
}

View File

@ -1,7 +1,7 @@
/**
* @file dotnet/Interop.cs
/*
* dotnet/Interop.cs
*
* @copyright 2015-2017 Bill Zissimopoulos
* Copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
@ -102,16 +102,28 @@ namespace Fsp.Interop
}
}
/// <summary>
/// Contains volume information about a file system.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct VolumeInfo
{
internal const int VolumeLabelSize = 32;
/// <summary>
/// Total size of volume in bytes.
/// </summary>
public UInt64 TotalSize;
/// <summary>
/// Free size of volume in bytes.
/// </summary>
public UInt64 FreeSize;
internal UInt16 VolumeLabelLength;
internal unsafe fixed UInt16 VolumeLabel[VolumeLabelSize];
/// <summary>
/// Sets the volume label.
/// </summary>
public unsafe void SetVolumeLabel(String Value)
{
fixed (UInt16 *P = VolumeLabel)
@ -121,23 +133,59 @@ namespace Fsp.Interop
Size = VolumeLabelSize;
for (int I = 0; Size > I; I++)
P[I] = Value[I];
VolumeLabelLength = (UInt16)Size;
VolumeLabelLength = (UInt16)(Size * 2);
}
}
}
/// <summary>
/// Contains metadata information about a file or directory.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct FileInfo
{
/// <summary>
/// The file or directory attributes.
/// </summary>
public UInt32 FileAttributes;
/// <summary>
/// The reparse tag of the file or directory.
/// This value is 0 if the file or directory is not a reparse point.
/// </summary>
public UInt32 ReparseTag;
/// <summary>
/// The allocation size of the file.
/// </summary>
public UInt64 AllocationSize;
/// <summary>
/// The file size of the file (end of file).
/// </summary>
public UInt64 FileSize;
/// <summary>
/// The time that the file or directory was created.
/// </summary>
public UInt64 CreationTime;
/// <summary>
/// The time that the file or directory was last accessed.
/// </summary>
public UInt64 LastAccessTime;
/// <summary>
/// The time that the file or direcotry was last modified.
/// </summary>
public UInt64 LastWriteTime;
/// <summary>
/// The time that the file or directory metadata was last modified.
/// </summary>
public UInt64 ChangeTime;
/// <summary>
/// A unique identifier that is associated with the file or directory.
/// Not all file systems support this value.
/// </summary>
public UInt64 IndexNumber;
/// <summary>
/// The number of hard links.
/// Not currently implemented. Set to 0.
/// </summary>
public UInt32 HardLinks;
}
@ -156,7 +204,7 @@ namespace Fsp.Interop
Size = NormalizedNameSize;
for (int I = 0; Size > I; I++)
P[I] = Value[I];
NormalizedNameSize = (UInt16)Size;
NormalizedNameSize = (UInt16)(Size * 2);
}
}
@ -164,7 +212,8 @@ namespace Fsp.Interop
internal struct DirInfo
{
internal const int FileNameBufSize = 255;
internal static int FileNameBufOffset = (int)Marshal.OffsetOf(typeof(DirInfo), "FileNameBuf");
internal static int FileNameBufOffset =
(int)Marshal.OffsetOf(typeof(DirInfo), "FileNameBuf");
internal UInt16 Size;
internal FileInfo FileInfo;
@ -172,7 +221,7 @@ namespace Fsp.Interop
//internal unsafe fixed UInt16 FileNameBuf[];
internal unsafe fixed UInt16 FileNameBuf[FileNameBufSize];
public unsafe void SetFileNameBuf(String Value)
internal unsafe void SetFileNameBuf(String Value)
{
fixed (UInt16 *P = FileNameBuf)
{
@ -189,10 +238,28 @@ namespace Fsp.Interop
[StructLayout(LayoutKind.Sequential)]
internal struct StreamInfo
{
internal const int StreamNameBufSize = 255;
internal static int StreamNameBufOffset =
(int)Marshal.OffsetOf(typeof(StreamInfo), "StreamNameBuf");
internal UInt16 Size;
internal UInt64 StreamSize;
internal UInt64 StreamAllocationSize;
//internal unsafe fixed UInt16 StreamNameBuf[];
internal unsafe fixed UInt16 StreamNameBuf[StreamNameBufSize];
internal unsafe void SetStreamNameBuf(String Value)
{
fixed (UInt16 *P = StreamNameBuf)
{
int Size = null != Value ? Value.Length : 0;
if (Size > StreamNameBufSize)
Size = StreamNameBufSize;
for (int I = 0; Size > I; I++)
P[I] = Value[I];
this.Size = (UInt16)(StreamNameBufOffset + Size * 2);
}
}
}
[StructLayout(LayoutKind.Sequential)]
@ -354,14 +421,14 @@ namespace Fsp.Interop
[MarshalAs(UnmanagedType.U1)] Boolean ResolveLastPathComponent,
out IoStatusBlock PIoStatus,
IntPtr Buffer,
ref UIntPtr PSize);
IntPtr PSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 GetReparsePoint(
IntPtr FileSystem,
ref FullContext FullContext,
[MarshalAs(UnmanagedType.LPWStr)] String FileName,
IntPtr Buffer,
out UIntPtr PSize);
IntPtr PSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 SetReparsePoint(
IntPtr FileSystem,
@ -471,7 +538,8 @@ namespace Fsp.Interop
UInt32 Length,
out UInt32 PBytesTransferred);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FspFileSystemFindReparsePoint(
[return: MarshalAs(UnmanagedType.U1)]
internal delegate Boolean FspFileSystemFindReparsePoint(
IntPtr FileSystem,
GetReparsePointByName GetReparsePointByName,
IntPtr Context,
@ -487,7 +555,7 @@ namespace Fsp.Interop
[MarshalAs(UnmanagedType.U1)] Boolean ResolveLastPathComponent,
out IoStatusBlock PIoStatus,
IntPtr Buffer,
ref UIntPtr PSize);
IntPtr PSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FspFileSystemCanReplaceReparsePoint(
IntPtr CurrentReparseData,
@ -526,6 +594,16 @@ namespace Fsp.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FspFileSystemDeleteDirectoryBuffer(
ref IntPtr PDirBuffer);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FspSetSecurityDescriptor(
IntPtr InputDescriptor,
UInt32 SecurityInformation,
IntPtr ModificationDescriptor,
out IntPtr PSecurityDescriptor);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FspDeleteSecurityDescriptor(
IntPtr SecurityDescriptor,
IntPtr CreateFunc);
/* Service */
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
@ -575,6 +653,10 @@ namespace Fsp.Interop
internal delegate UInt32 FspWin32FromNtStatus(
Int32 Status);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FspDebugLog(
[MarshalAs(UnmanagedType.LPStr)] String Format,
[MarshalAs(UnmanagedType.LPStr)] String Message);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FspDebugLogSetHandle(
IntPtr Handle);
@ -586,7 +668,7 @@ namespace Fsp.Interop
[MarshalAs(UnmanagedType.LPWStr)] String FileName,
[MarshalAs(UnmanagedType.U1)] Boolean IsDirectory,
IntPtr Buffer,
ref UIntPtr PSize);
IntPtr PSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 ServiceStart(
IntPtr Service,
@ -619,13 +701,16 @@ namespace Fsp.Interop
internal static Proto.FspFileSystemAddDirInfo _FspFileSystemAddDirInfo;
internal static Proto.FspFileSystemFindReparsePoint FspFileSystemFindReparsePoint;
internal static Proto.FspFileSystemResolveReparsePoints FspFileSystemResolveReparsePoints;
internal static Proto.FspFileSystemCanReplaceReparsePoint FspFileSystemCanReplaceReparsePoint;
internal static Proto.FspFileSystemAddStreamInfo FspFileSystemAddStreamInfo;
internal static Proto.FspFileSystemCanReplaceReparsePoint _FspFileSystemCanReplaceReparsePoint;
internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo;
internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer;
internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer;
internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer;
internal static Proto.FspFileSystemReadDirectoryBuffer FspFileSystemReadDirectoryBuffer;
internal static Proto.FspFileSystemDeleteDirectoryBuffer FspFileSystemDeleteDirectoryBuffer;
internal static Proto.FspSetSecurityDescriptor FspSetSecurityDescriptor;
internal static IntPtr _FspSetSecurityDescriptorPtr;
internal static Proto.FspDeleteSecurityDescriptor FspDeleteSecurityDescriptor;
internal static Proto.FspServiceCreate FspServiceCreate;
internal static Proto.FspServiceDelete FspServiceDelete;
internal static Proto.FspServiceAllowConsoleMode FspServiceAllowConsoleMode;
@ -638,6 +723,7 @@ namespace Fsp.Interop
internal static Proto.FspVersion FspVersion;
internal static Proto.FspNtStatusFromWin32 FspNtStatusFromWin32;
internal static Proto.FspWin32FromNtStatus FspWin32FromNtStatus;
internal static Proto.FspDebugLog FspDebugLog;
internal static Proto.FspDebugLogSetHandle FspDebugLogSetHandle;
internal static unsafe Int32 FspFileSystemSetMountPointEx(
@ -662,6 +748,29 @@ namespace Fsp.Interop
fixed (DirInfo *P = &DirInfo)
return _FspFileSystemAddDirInfo((IntPtr)P, Buffer, Length, out PBytesTransferred);
}
internal static unsafe Boolean FspFileSystemEndDirInfo(
IntPtr Buffer,
UInt32 Length,
out UInt32 PBytesTransferred)
{
return _FspFileSystemAddDirInfo(IntPtr.Zero, Buffer, Length, out PBytesTransferred);
}
internal static unsafe Boolean FspFileSystemAddStreamInfo(
ref StreamInfo StreamInfo,
IntPtr Buffer,
UInt32 Length,
out UInt32 PBytesTransferred)
{
fixed (StreamInfo *P = &StreamInfo)
return _FspFileSystemAddStreamInfo((IntPtr)P, Buffer, Length, out PBytesTransferred);
}
internal static unsafe Boolean FspFileSystemEndStreamInfo(
IntPtr Buffer,
UInt32 Length,
out UInt32 PBytesTransferred)
{
return _FspFileSystemAddStreamInfo(IntPtr.Zero, Buffer, Length, out PBytesTransferred);
}
internal unsafe static Object GetUserContext(
IntPtr NativePtr)
@ -673,61 +782,61 @@ namespace Fsp.Interop
IntPtr NativePtr,
Object Obj)
{
if (null != Obj)
Debug.Assert(IntPtr.Zero == *(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr)));
GCHandle Handle = GCHandle.Alloc(Obj, GCHandleType.Weak);
*(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr)) = (IntPtr)Handle;
}
internal unsafe static void DisposeUserContext(
IntPtr NativePtr)
{
IntPtr UserContext = *(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr));
Debug.Assert(IntPtr.Zero != UserContext);
if (IntPtr.Zero != UserContext)
{
Debug.Assert(IntPtr.Zero == *(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr)));
GCHandle Handle = GCHandle.Alloc(Obj, GCHandleType.Weak);
*(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr)) = (IntPtr)Handle;
}
else
{
IntPtr UserContext = *(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr));
if (IntPtr.Zero != UserContext)
{
GCHandle.FromIntPtr(UserContext).Free();
*(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr)) = IntPtr.Zero;
}
GCHandle.FromIntPtr(UserContext).Free();
*(IntPtr *)((Byte *)NativePtr + sizeof(IntPtr)) = IntPtr.Zero;
}
}
private class FullContextHolder
{
public Object FileNode;
public Object FileDesc;
}
internal static void GetFullContext(ref FullContext FullContext,
out Object FileNode, out Object FileDesc)
{
FileNode = 0 != FullContext.UserContext ?
GCHandle.FromIntPtr((IntPtr)FullContext.UserContext).Target : null;
FileDesc = 0 != FullContext.UserContext2 ?
GCHandle.FromIntPtr((IntPtr)FullContext.UserContext2).Target : null;
FullContextHolder Holder = 0 != FullContext.UserContext2 ?
(FullContextHolder)GCHandle.FromIntPtr((IntPtr)FullContext.UserContext2).Target :
null;
if (null != Holder)
{
FileNode = Holder.FileNode;
FileDesc = Holder.FileDesc;
}
else
{
FileNode = null;
FileDesc = null;
}
}
internal static void SetFullContext(ref FullContext FullContext,
Object FileNode, Object FileDesc)
{
if (null != FileNode)
Debug.Assert(0 == FullContext.UserContext && 0 == FullContext.UserContext2);
FullContextHolder Holder = new FullContextHolder();
Holder.FileNode = FileNode;
Holder.FileDesc = FileDesc;
GCHandle Handle = GCHandle.Alloc(Holder, GCHandleType.Normal);
FullContext.UserContext2 = (UInt64)(IntPtr)Handle;
}
internal static void DisposeFullContext(ref FullContext FullContext)
{
Debug.Assert(0 == FullContext.UserContext && 0 != FullContext.UserContext2);
if (0 != FullContext.UserContext2)
{
Debug.Assert(0 == FullContext.UserContext);
GCHandle Handle = GCHandle.Alloc(FileNode, GCHandleType.Normal);
FullContext.UserContext = (UInt64)(IntPtr)Handle;
}
else
{
if (0 != FullContext.UserContext)
{
GCHandle.FromIntPtr((IntPtr)FullContext.UserContext).Free();
FullContext.UserContext = 0;
}
}
if (null != FileDesc)
{
Debug.Assert(0 == FullContext.UserContext2);
GCHandle Handle = GCHandle.Alloc(FileDesc, GCHandleType.Normal);
FullContext.UserContext2 = (UInt64)(IntPtr)Handle;
}
else
{
if (0 != FullContext.UserContext2)
{
GCHandle.FromIntPtr((IntPtr)FullContext.UserContext2).Free();
FullContext.UserContext2 = 0;
}
GCHandle.FromIntPtr((IntPtr)FullContext.UserContext2).Free();
FullContext.UserContext2 = 0;
}
}
@ -768,6 +877,67 @@ namespace Fsp.Interop
else
return null;
}
internal unsafe static byte[] ModifySecurityDescriptor(
Byte[] SecurityDescriptorBytes,
UInt32 SecurityInformation,
Byte[] ModificationDescriptorBytes)
{
fixed (Byte *S = SecurityDescriptorBytes)
fixed (Byte *M = ModificationDescriptorBytes)
{
IntPtr SecurityDescriptor;
Int32 Result = FspSetSecurityDescriptor(
(IntPtr)S, SecurityInformation, (IntPtr)M, out SecurityDescriptor);
if (0 > Result)
return null;
SecurityDescriptorBytes = MakeSecurityDescriptor(SecurityDescriptor);
FspDeleteSecurityDescriptor(SecurityDescriptor, _FspSetSecurityDescriptorPtr);
return SecurityDescriptorBytes;
}
}
internal unsafe static Int32 CopyReparsePoint(
Byte[] ReparseData,
IntPtr Buffer,
IntPtr PSize)
{
if (IntPtr.Zero != Buffer)
{
if (null != ReparseData)
{
if (ReparseData.Length > (int)*(UIntPtr *)PSize)
return unchecked((Int32)0xc0000023)/*STATUS_BUFFER_TOO_SMALL*/;
*(UIntPtr *)PSize = (UIntPtr)ReparseData.Length;
Marshal.Copy(ReparseData, 0, Buffer, ReparseData.Length);
}
else
*(UIntPtr *)PSize = UIntPtr.Zero;
}
return 0/*STATUS_SUCCESS*/;
}
internal static Byte[] MakeReparsePoint(
IntPtr Buffer,
UIntPtr Size)
{
if (IntPtr.Zero != Buffer)
{
Byte[] ReparseData = new Byte[(int)Size];
Marshal.Copy(Buffer, ReparseData, 0, ReparseData.Length);
return ReparseData;
}
else
return null;
}
internal unsafe static Int32 FspFileSystemCanReplaceReparsePoint(
Byte[] CurrentReparseData,
Byte[] ReplaceReparseData)
{
fixed (Byte *C = CurrentReparseData)
fixed (Byte *R = ReplaceReparseData)
return _FspFileSystemCanReplaceReparsePoint(
(IntPtr)C, (UIntPtr)CurrentReparseData.Length,
(IntPtr)R, (UIntPtr)ReplaceReparseData.Length);
}
internal static Int32 SetDebugLogFile(String FileName)
{
@ -812,17 +982,17 @@ namespace Fsp.Interop
}
return Module;
}
private static IntPtr GetEntryPointPtr(IntPtr Module, String Name)
{
IntPtr Proc = GetProcAddress(Module, Name);
if (IntPtr.Zero == Proc)
throw new EntryPointNotFoundException("cannot get entry point " + Name);
return Proc;
}
private static T GetEntryPoint<T>(IntPtr Module)
{
try
{
return (T)(object)Marshal.GetDelegateForFunctionPointer(
GetProcAddress(Module, typeof(T).Name), typeof(T));
}
catch (ArgumentNullException)
{
throw new EntryPointNotFoundException("cannot get entry point " + typeof(T).Name);
}
return (T)(object)Marshal.GetDelegateForFunctionPointer(
GetEntryPointPtr(Module, typeof(T).Name), typeof(T));
}
private static void LoadProto(IntPtr Module)
{
@ -840,13 +1010,16 @@ namespace Fsp.Interop
_FspFileSystemAddDirInfo = GetEntryPoint<Proto.FspFileSystemAddDirInfo>(Module);
FspFileSystemFindReparsePoint = GetEntryPoint<Proto.FspFileSystemFindReparsePoint>(Module);
FspFileSystemResolveReparsePoints = GetEntryPoint<Proto.FspFileSystemResolveReparsePoints>(Module);
FspFileSystemCanReplaceReparsePoint = GetEntryPoint<Proto.FspFileSystemCanReplaceReparsePoint>(Module);
FspFileSystemAddStreamInfo = GetEntryPoint<Proto.FspFileSystemAddStreamInfo>(Module);
_FspFileSystemCanReplaceReparsePoint = GetEntryPoint<Proto.FspFileSystemCanReplaceReparsePoint>(Module);
_FspFileSystemAddStreamInfo = GetEntryPoint<Proto.FspFileSystemAddStreamInfo>(Module);
FspFileSystemAcquireDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemAcquireDirectoryBuffer>(Module);
FspFileSystemFillDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemFillDirectoryBuffer>(Module);
FspFileSystemReleaseDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemReleaseDirectoryBuffer>(Module);
FspFileSystemReadDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemReadDirectoryBuffer>(Module);
FspFileSystemDeleteDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemDeleteDirectoryBuffer>(Module);
FspSetSecurityDescriptor = GetEntryPoint<Proto.FspSetSecurityDescriptor>(Module);
_FspSetSecurityDescriptorPtr = GetEntryPointPtr(Module, "FspSetSecurityDescriptor");
FspDeleteSecurityDescriptor = GetEntryPoint<Proto.FspDeleteSecurityDescriptor>(Module);
FspServiceCreate = GetEntryPoint<Proto.FspServiceCreate>(Module);
FspServiceDelete = GetEntryPoint<Proto.FspServiceDelete>(Module);
FspServiceAllowConsoleMode = GetEntryPoint<Proto.FspServiceAllowConsoleMode>(Module);
@ -859,6 +1032,7 @@ namespace Fsp.Interop
FspVersion = GetEntryPoint<Proto.FspVersion>(Module);
FspNtStatusFromWin32 = GetEntryPoint<Proto.FspNtStatusFromWin32>(Module);
FspWin32FromNtStatus = GetEntryPoint<Proto.FspWin32FromNtStatus>(Module);
FspDebugLog = GetEntryPoint<Proto.FspDebugLog>(Module);
FspDebugLogSetHandle = GetEntryPoint<Proto.FspDebugLogSetHandle>(Module);
}
private static void CheckVersion()

View File

@ -1,7 +1,7 @@
/**
* @file dotnet/Service.cs
/*
* dotnet/Service.cs
*
* @copyright 2015-2017 Bill Zissimopoulos
* Copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
@ -16,13 +16,16 @@
*/
using System;
using System.Runtime.InteropServices;
using Fsp.Interop;
namespace Fsp
{
/// <summary>
/// Provides the base class for a process that can be run as a service,
/// command line application or under the control of the WinFsp launcher.
/// </summary>
public class Service
{
/* const */
@ -31,25 +34,33 @@ namespace Fsp
public const UInt32 EVENTLOG_INFORMATION_TYPE = 0x0004;
/* ctor/dtor */
/// <summary>
/// Creates an instance of the Service class.
/// </summary>
/// <param name="ServiceName">The name of the service.</param>
public Service(String ServiceName)
{
Api.FspServiceCreate(ServiceName, _OnStart, _OnStop, null, out _Service);
if (IntPtr.Zero != _Service)
Api.SetUserContext(_Service, this);
Api.FspServiceCreate(ServiceName, _OnStart, _OnStop, null, out _ServicePtr);
if (IntPtr.Zero != _ServicePtr)
Api.SetUserContext(_ServicePtr, this);
}
~Service()
{
if (IntPtr.Zero != _Service)
if (IntPtr.Zero != _ServicePtr)
{
Api.SetUserContext(_Service, null);
Api.FspServiceDelete(_Service);
Api.DisposeUserContext(_ServicePtr);
Api.FspServiceDelete(_ServicePtr);
}
}
/* control */
/// <summary>
/// Runs a service.
/// </summary>
/// <returns>Service process exit code.</returns>
public int Run()
{
if (IntPtr.Zero == _Service)
if (IntPtr.Zero == _ServicePtr)
{
const Int32 STATUS_INSUFFICIENT_RESOURCES = unchecked((Int32)0xc000009a);
Log(EVENTLOG_ERROR_TYPE,
@ -57,9 +68,9 @@ namespace Fsp
GetType().FullName, STATUS_INSUFFICIENT_RESOURCES));
return (int)Api.FspWin32FromNtStatus(STATUS_INSUFFICIENT_RESOURCES);
}
Api.FspServiceAllowConsoleMode(_Service);
Int32 Result = Api.FspServiceLoop(_Service);
int ExitCode = (int)Api.FspServiceGetExitCode(_Service);
Api.FspServiceAllowConsoleMode(_ServicePtr);
Int32 Result = Api.FspServiceLoop(_ServicePtr);
int ExitCode = (int)Api.FspServiceGetExitCode(_ServicePtr);
if (0 > Result)
{
Log(EVENTLOG_ERROR_TYPE,
@ -69,36 +80,42 @@ namespace Fsp
}
return ExitCode;
}
/// <summary>
/// Stops a running service.
/// </summary>
public void Stop()
{
if (IntPtr.Zero == _Service)
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
Api.FspServiceStop(_Service);
Api.FspServiceStop(_ServicePtr);
}
public void RequestTime(UInt32 Time)
{
if (IntPtr.Zero == _Service)
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
Api.FspServiceRequestTime(_Service, Time);
Api.FspServiceRequestTime(_ServicePtr, Time);
}
/// <summary>
/// Gets or sets the service process exit code.
/// </summary>
public int ExitCode
{
get
{
if (IntPtr.Zero == _Service)
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
return (int)Api.FspServiceGetExitCode(_Service);
return (int)Api.FspServiceGetExitCode(_ServicePtr);
}
set
{
if (IntPtr.Zero == _Service)
if (IntPtr.Zero == _ServicePtr)
throw new InvalidOperationException();
Api.FspServiceSetExitCode(_Service, (UInt32)value);
Api.FspServiceSetExitCode(_ServicePtr, (UInt32)value);
}
}
public IntPtr ServiceHandle
{
get { return _Service; }
get { return _ServicePtr; }
}
public static void Log(UInt32 Type, String Message)
{
@ -106,13 +123,25 @@ namespace Fsp
}
/* start/stop */
/// <summary>
/// Provides a means to customize the returned status code when an exception happens.
/// </summary>
/// <param name="ex"></param>
/// <returns>STATUS_SUCCESS or error code.</returns>
protected virtual Int32 ExceptionHandler(Exception ex)
{
return unchecked((Int32)0xE0434f4D)/*STATUS_CLR_EXCEPTION*/;
}
/// <summary>
/// Occurs when the service starts.
/// </summary>
/// <param name="Args">Command line arguments passed to the service.</param>
protected virtual void OnStart(String[] Args)
{
}
/// <summary>
/// Occurs when the service stops.
/// </summary>
protected virtual void OnStop()
{
}
@ -152,7 +181,7 @@ namespace Fsp
private static Api.Proto.ServiceStart _OnStart = OnStart;
private static Api.Proto.ServiceStop _OnStop = OnStop;
private IntPtr _Service;
private IntPtr _ServicePtr;
}
}

View File

@ -1098,7 +1098,44 @@ static NTSTATUS FspFsvolCreateTryOpen(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Re
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);
if (FlushImage)

View File

@ -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);
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
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,
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber);
VOID FspFileNodeInvalidateFileInfo(FSP_FILE_NODE *FileNode);

View File

@ -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);
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
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,
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber);
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, FspFileNodeTryGetFileInfo)
#pragma alloc_text(PAGE, FspFileNodeSetFileInfo)
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfoOnOpen)
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfo)
#pragma alloc_text(PAGE, FspFileNodeInvalidateFileInfo)
#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,
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber)
{

View File

@ -17,12 +17,16 @@ launchctl-x64 start memfs64 testdsk "" M: >nul
launchctl-x64 start memfs64 testnet \memfs64\test N: >nul
launchctl-x64 start memfs32 testdsk "" O: >nul
launchctl-x64 start memfs32 testnet \memfs32\test P: >nul
launchctl-x64 start memfs-dotnet testdsk "" Q: >nul
launchctl-x64 start memfs-dotnet testnet \memfs-dotnet\test R: >nul
rem Cannot use timeout under cygwin/mintty: "Input redirection is not supported"
waitfor 7BF47D72F6664550B03248ECFE77C7DD /t 3 2>nul
cd M: >nul 2>nul || (echo === Unable to find drive M: >&2 & goto fail)
cd N: >nul 2>nul || (echo === Unable to find drive N: >&2 & goto fail)
cd O: >nul 2>nul || (echo === Unable to find drive O: >&2 & goto fail)
cd P: >nul 2>nul || (echo === Unable to find drive P: >&2 & goto fail)
cd Q: >nul 2>nul || (echo === Unable to find drive Q: >&2 & goto fail)
cd R: >nul 2>nul || (echo === Unable to find drive R: >&2 & goto fail)
set dfl_tests=^
winfsp-tests-x64 ^
@ -31,6 +35,7 @@ set dfl_tests=^
winfsp-tests-x64-mountpoint-dir ^
winfsp-tests-x64-no-traverse ^
winfsp-tests-x64-oplock ^
winfsp-tests-x64-external ^
winfsp-tests-x64-external-share ^
fsx-memfs-x64-disk ^
fsx-memfs-x64-net ^
@ -46,6 +51,7 @@ set dfl_tests=^
winfsp-tests-x86-mountpoint-dir ^
winfsp-tests-x86-no-traverse ^
winfsp-tests-x86-oplock ^
winfsp-tests-x86-external ^
winfsp-tests-x86-external-share ^
fsx-memfs-x86-disk ^
fsx-memfs-x86-net ^
@ -54,14 +60,19 @@ set dfl_tests=^
net-use-memfs-x86 ^
winfstest-memfs-x86-disk ^
winfstest-memfs-x86-net ^
fscrash-x86
fscrash-x86 ^
winfsp-tests-dotnet-external ^
winfsp-tests-dotnet-external-share ^
fsx-memfs-dotnet-disk ^
fsx-memfs-dotnet-net ^
winfstest-memfs-dotnet-disk ^
winfstest-memfs-dotnet-net
set opt_tests=^
ifstest-memfs-x64-disk ^
ifstest-memfs-x86-disk ^
ifstest-memfs-dotnet-disk ^
sample-passthrough-x64 ^
sample-passthrough-x86 ^
sample-passthrough-cpp-x64 ^
sample-passthrough-cpp-x86 ^
sample-passthrough-fuse-x64 ^
sample-passthrough-fuse-x86 ^
sample-passthrough-dotnet
@ -121,6 +132,8 @@ launchctl-x64 stop memfs64 testdsk >nul
launchctl-x64 stop memfs64 testnet >nul
launchctl-x64 stop memfs32 testdsk >nul
launchctl-x64 stop memfs32 testnet >nul
launchctl-x64 stop memfs-dotnet testdsk >nul
launchctl-x64 stop memfs-dotnet testnet >nul
rem Cannot use timeout under cygwin/mintty: "Input redirection is not supported"
waitfor 7BF47D72F6664550B03248ECFE77C7DD /t 3 2>nul
@ -195,6 +208,12 @@ winfsp-tests-x86 --oplock=filter --resilient
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x64-external
M:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-x64.exe" --external --resilient
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x64-external-share
M:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-x64.exe" --external --share=winfsp-tests-share=M:\ --resilient ^
@ -242,6 +261,12 @@ net use L: /delete
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x86-external
O:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-x86.exe" --external --resilient
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x86-external-share
O:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-x86.exe" --external --share=winfsp-tests-share=O:\ --resilient ^
@ -351,6 +376,47 @@ fscrash-x86 --huge-alloc-size --cached >nul 2>&1
if !ERRORLEVEL! neq 1 goto fail
exit /b 0
:winfsp-tests-dotnet-external
Q:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-x64.exe" --external --resilient
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-dotnet-external-share
Q:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-x64.exe" --external --share=winfsp-tests-share=Q:\ --resilient ^
-reparse_symlink*
if !ERRORLEVEL! neq 0 goto fail
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
call :__ifstest-memfs M: \Device\WinFsp.Disk C:
if !ERRORLEVEL! neq 0 goto fail
@ -361,6 +427,11 @@ call :__ifstest-memfs O: \Device\WinFsp.Disk C:
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:ifstest-memfs-dotnet-disk
call :__ifstest-memfs Q: \Device\WinFsp.Disk C:
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:__ifstest-memfs
%1
set IfsTestDirectories=^

0
tools/version-info.bat Normal file → Executable file
View File

1156
tst/memfs-dotnet/Program.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -192,7 +192,7 @@ typedef struct _MEMFS_FILE_NODE
SIZE_T ReparseDataSize;
PVOID ReparseData;
#endif
ULONG RefCount;
volatile LONG RefCount;
#if defined(MEMFS_NAMED_STREAMS)
struct _MEMFS_FILE_NODE *MainFileNode;
#endif
@ -260,13 +260,13 @@ VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode)
static inline
VOID MemfsFileNodeReference(MEMFS_FILE_NODE *FileNode)
{
FileNode->RefCount++;
InterlockedIncrement(&FileNode->RefCount);
}
static inline
VOID MemfsFileNodeDereference(MEMFS_FILE_NODE *FileNode)
{
if (0 == --FileNode->RefCount)
if (0 == InterlockedDecrement(&FileNode->RefCount))
MemfsFileNodeDelete(FileNode);
}
@ -850,14 +850,18 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS Result;
#if defined(MEMFS_NAMED_STREAMS)
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { TRUE };
ULONG Index;
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
MemfsFileNodeMapEnumerateFn, &Context);
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]);
}
MemfsFileNodeMapEnumerateFree(&Context);
#endif

View File

@ -316,6 +316,9 @@ static void dirbuf_fill_test(void)
void dirbuf_tests(void)
{
if (OptExternal)
return;
TEST(dirbuf_empty_test);
TEST(dirbuf_dots_test);
TEST(dirbuf_fill_test);

View File

@ -31,5 +31,8 @@ void eventlog_test(void)
void eventlog_tests(void)
{
if (OptExternal)
return;
TEST_OPT(eventlog_test);
}

View File

@ -370,5 +370,8 @@ void fuse_opt_parse_test(void)
void fuse_opt_tests(void)
{
if (OptExternal)
return;
TEST(fuse_opt_parse_test);
}

View File

@ -102,5 +102,8 @@ void memfs_test(void)
void memfs_tests(void)
{
if (OptExternal)
return;
TEST(memfs_test);
}

View File

@ -336,7 +336,7 @@ void mount_preflight_test(void)
void mount_tests(void)
{
if (NtfsTests || OptOplock)
if (OptExternal || OptOplock)
return;
TEST_OPT(mount_invalid_test);

View File

@ -126,6 +126,9 @@ void path_suffix_test(void)
void path_tests(void)
{
if (OptExternal)
return;
TEST(path_prefix_test);
TEST(path_suffix_test);
}

View File

@ -265,6 +265,9 @@ void posix_map_path_test(void)
void posix_tests(void)
{
if (OptExternal)
return;
TEST(posix_map_sid_test);
TEST(posix_map_sd_test);
TEST(posix_map_path_test);

View File

@ -262,7 +262,7 @@ void timeout_transact_test(void)
void timeout_tests(void)
{
if (NtfsTests || OptOplock)
if (OptExternal || OptOplock)
return;
TEST_OPT(timeout_pending_test);

View File

@ -38,5 +38,8 @@ static void version_test(void)
void version_tests(void)
{
if (OptExternal)
return;
TEST(version_test);
}

View File

@ -28,6 +28,7 @@ int NtfsTests = 0;
int WinFspDiskTests = 1;
int WinFspNetTests = 1;
BOOLEAN OptExternal = FALSE;
BOOLEAN OptResilient = FALSE;
BOOLEAN OptCaseInsensitiveCmp = FALSE;
BOOLEAN OptCaseInsensitive = FALSE;
@ -211,6 +212,7 @@ int main(int argc, char *argv[])
{
if (0 == strcmp("--ntfs", a) || 0 == strcmp("--external", a))
{
OptExternal = TRUE;
NtfsTests = 1;
WinFspDiskTests = 0;
WinFspNetTests = 0;

View File

@ -149,6 +149,7 @@ extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
extern BOOLEAN OptExternal;
extern BOOLEAN OptResilient;
extern BOOLEAN OptCaseInsensitiveCmp;
extern BOOLEAN OptCaseInsensitive;