Merge branch 'pvt-xattr'

This commit is contained in:
Bill Zissimopoulos 2019-03-19 18:23:47 -07:00
commit ff6421866d
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
26 changed files with 2852 additions and 121 deletions

View File

@ -11,6 +11,9 @@ environment:
#- CONFIGURATION: Release #- CONFIGURATION: Release
# TESTING: Perf # TESTING: Perf
init:
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
install: install:
- git submodule update --init --recursive - git submodule update --init --recursive
- appveyor AddMessage "Change boot configuration and reboot" -Category Information - appveyor AddMessage "Change boot configuration and reboot" -Category Information
@ -42,3 +45,4 @@ test_script:
on_finish: on_finish:
- if exist %SystemRoot%\memory.dmp (7z a memory.dmp.zip %SystemRoot%\memory.dmp && appveyor PushArtifact memory.dmp.zip) - if exist %SystemRoot%\memory.dmp (7z a memory.dmp.zip %SystemRoot%\memory.dmp && appveyor PushArtifact memory.dmp.zip)
- verifier /query - verifier /query
#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))

View File

@ -185,6 +185,7 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\devctl-test.c" /> <ClCompile Include="..\..\..\tst\winfsp-tests\devctl-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\dirbuf-test.c" /> <ClCompile Include="..\..\..\tst\winfsp-tests\dirbuf-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\dirctl-test.c" /> <ClCompile Include="..\..\..\tst\winfsp-tests\dirctl-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\ea-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\eventlog-test.c" /> <ClCompile Include="..\..\..\tst\winfsp-tests\eventlog-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\exec-test.c" /> <ClCompile Include="..\..\..\tst\winfsp-tests\exec-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\flush-test.c" /> <ClCompile Include="..\..\..\tst\winfsp-tests\flush-test.c" />

View File

@ -94,6 +94,9 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\fuse-test.c"> <ClCompile Include="..\..\..\tst\winfsp-tests\fuse-test.c">
<Filter>Source</Filter> <Filter>Source</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\tst\winfsp-tests\ea-test.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\ext\tlib\testsuite.h"> <ClInclude Include="..\..\..\ext\tlib\testsuite.h">

View File

@ -60,232 +60,168 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fsptool", "tools\fsptool.vc
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
Installer.Debug|Any CPU = Installer.Debug|Any CPU
Installer.Debug|x64 = Installer.Debug|x64 Installer.Debug|x64 = Installer.Debug|x64
Installer.Debug|x86 = Installer.Debug|x86 Installer.Debug|x86 = Installer.Debug|x86
Installer.Release|Any CPU = Installer.Release|Any CPU
Installer.Release|x64 = Installer.Release|x64 Installer.Release|x64 = Installer.Release|x64
Installer.Release|x86 = Installer.Release|x86 Installer.Release|x86 = Installer.Release|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64 Release|x64 = Release|x64
Release|x86 = Release|x86 Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|Any CPU.ActiveCfg = Debug|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x64.ActiveCfg = Debug|x64 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x64.ActiveCfg = Debug|x64
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x64.Build.0 = Debug|x64 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x64.Build.0 = Debug|x64
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x86.ActiveCfg = Debug|Win32 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x86.ActiveCfg = Debug|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x86.Build.0 = Debug|Win32 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Debug|x86.Build.0 = Debug|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Debug|Any CPU.ActiveCfg = Release|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Debug|x64.ActiveCfg = Debug|x64 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Debug|x64.ActiveCfg = Debug|x64
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Release|Any CPU.ActiveCfg = Release|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Release|x64.ActiveCfg = Release|x64 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Release|x64.ActiveCfg = Release|x64
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Release|x86.ActiveCfg = Release|Win32 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Installer.Release|x86.ActiveCfg = Release|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|Any CPU.ActiveCfg = Release|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x64.ActiveCfg = Release|x64 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x64.ActiveCfg = Release|x64
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x64.Build.0 = Release|x64 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x64.Build.0 = Release|x64
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x86.ActiveCfg = Release|Win32 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x86.ActiveCfg = Release|Win32
{4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x86.Build.0 = Release|Win32 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x86.Build.0 = Release|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|Any CPU.ActiveCfg = Debug|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x64.ActiveCfg = Debug|x64 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x64.ActiveCfg = Debug|x64
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x64.Build.0 = Debug|x64 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x64.Build.0 = Debug|x64
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x86.ActiveCfg = Debug|Win32 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x86.ActiveCfg = Debug|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x86.Build.0 = Debug|Win32 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Debug|x86.Build.0 = Debug|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Debug|Any CPU.ActiveCfg = Release|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Debug|x64.ActiveCfg = Debug|x64 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Debug|x64.ActiveCfg = Debug|x64
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Release|Any CPU.ActiveCfg = Release|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Release|x64.ActiveCfg = Release|x64 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Release|x64.ActiveCfg = Release|x64
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Release|x86.ActiveCfg = Release|Win32 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Installer.Release|x86.ActiveCfg = Release|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|Any CPU.ActiveCfg = Release|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x64.ActiveCfg = Release|x64 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x64.ActiveCfg = Release|x64
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x64.Build.0 = Release|x64 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x64.Build.0 = Release|x64
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x86.ActiveCfg = Release|Win32 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x86.ActiveCfg = Release|Win32
{C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x86.Build.0 = Release|Win32 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x86.Build.0 = Release|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|Any CPU.ActiveCfg = Debug|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.ActiveCfg = Debug|x64 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.ActiveCfg = Debug|x64
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.Build.0 = Debug|x64 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.Build.0 = Debug|x64
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.ActiveCfg = Debug|Win32 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.ActiveCfg = Debug|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.Build.0 = Debug|Win32 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.Build.0 = Debug|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Debug|Any CPU.ActiveCfg = Release|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Debug|x64.ActiveCfg = Debug|x64 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Debug|x64.ActiveCfg = Debug|x64
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Release|Any CPU.ActiveCfg = Release|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Release|x64.ActiveCfg = Release|x64 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Release|x64.ActiveCfg = Release|x64
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Release|x86.ActiveCfg = Release|Win32 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Installer.Release|x86.ActiveCfg = Release|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|Any CPU.ActiveCfg = Release|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.ActiveCfg = Release|x64 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.ActiveCfg = Release|x64
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.Build.0 = Release|x64 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.Build.0 = Release|x64
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.ActiveCfg = Release|Win32 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.ActiveCfg = Release|Win32
{262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.Build.0 = Release|Win32 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.Build.0 = Release|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|Any CPU.ActiveCfg = Debug|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x64.ActiveCfg = Debug|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x64.ActiveCfg = Debug|x64
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x64.Build.0 = Debug|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x64.Build.0 = Debug|x64
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x86.ActiveCfg = Debug|Win32 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x86.ActiveCfg = Debug|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x86.Build.0 = Debug|Win32 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Debug|x86.Build.0 = Debug|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Debug|Any CPU.ActiveCfg = Release|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Debug|x64.ActiveCfg = Debug|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Debug|x64.ActiveCfg = Debug|x64
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Release|Any CPU.ActiveCfg = Release|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Release|x64.ActiveCfg = Release|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Release|x64.ActiveCfg = Release|x64
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Release|x86.ActiveCfg = Release|Win32 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Installer.Release|x86.ActiveCfg = Release|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|Any CPU.ActiveCfg = Release|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x64.ActiveCfg = Release|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x64.ActiveCfg = Release|x64
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x64.Build.0 = Release|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x64.Build.0 = Release|x64
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.ActiveCfg = Release|Win32 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.ActiveCfg = Release|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.Build.0 = Release|Win32 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.Build.0 = Release|Win32
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Debug|Any CPU.ActiveCfg = Debug|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Debug|x64.ActiveCfg = Debug|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Debug|x64.ActiveCfg = Debug|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Debug|x86.ActiveCfg = Debug|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Debug|x86.ActiveCfg = Debug|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|Any CPU.ActiveCfg = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|Any CPU.Build.0 = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x64.ActiveCfg = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x64.ActiveCfg = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x64.Build.0 = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x64.Build.0 = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x86.ActiveCfg = Debug|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x86.ActiveCfg = Debug|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x86.Build.0 = Debug|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Debug|x86.Build.0 = Debug|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|Any CPU.ActiveCfg = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|Any CPU.Build.0 = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x64.ActiveCfg = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x64.ActiveCfg = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x64.Build.0 = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x64.Build.0 = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x86.ActiveCfg = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x86.ActiveCfg = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x86.Build.0 = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Installer.Release|x86.Build.0 = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Release|Any CPU.ActiveCfg = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Release|x64.ActiveCfg = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Release|x64.ActiveCfg = Release|x86
{D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Release|x86.ActiveCfg = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Release|x86.ActiveCfg = Release|x86
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Debug|Any CPU.ActiveCfg = Debug|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Debug|x64.ActiveCfg = Debug|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Debug|x64.ActiveCfg = Debug|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Debug|x86.ActiveCfg = Debug|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Debug|x86.ActiveCfg = Debug|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|Any CPU.ActiveCfg = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|Any CPU.Build.0 = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x64.ActiveCfg = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x64.ActiveCfg = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x64.Build.0 = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x64.Build.0 = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x86.Build.0 = Debug|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Debug|x86.Build.0 = Debug|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|Any CPU.ActiveCfg = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|Any CPU.Build.0 = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x64.ActiveCfg = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x64.ActiveCfg = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x64.Build.0 = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x64.Build.0 = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x86.ActiveCfg = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x86.ActiveCfg = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x86.Build.0 = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Installer.Release|x86.Build.0 = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Release|Any CPU.ActiveCfg = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Release|x64.ActiveCfg = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Release|x64.ActiveCfg = Release|Win32
{95C223E6-B5F1-4FD0-9376-41CDBC824445}.Release|x86.ActiveCfg = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Release|x86.ActiveCfg = Release|Win32
{10757011-749D-4954-873B-AE38D8145472}.Debug|Any CPU.ActiveCfg = Debug|Win32
{10757011-749D-4954-873B-AE38D8145472}.Debug|x64.ActiveCfg = Debug|x64 {10757011-749D-4954-873B-AE38D8145472}.Debug|x64.ActiveCfg = Debug|x64
{10757011-749D-4954-873B-AE38D8145472}.Debug|x64.Build.0 = Debug|x64 {10757011-749D-4954-873B-AE38D8145472}.Debug|x64.Build.0 = Debug|x64
{10757011-749D-4954-873B-AE38D8145472}.Debug|x86.ActiveCfg = Debug|Win32 {10757011-749D-4954-873B-AE38D8145472}.Debug|x86.ActiveCfg = Debug|Win32
{10757011-749D-4954-873B-AE38D8145472}.Debug|x86.Build.0 = Debug|Win32 {10757011-749D-4954-873B-AE38D8145472}.Debug|x86.Build.0 = Debug|Win32
{10757011-749D-4954-873B-AE38D8145472}.Installer.Debug|Any CPU.ActiveCfg = Release|Win32
{10757011-749D-4954-873B-AE38D8145472}.Installer.Debug|x64.ActiveCfg = Debug|x64 {10757011-749D-4954-873B-AE38D8145472}.Installer.Debug|x64.ActiveCfg = Debug|x64
{10757011-749D-4954-873B-AE38D8145472}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {10757011-749D-4954-873B-AE38D8145472}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{10757011-749D-4954-873B-AE38D8145472}.Installer.Release|Any CPU.ActiveCfg = Release|Win32
{10757011-749D-4954-873B-AE38D8145472}.Installer.Release|x64.ActiveCfg = Release|x64 {10757011-749D-4954-873B-AE38D8145472}.Installer.Release|x64.ActiveCfg = Release|x64
{10757011-749D-4954-873B-AE38D8145472}.Installer.Release|x86.ActiveCfg = Release|Win32 {10757011-749D-4954-873B-AE38D8145472}.Installer.Release|x86.ActiveCfg = Release|Win32
{10757011-749D-4954-873B-AE38D8145472}.Release|Any CPU.ActiveCfg = Release|Win32
{10757011-749D-4954-873B-AE38D8145472}.Release|x64.ActiveCfg = Release|x64 {10757011-749D-4954-873B-AE38D8145472}.Release|x64.ActiveCfg = Release|x64
{10757011-749D-4954-873B-AE38D8145472}.Release|x64.Build.0 = Release|x64 {10757011-749D-4954-873B-AE38D8145472}.Release|x64.Build.0 = Release|x64
{10757011-749D-4954-873B-AE38D8145472}.Release|x86.ActiveCfg = Release|Win32 {10757011-749D-4954-873B-AE38D8145472}.Release|x86.ActiveCfg = Release|Win32
{10757011-749D-4954-873B-AE38D8145472}.Release|x86.Build.0 = Release|Win32 {10757011-749D-4954-873B-AE38D8145472}.Release|x86.Build.0 = Release|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|Any CPU.ActiveCfg = Debug|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x64.ActiveCfg = Debug|x64 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x64.ActiveCfg = Debug|x64
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x64.Build.0 = Debug|x64 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x64.Build.0 = Debug|x64
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x86.ActiveCfg = Debug|Win32 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x86.ActiveCfg = Debug|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x86.Build.0 = Debug|Win32 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Debug|x86.Build.0 = Debug|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Debug|Any CPU.ActiveCfg = Release|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Debug|x64.ActiveCfg = Debug|x64 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Debug|x64.ActiveCfg = Debug|x64
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Release|Any CPU.ActiveCfg = Release|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Release|x64.ActiveCfg = Release|x64 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Release|x64.ActiveCfg = Release|x64
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Release|x86.ActiveCfg = Release|Win32 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Installer.Release|x86.ActiveCfg = Release|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|Any CPU.ActiveCfg = Release|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x64.ActiveCfg = Release|x64 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x64.ActiveCfg = Release|x64
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x64.Build.0 = Release|x64 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x64.Build.0 = Release|x64
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x86.ActiveCfg = Release|Win32 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x86.ActiveCfg = Release|Win32
{C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x86.Build.0 = Release|Win32 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x86.Build.0 = Release|Win32
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x64.ActiveCfg = Debug|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x64.ActiveCfg = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x64.Build.0 = Debug|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x64.Build.0 = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x86.ActiveCfg = Debug|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x86.ActiveCfg = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x86.Build.0 = Debug|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Debug|x86.Build.0 = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Debug|x64.ActiveCfg = Debug|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Debug|x64.ActiveCfg = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Debug|x86.ActiveCfg = Debug|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Debug|x86.ActiveCfg = Debug|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Release|Any CPU.ActiveCfg = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Release|x64.ActiveCfg = Release|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Release|x64.ActiveCfg = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Release|x86.ActiveCfg = Release|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Installer.Release|x86.ActiveCfg = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|Any CPU.Build.0 = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x64.ActiveCfg = Release|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x64.ActiveCfg = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x64.Build.0 = Release|Any CPU {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.ActiveCfg = Release|Any CPU
{94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x86.Build.0 = 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.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x64.Build.0 = 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.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x86.Build.0 = 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|x64.ActiveCfg = Debug|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x64.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x86.ActiveCfg = Debug|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x86.ActiveCfg = Debug|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|Any CPU.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x64.ActiveCfg = Release|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x64.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x86.ActiveCfg = Release|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x86.ActiveCfg = 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.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x64.Build.0 = 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.ActiveCfg = Release|Any CPU
{4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.Build.0 = Release|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.Build.0 = Release|Any CPU
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|Any CPU.ActiveCfg = Debug|Win32
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x64.ActiveCfg = Debug|x64 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x64.ActiveCfg = Debug|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x64.Build.0 = Debug|x64 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x64.Build.0 = Debug|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x86.ActiveCfg = Debug|Win32 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x86.ActiveCfg = Debug|Win32
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x86.Build.0 = Debug|Win32 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Debug|x86.Build.0 = Debug|Win32
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Debug|Any CPU.ActiveCfg = Release|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Debug|x64.ActiveCfg = Debug|x64 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Debug|x64.ActiveCfg = Debug|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Release|Any CPU.ActiveCfg = Release|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Release|x64.ActiveCfg = Release|x64 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Release|x64.ActiveCfg = Release|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Release|x86.ActiveCfg = Release|Win32 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Installer.Release|x86.ActiveCfg = Release|Win32
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|Any CPU.ActiveCfg = Release|Win32
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x64.ActiveCfg = Release|x64 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x64.ActiveCfg = Release|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x64.Build.0 = Release|x64 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x64.Build.0 = Release|x64
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x86.ActiveCfg = Release|Win32 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x86.ActiveCfg = Release|Win32
{6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x86.Build.0 = Release|Win32 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x86.Build.0 = Release|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|Any CPU.ActiveCfg = Debug|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x64.ActiveCfg = Debug|x64 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x64.ActiveCfg = Debug|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x64.Build.0 = Debug|x64 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x64.Build.0 = Debug|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x86.ActiveCfg = Debug|Win32 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x86.ActiveCfg = Debug|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x86.Build.0 = Debug|Win32 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Debug|x86.Build.0 = Debug|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Debug|Any CPU.ActiveCfg = Release|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Debug|x64.ActiveCfg = Debug|x64 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Debug|x64.ActiveCfg = Debug|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Release|Any CPU.ActiveCfg = Release|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Release|x64.ActiveCfg = Release|x64 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Release|x64.ActiveCfg = Release|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Release|x86.ActiveCfg = Release|Win32 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Installer.Release|x86.ActiveCfg = Release|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|Any CPU.ActiveCfg = Release|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x64.ActiveCfg = Release|x64 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x64.ActiveCfg = Release|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x64.Build.0 = Release|x64 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x64.Build.0 = Release|x64
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x86.ActiveCfg = Release|Win32 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x86.ActiveCfg = Release|Win32
{264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x86.Build.0 = Release|Win32 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x86.Build.0 = Release|Win32
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|Any CPU.ActiveCfg = Debug|Win32
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x64.ActiveCfg = Debug|x64 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x64.ActiveCfg = Debug|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x64.Build.0 = Debug|x64 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x64.Build.0 = Debug|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x86.ActiveCfg = Debug|Win32 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x86.ActiveCfg = Debug|Win32
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x86.Build.0 = Debug|Win32 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Debug|x86.Build.0 = Debug|Win32
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Debug|Any CPU.ActiveCfg = Release|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Debug|x64.ActiveCfg = Debug|x64 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Debug|x64.ActiveCfg = Debug|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Debug|x86.ActiveCfg = Debug|Win32 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Debug|x86.ActiveCfg = Debug|Win32
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Release|Any CPU.ActiveCfg = Release|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Release|x64.ActiveCfg = Release|x64 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Release|x64.ActiveCfg = Release|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Release|x86.ActiveCfg = Release|Win32 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Installer.Release|x86.ActiveCfg = Release|Win32
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|Any CPU.ActiveCfg = Release|Win32
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|x64.ActiveCfg = Release|x64 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|x64.ActiveCfg = Release|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|x64.Build.0 = Release|x64 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|x64.Build.0 = Release|x64
{1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|x86.ActiveCfg = Release|Win32 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|x86.ActiveCfg = Release|Win32

View File

@ -152,7 +152,7 @@ enum
UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */\ UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */\
UINT32 NamedStreams:1; /* file system supports named streams */\ UINT32 NamedStreams:1; /* file system supports named streams */\
UINT32 HardLinks:1; /* unimplemented; set to 0 */\ UINT32 HardLinks:1; /* unimplemented; set to 0 */\
UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */\ UINT32 ExtendedAttributes:1; /* file system supports extended attributes */\
UINT32 ReadOnlyVolume:1;\ UINT32 ReadOnlyVolume:1;\
/* kernel-mode flags */\ /* kernel-mode flags */\
UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */\ UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */\
@ -167,7 +167,8 @@ enum
UINT32 UmReservedFlags:6;\ UINT32 UmReservedFlags:6;\
/* additional kernel-mode flags */\ /* additional kernel-mode flags */\
UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\ UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\
UINT32 KmReservedFlags:7;\ UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\
UINT32 KmReservedFlags:6;\
WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\ WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\
WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)];
#define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\ #define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\
@ -176,22 +177,28 @@ enum
UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\ UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\
UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\ UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\
UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\ UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\
UINT32 KmAdditionalReservedFlags:28;\ UINT32 EaTimeoutValid:1; /* EaTimeout field is valid */\
UINT32 KmAdditionalReservedFlags:27;\
UINT32 VolumeInfoTimeout; /* volume info timeout (millis); overrides FileInfoTimeout */\ UINT32 VolumeInfoTimeout; /* volume info timeout (millis); overrides FileInfoTimeout */\
UINT32 DirInfoTimeout; /* dir info timeout (millis); overrides FileInfoTimeout */\ UINT32 DirInfoTimeout; /* dir info timeout (millis); overrides FileInfoTimeout */\
UINT32 SecurityTimeout; /* security info timeout (millis); overrides FileInfoTimeout */\ UINT32 SecurityTimeout; /* security info timeout (millis); overrides FileInfoTimeout */\
UINT32 StreamInfoTimeout; /* stream info timeout (millis); overrides FileInfoTimeout */\ UINT32 StreamInfoTimeout; /* stream info timeout (millis); overrides FileInfoTimeout */\
UINT32 Reserved32[3];\ UINT32 EaTimeout; /* EA timeout (millis); overrides FileInfoTimeout */\
UINT32 Reserved32[2];\
UINT64 Reserved64[2]; UINT64 Reserved64[2];
typedef struct typedef struct
{ {
FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN
} FSP_FSCTL_VOLUME_PARAMS_V0; } FSP_FSCTL_VOLUME_PARAMS_V0;
FSP_FSCTL_STATIC_ASSERT(456 == sizeof(FSP_FSCTL_VOLUME_PARAMS_V0),
"sizeof(FSP_FSCTL_VOLUME_PARAMS_V0) must be exactly 456.");
typedef struct typedef struct
{ {
FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN
FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN
} FSP_FSCTL_VOLUME_PARAMS; } FSP_FSCTL_VOLUME_PARAMS;
FSP_FSCTL_STATIC_ASSERT(504 == sizeof(FSP_FSCTL_VOLUME_PARAMS),
"sizeof(FSP_FSCTL_VOLUME_PARAMS) is currently 504. Update this assertion check if it changes.");
typedef struct typedef struct
{ {
UINT64 TotalSize; UINT64 TotalSize;
@ -261,7 +268,7 @@ typedef struct
UINT32 DesiredAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ UINT32 DesiredAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */
UINT32 GrantedAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ UINT32 GrantedAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */
UINT32 ShareAccess; /* FILE_SHARE_{READ,WRITE,DELETE} */ UINT32 ShareAccess; /* FILE_SHARE_{READ,WRITE,DELETE} */
FSP_FSCTL_TRANSACT_BUF Ea; /* reserved; not currently implemented */ FSP_FSCTL_TRANSACT_BUF Ea; /* extended attributes buffer */
UINT32 UserMode:1; /* request originated in user mode */ UINT32 UserMode:1; /* request originated in user mode */
UINT32 HasTraversePrivilege:1; /* requestor has TOKEN_HAS_TRAVERSE_PRIVILEGE */ UINT32 HasTraversePrivilege:1; /* requestor has TOKEN_HAS_TRAVERSE_PRIVILEGE */
UINT32 HasBackupPrivilege:1; /* requestor has TOKEN_HAS_BACKUP_PRIVILEGE */ UINT32 HasBackupPrivilege:1; /* requestor has TOKEN_HAS_BACKUP_PRIVILEGE */
@ -280,6 +287,7 @@ typedef struct
UINT32 FileAttributes; /* file attributes for overwritten/superseded files */ UINT32 FileAttributes; /* file attributes for overwritten/superseded files */
UINT64 AllocationSize; /* allocation size for overwritten/superseded files */ UINT64 AllocationSize; /* allocation size for overwritten/superseded files */
UINT32 Supersede:1; /* 0: FILE_OVERWRITE operation, 1: FILE_SUPERSEDE operation */ UINT32 Supersede:1; /* 0: FILE_OVERWRITE operation, 1: FILE_SUPERSEDE operation */
FSP_FSCTL_TRANSACT_BUF Ea; /* extended attributes buffer */
} Overwrite; } Overwrite;
struct struct
{ {
@ -356,6 +364,17 @@ typedef struct
} Info; } Info;
} SetInformation; } SetInformation;
struct struct
{
UINT64 UserContext;
UINT64 UserContext2;
} QueryEa;
struct
{
UINT64 UserContext;
UINT64 UserContext2;
FSP_FSCTL_TRANSACT_BUF Ea;
} SetEa;
struct
{ {
UINT64 UserContext; UINT64 UserContext;
UINT64 UserContext2; UINT64 UserContext2;
@ -470,6 +489,14 @@ typedef struct
FSP_FSCTL_FILE_INFO FileInfo; /* valid: File{Allocation,Basic,EndOfFile}Information */ FSP_FSCTL_FILE_INFO FileInfo; /* valid: File{Allocation,Basic,EndOfFile}Information */
} SetInformation; } SetInformation;
struct struct
{
FSP_FSCTL_TRANSACT_BUF Ea;
} QueryEa;
struct
{
FSP_FSCTL_TRANSACT_BUF Ea; /* Size==0 means no extended atttributed returned */
} SetEa;
struct
{ {
FSP_FSCTL_FILE_INFO FileInfo; /* valid when flushing file (not volume) */ FSP_FSCTL_FILE_INFO FileInfo; /* valid when flushing file (not volume) */
} FlushBuffers; } FlushBuffers;

View File

@ -85,6 +85,21 @@ typedef struct _REPARSE_DATA_BUFFER
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif #endif
/*
* The FILE_FULL_EA_INFORMATION definitions are missing from the user mode headers.
*/
#if !defined(FILE_NEED_EA)
#define FILE_NEED_EA 0x00000080
#endif
typedef struct _FILE_FULL_EA_INFORMATION
{
ULONG NextEntryOffset;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
/** /**
* @group File System * @group File System
* *
@ -894,12 +909,134 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
*/ */
NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem, NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile); PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile);
/**
* Create new file or directory.
*
* This function works like Create, except that it also accepts EA (extended attributes).
*
* NOTE: If both Create and CreateEx are defined, CreateEx takes precedence.
*
* @param FileSystem
* The file system on which this request is posted.
* @param FileName
* The name of the file or directory to be created.
* @param CreateOptions
* Create options for this request. This parameter has the same meaning as the
* CreateOptions parameter of the NtCreateFile API. User mode file systems should typically
* only be concerned with the flag FILE_DIRECTORY_FILE, which is an instruction to create a
* directory rather than a file. Some file systems may also want to pay attention to the
* FILE_NO_INTERMEDIATE_BUFFERING and FILE_WRITE_THROUGH flags, although these are
* typically handled by the FSD component.
* @param GrantedAccess
* Determines the specific access rights that have been granted for this request. Upon
* receiving this call all access checks have been performed and the user mode file system
* need not perform any additional checks. However this parameter may be useful to a user
* mode file system; for example the WinFsp-FUSE layer uses this parameter to determine
* which flags to use in its POSIX open() call.
* @param FileAttributes
* File attributes to apply to the newly created file or directory.
* @param SecurityDescriptor
* Security descriptor to apply to the newly created file or directory. This security
* descriptor will always be in self-relative format. Its length can be retrieved using the
* Windows GetSecurityDescriptorLength API. Will be NULL for named streams.
* @param AllocationSize
* Allocation size for the newly created file.
* @param Ea
* Extended attributes buffer.
* @param EaLength
* Extended attributes buffer length.
* @param PFileContext [out]
* Pointer that will receive the file context on successful return from this call.
* @param FileInfo [out]
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*CreateEx)(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo);
/**
* Overwrite a file.
*
* This function works like Overwrite, except that it also accepts EA (extended attributes).
*
* NOTE: If both Overwrite and OverwriteEx are defined, OverwriteEx takes precedence.
*
* @param FileSystem
* The file system on which this request is posted.
* @param FileContext
* The file context of the file to overwrite.
* @param FileAttributes
* File attributes to apply to the overwritten file.
* @param 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 AllocationSize
* Allocation size for the overwritten file.
* @param Ea
* Extended attributes buffer.
* @param EaLength
* Extended attributes buffer length.
* @param FileInfo [out]
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*OverwriteEx)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
FSP_FSCTL_FILE_INFO *FileInfo);
/**
* Get extended attributes.
*
* @param FileSystem
* The file system on which this request is posted.
* @param FileContext
* The file context of the file to get extended attributes for.
* @param Ea
* Extended attributes buffer.
* @param EaLength
* Extended attributes buffer length.
* @param PBytesTransferred [out]
* Pointer to a memory location that will receive the actual number of bytes transferred.
* @return
* STATUS_SUCCESS or error code.
* @see
* SetEa
* FspFileSystemAddEa
*/
NTSTATUS (*GetEa)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred);
/**
* Set extended attributes.
*
* @param FileSystem
* The file system on which this request is posted.
* @param FileContext
* The file context of the file to set extended attributes for.
* @param Ea
* Extended attributes buffer.
* @param EaLength
* Extended attributes buffer length.
* @return
* STATUS_SUCCESS or error code.
* @see
* GetEa
*/
NTSTATUS (*SetEa)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength);
/* /*
* This ensures that this interface will always contain 64 function pointers. * This ensures that this interface will always contain 64 function pointers.
* Please update when changing the interface as it is important for future compatibility. * Please update when changing the interface as it is important for future compatibility.
*/ */
NTSTATUS (*Reserved[37])(); NTSTATUS (*Reserved[33])();
} FSP_FILE_SYSTEM_INTERFACE; } FSP_FILE_SYSTEM_INTERFACE;
FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()), FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()),
"FSP_FILE_SYSTEM_INTERFACE must have 64 entries."); "FSP_FILE_SYSTEM_INTERFACE must have 64 entries.");
@ -1203,6 +1340,10 @@ FSP_API NTSTATUS FspFileSystemOpQueryInformation(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpFlushBuffers(FSP_FILE_SYSTEM *FileSystem, FSP_API NTSTATUS FspFileSystemOpFlushBuffers(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem,
@ -1433,6 +1574,56 @@ FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint(
*/ */
FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo, FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo,
PVOID Buffer, ULONG Length, PULONG PBytesTransferred); PVOID Buffer, ULONG Length, PULONG PBytesTransferred);
/**
* Enumerate extended attributes in a buffer.
*
* This is a helper for implementing the CreateEx and SetEa operations in file systems
* that support extended attributes.
*
* @param FileSystem
* The file system object.
* @param EnumerateEa
* Pointer to function that receives a single extended attribute. The function
* should return STATUS_SUCCESS or an error code if unsuccessful.
* @param Context
* User context to supply to EnumEa.
* @param Ea
* Extended attributes buffer.
* @param EaLength
* Extended attributes buffer length.
* @return
* STATUS_SUCCESS or error code from EnumerateEa.
*/
FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*EnumerateEa)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PFILE_FULL_EA_INFORMATION SingleEa),
PVOID Context,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength);
/**
* Add extended attribute to a buffer.
*
* This is a helper for implementing the GetEa operation.
*
* @param SingleEa
* The extended attribute to add. A value of NULL acts as an EOF marker for a GetEa
* operation.
* @param Ea
* Pointer to a buffer that will receive the extended attribute. This should contain
* the same value passed to the GetEa Ea parameter.
* @param EaLength
* Length of buffer. This should contain the same value passed to the GetEa
* EaLength parameter.
* @param PBytesTransferred [out]
* Pointer to a memory location that will receive the actual number of bytes stored. This should
* contain the same value passed to the GetEa PBytesTransferred parameter.
* @return
* TRUE if the extended attribute was added, FALSE if there was not enough space to add it.
* @see
* GetEa
*/
FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred);
/* /*
* Directory buffering * Directory buffering

View File

@ -161,6 +161,8 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath,
FileSystem->Operations[FspFsctlTransactWriteKind] = FspFileSystemOpWrite; FileSystem->Operations[FspFsctlTransactWriteKind] = FspFileSystemOpWrite;
FileSystem->Operations[FspFsctlTransactQueryInformationKind] = FspFileSystemOpQueryInformation; FileSystem->Operations[FspFsctlTransactQueryInformationKind] = FspFileSystemOpQueryInformation;
FileSystem->Operations[FspFsctlTransactSetInformationKind] = FspFileSystemOpSetInformation; FileSystem->Operations[FspFsctlTransactSetInformationKind] = FspFileSystemOpSetInformation;
FileSystem->Operations[FspFsctlTransactQueryEaKind] = FspFileSystemOpQueryEa;
FileSystem->Operations[FspFsctlTransactSetEaKind] = FspFileSystemOpSetEa;
FileSystem->Operations[FspFsctlTransactFlushBuffersKind] = FspFileSystemOpFlushBuffers; FileSystem->Operations[FspFsctlTransactFlushBuffersKind] = FspFileSystemOpFlushBuffers;
FileSystem->Operations[FspFsctlTransactQueryVolumeInformationKind] = FspFileSystemOpQueryVolumeInformation; FileSystem->Operations[FspFsctlTransactQueryVolumeInformationKind] = FspFileSystemOpQueryVolumeInformation;
FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation; FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation;

View File

@ -436,6 +436,15 @@ static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem,
memset(&OpenFileInfo, 0, sizeof OpenFileInfo); memset(&OpenFileInfo, 0, sizeof OpenFileInfo);
OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedName = (PVOID)Response->Buffer;
OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX;
if (0 != FileSystem->Interface->CreateEx)
Result = FileSystem->Interface->CreateEx(FileSystem,
(PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess,
Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize,
0 != Request->Req.Create.Ea.Size ?
(PVOID)(Request->Buffer + Request->Req.Create.Ea.Offset) : 0,
Request->Req.Create.Ea.Size,
AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo);
else
Result = FileSystem->Interface->Create(FileSystem, Result = FileSystem->Interface->Create(FileSystem,
(PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess,
Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize,
@ -574,6 +583,15 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem,
memset(&OpenFileInfo, 0, sizeof OpenFileInfo); memset(&OpenFileInfo, 0, sizeof OpenFileInfo);
OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedName = (PVOID)Response->Buffer;
OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX;
if (0 != FileSystem->Interface->CreateEx)
Result = FileSystem->Interface->CreateEx(FileSystem,
(PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess,
Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize,
0 != Request->Req.Create.Ea.Size ?
(PVOID)(Request->Buffer + Request->Req.Create.Ea.Offset) : 0,
Request->Req.Create.Ea.Size,
AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo);
else
Result = FileSystem->Interface->Create(FileSystem, Result = FileSystem->Interface->Create(FileSystem,
(PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess,
Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize,
@ -699,6 +717,15 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste
memset(&OpenFileInfo, 0, sizeof OpenFileInfo); memset(&OpenFileInfo, 0, sizeof OpenFileInfo);
OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedName = (PVOID)Response->Buffer;
OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX;
if (0 != FileSystem->Interface->CreateEx)
Result = FileSystem->Interface->CreateEx(FileSystem,
(PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess,
Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize,
0 != Request->Req.Create.Ea.Size ?
(PVOID)(Request->Buffer + Request->Req.Create.Ea.Offset) : 0,
Request->Req.Create.Ea.Size,
AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo);
else
Result = FileSystem->Interface->Create(FileSystem, Result = FileSystem->Interface->Create(FileSystem,
(PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess,
Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize,
@ -865,9 +892,9 @@ FSP_API NTSTATUS FspFileSystemOpCreate(FSP_FILE_SYSTEM *FileSystem,
{ {
NTSTATUS Result; NTSTATUS Result;
if (0 == FileSystem->Interface->Create || if ((0 == FileSystem->Interface->Create && 0 == FileSystem->Interface->CreateEx) ||
0 == FileSystem->Interface->Open || 0 == FileSystem->Interface->Open ||
0 == FileSystem->Interface->Overwrite) (0 == FileSystem->Interface->Overwrite && 0 == FileSystem->Interface->OverwriteEx))
return STATUS_INVALID_DEVICE_REQUEST; return STATUS_INVALID_DEVICE_REQUEST;
if (Request->Req.Create.OpenTargetDirectory) if (Request->Req.Create.OpenTargetDirectory)
@ -910,10 +937,21 @@ FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS Result; NTSTATUS Result;
FSP_FSCTL_FILE_INFO FileInfo; FSP_FSCTL_FILE_INFO FileInfo;
if (0 == FileSystem->Interface->Overwrite) if (0 == FileSystem->Interface->Overwrite && 0 == FileSystem->Interface->OverwriteEx)
return STATUS_INVALID_DEVICE_REQUEST; return STATUS_INVALID_DEVICE_REQUEST;
memset(&FileInfo, 0, sizeof FileInfo); memset(&FileInfo, 0, sizeof FileInfo);
if (0 != FileSystem->Interface->OverwriteEx)
Result = FileSystem->Interface->OverwriteEx(FileSystem,
(PVOID)ValOfFileContext(Request->Req.Overwrite),
Request->Req.Overwrite.FileAttributes,
Request->Req.Overwrite.Supersede,
Request->Req.Overwrite.AllocationSize,
0 != Request->Req.Overwrite.Ea.Size ?
(PVOID)(Request->Buffer + Request->Req.Overwrite.Ea.Offset) : 0,
Request->Req.Overwrite.Ea.Size,
&FileInfo);
else
Result = FileSystem->Interface->Overwrite(FileSystem, Result = FileSystem->Interface->Overwrite(FileSystem,
(PVOID)ValOfFileContext(Request->Req.Overwrite), (PVOID)ValOfFileContext(Request->Req.Overwrite),
Request->Req.Overwrite.FileAttributes, Request->Req.Overwrite.FileAttributes,
@ -1145,6 +1183,39 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
NTSTATUS Result;
ULONG BytesTransferred;
if (0 == FileSystem->Interface->GetEa)
return STATUS_INVALID_DEVICE_REQUEST;
BytesTransferred = 0;
Result = FileSystem->Interface->GetEa(FileSystem,
(PVOID)ValOfFileContext(Request->Req.QueryEa),
(PVOID)Response->Buffer, FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX, &BytesTransferred);
if (!NT_SUCCESS(Result))
return STATUS_BUFFER_OVERFLOW != Result ? Result : STATUS_EA_LIST_INCONSISTENT;
Response->Size = (UINT16)(sizeof *Response + BytesTransferred);
Response->Rsp.QueryEa.Ea.Offset = 0;
Response->Rsp.QueryEa.Ea.Size = (UINT16)BytesTransferred;
return STATUS_SUCCESS;
}
FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
if (0 == FileSystem->Interface->SetEa)
return STATUS_INVALID_DEVICE_REQUEST;
return FileSystem->Interface->SetEa(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetEa),
(PVOID)Request->Buffer, Request->Req.SetEa.Ea.Size);
}
FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{ {
@ -1726,3 +1797,51 @@ FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo,
{ {
return FspFileSystemAddXxxInfo(StreamInfo, Buffer, Length, PBytesTransferred); return FspFileSystemAddXxxInfo(StreamInfo, Buffer, Length, PBytesTransferred);
} }
FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*EnumerateEa)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PFILE_FULL_EA_INFORMATION SingleEa),
PVOID Context,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength)
{
NTSTATUS Result = STATUS_SUCCESS;
for (PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + EaLength);
EaEnd > Ea; Ea = FSP_NEXT_EA(Ea, EaEnd))
{
Result = EnumerateEa(FileSystem, Context, Ea);
if (!NT_SUCCESS(Result))
break;
}
return Result;
}
FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa,
PFILE_FULL_EA_INFORMATION Ea, ULONG Length, PULONG PBytesTransferred)
{
if (0 != SingleEa)
{
PUINT8 EaPtr = (PUINT8)Ea + FSP_FSCTL_ALIGN_UP(*PBytesTransferred, sizeof(ULONG));
PUINT8 EaEnd = (PUINT8)Ea + Length;
ULONG EaLen = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
SingleEa->EaNameLength + 1 + SingleEa->EaValueLength;
if (EaEnd < EaPtr + EaLen)
return FALSE;
memcpy(EaPtr, SingleEa, EaLen);
((PFILE_FULL_EA_INFORMATION)EaPtr)->NextEntryOffset = FSP_FSCTL_ALIGN_UP(EaLen, sizeof(ULONG));
*PBytesTransferred = (ULONG)(EaPtr + EaLen - (PUINT8)Ea);
}
else if ((ULONG)FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) <= *PBytesTransferred)
{
PUINT8 EaEnd = (PUINT8)Ea + *PBytesTransferred;
while (EaEnd > (PUINT8)Ea + Ea->NextEntryOffset)
Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset);
Ea->NextEntryOffset = 0;
}
return TRUE;
}

View File

@ -92,4 +92,7 @@ static inline BOOLEAN FspPathIsDrive(PWSTR FileName)
L':' == FileName[1] && L'\0' == FileName[2]; L':' == FileName[1] && L'\0' == FileName[2];
} }
#define FSP_NEXT_EA(Ea, EaEnd) \
(0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd))
#endif #endif

View File

@ -1078,6 +1078,108 @@ namespace Fsp
else else
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
public virtual Int32 CreateEx(
String FileName,
UInt32 CreateOptions,
UInt32 GrantedAccess,
UInt32 FileAttributes,
Byte[] SecurityDescriptor,
UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
out Object FileNode,
out Object FileDesc,
out FileInfo FileInfo,
out String NormalizedName)
{
return Create(
FileName,
CreateOptions,
GrantedAccess,
FileAttributes,
SecurityDescriptor,
AllocationSize,
out FileNode,
out FileDesc,
out FileInfo,
out NormalizedName);
}
public virtual Int32 OverwriteEx(
Object FileNode,
Object FileDesc,
UInt32 FileAttributes,
Boolean ReplaceFileAttributes,
UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
out FileInfo FileInfo)
{
return Overwrite(
FileNode,
FileDesc,
FileAttributes,
ReplaceFileAttributes,
AllocationSize,
out FileInfo);
}
public virtual Int32 GetEa(
Object FileNode,
Object FileDesc,
IntPtr Ea,
UInt32 EaLength,
out UInt32 BytesTransferred)
{
Object Context = null;
String EaName;
Byte[] EaValue;
Boolean NeedEa;
FullEaInformation EaInfo = new FullEaInformation();
BytesTransferred = default(UInt32);
while (GetEaEntry(FileNode, FileDesc, ref Context, out EaName, out EaValue, out NeedEa))
{
EaInfo.Set(EaName, EaValue, NeedEa);
if (!Api.FspFileSystemAddEa(ref EaInfo, Ea, EaLength, out BytesTransferred))
return STATUS_SUCCESS;
}
Api.FspFileSystemEndEa(Ea, EaLength, out BytesTransferred);
return STATUS_SUCCESS;
}
public virtual Boolean GetEaEntry(
Object FileNode,
Object FileDesc,
ref Object Context,
out String EaName,
out Byte[] EaValue,
out Boolean NeedEa)
{
EaName = default(String);
EaValue = default(Byte[]);
NeedEa = default(Boolean);
return false;
}
public virtual Int32 SetEa(
Object FileNode,
Object FileDesc,
IntPtr Ea,
UInt32 EaLength)
{
return Api.FspFileSystemEnumerateEa(
FileNode,
FileDesc,
this.SetEaEntry,
Ea,
EaLength);
}
public virtual Int32 SetEaEntry(
Object FileNode,
Object FileDesc,
ref Object Context,
String EaName,
Byte[] EaValue,
Boolean NeedEa)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/* helpers */ /* helpers */
/// <summary> /// <summary>

View File

@ -183,6 +183,14 @@ namespace Fsp
get { return 0 != (_VolumeParams.Flags & VolumeParams.NamedStreams); } get { return 0 != (_VolumeParams.Flags & VolumeParams.NamedStreams); }
set { _VolumeParams.Flags |= (value ? VolumeParams.NamedStreams : 0); } set { _VolumeParams.Flags |= (value ? VolumeParams.NamedStreams : 0); }
} }
/// <summary>
/// Gets or sets a value that determines whether the file system supports extended attributes.
/// </summary>
public Boolean ExtendedAttributes
{
get { return 0 != (_VolumeParams.Flags & VolumeParams.ExtendedAttributes); }
set { _VolumeParams.Flags |= (value ? VolumeParams.ExtendedAttributes : 0); }
}
public Boolean PostCleanupWhenModifiedOnly public Boolean PostCleanupWhenModifiedOnly
{ {
get { return 0 != (_VolumeParams.Flags & VolumeParams.PostCleanupWhenModifiedOnly); } get { return 0 != (_VolumeParams.Flags & VolumeParams.PostCleanupWhenModifiedOnly); }
@ -464,6 +472,8 @@ namespace Fsp
UInt32 FileAttributes, UInt32 FileAttributes,
IntPtr SecurityDescriptor, IntPtr SecurityDescriptor,
UInt64 AllocationSize, UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
ref FullContext FullContext, ref FullContext FullContext,
ref OpenFileInfo OpenFileInfo) ref OpenFileInfo OpenFileInfo)
{ {
@ -473,13 +483,15 @@ namespace Fsp
Object FileNode, FileDesc; Object FileNode, FileDesc;
String NormalizedName; String NormalizedName;
Int32 Result; Int32 Result;
Result = FileSystem.Create( Result = FileSystem.CreateEx(
FileName, FileName,
CreateOptions, CreateOptions,
GrantedAccess, GrantedAccess,
FileAttributes, FileAttributes,
Api.MakeSecurityDescriptor(SecurityDescriptor), Api.MakeSecurityDescriptor(SecurityDescriptor),
AllocationSize, AllocationSize,
Ea,
EaLength,
out FileNode, out FileNode,
out FileDesc, out FileDesc,
out OpenFileInfo.FileInfo, out OpenFileInfo.FileInfo,
@ -538,6 +550,8 @@ namespace Fsp
UInt32 FileAttributes, UInt32 FileAttributes,
Boolean ReplaceFileAttributes, Boolean ReplaceFileAttributes,
UInt64 AllocationSize, UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
out FileInfo FileInfo) out FileInfo FileInfo)
{ {
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr); FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
@ -545,12 +559,14 @@ namespace Fsp
{ {
Object FileNode, FileDesc; Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc); Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.Overwrite( return FileSystem.OverwriteEx(
FileNode, FileNode,
FileDesc, FileDesc,
FileAttributes, FileAttributes,
ReplaceFileAttributes, ReplaceFileAttributes,
AllocationSize, AllocationSize,
Ea,
EaLength,
out FileInfo); out FileInfo);
} }
catch (Exception ex) catch (Exception ex)
@ -1077,15 +1093,62 @@ namespace Fsp
return ExceptionHandler(FileSystem, ex); return ExceptionHandler(FileSystem, ex);
} }
} }
private static Int32 GetEa(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr Ea,
UInt32 EaLength,
out UInt32 PBytesTransferred)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.GetEa(
FileNode,
FileDesc,
Ea,
EaLength,
out PBytesTransferred);
}
catch (Exception ex)
{
PBytesTransferred = default(UInt32);
return ExceptionHandler(FileSystem, ex);
}
}
private static Int32 SetEa(
IntPtr FileSystemPtr,
ref FullContext FullContext,
IntPtr Ea,
UInt32 EaLength)
{
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
try
{
Object FileNode, FileDesc;
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
return FileSystem.SetEa(
FileNode,
FileDesc,
Ea,
EaLength);
}
catch (Exception ex)
{
return ExceptionHandler(FileSystem, ex);
}
}
static FileSystemHost() static FileSystemHost()
{ {
_FileSystemInterface.GetVolumeInfo = GetVolumeInfo; _FileSystemInterface.GetVolumeInfo = GetVolumeInfo;
_FileSystemInterface.SetVolumeLabel = SetVolumeLabel; _FileSystemInterface.SetVolumeLabel = SetVolumeLabel;
_FileSystemInterface.GetSecurityByName = GetSecurityByName; _FileSystemInterface.GetSecurityByName = GetSecurityByName;
_FileSystemInterface.Create = Create; _FileSystemInterface.CreateEx = Create;
_FileSystemInterface.Open = Open; _FileSystemInterface.Open = Open;
_FileSystemInterface.Overwrite = Overwrite; _FileSystemInterface.OverwriteEx = Overwrite;
_FileSystemInterface.Cleanup = Cleanup; _FileSystemInterface.Cleanup = Cleanup;
_FileSystemInterface.Close = Close; _FileSystemInterface.Close = Close;
_FileSystemInterface.Read = Read; _FileSystemInterface.Read = Read;
@ -1106,6 +1169,8 @@ namespace Fsp
_FileSystemInterface.GetDirInfoByName = GetDirInfoByName; _FileSystemInterface.GetDirInfoByName = GetDirInfoByName;
_FileSystemInterface.Control = Control; _FileSystemInterface.Control = Control;
_FileSystemInterface.SetDelete = SetDelete; _FileSystemInterface.SetDelete = SetDelete;
_FileSystemInterface.GetEa = GetEa;
_FileSystemInterface.SetEa = SetEa;
_FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size); _FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size);
Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false); Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false);

View File

@ -52,6 +52,7 @@ namespace Fsp.Interop
internal const UInt32 UmFileContextIsUserContext2 = 0x00010000; internal const UInt32 UmFileContextIsUserContext2 = 0x00010000;
internal const UInt32 UmFileContextIsFullContext = 0x00020000; internal const UInt32 UmFileContextIsFullContext = 0x00020000;
internal const UInt32 AllowOpenInKernelMode = 0x01000000; internal const UInt32 AllowOpenInKernelMode = 0x01000000;
internal const UInt32 CasePreservedExtendedAttributes = 0x02000000;
internal const int PrefixSize = 192; internal const int PrefixSize = 192;
internal const int FileSystemNameSize = 16; internal const int FileSystemNameSize = 16;
@ -270,6 +271,40 @@ namespace Fsp.Interop
} }
} }
[StructLayout(LayoutKind.Sequential)]
internal struct FullEaInformation
{
internal const int EaNameSize = 16384 - 8;
internal UInt32 NextEntryOffset;
internal Byte Flags;
internal Byte EaNameLength;
internal UInt16 EaValueLength;
internal unsafe fixed Byte EaName[EaNameSize];
internal unsafe void Set(String Name, Byte[] Value, Boolean NeedEa)
{
int NameLength = 254 < Name.Length ? 254 : Name.Length;
int ValueLength = EaNameSize - Name.Length - 1 < Value.Length ?
EaNameSize - Name.Length - 1 : Value.Length;
NextEntryOffset = 0;
Flags = NeedEa ? (Byte)0x80/*FILE_NEED_EA*/ : (Byte)0;
EaNameLength = (Byte)NameLength;
EaValueLength = (UInt16)ValueLength;
fixed (Byte *P = EaName)
{
int I = 0, J = 0;
for (; NameLength > I; I++)
P[I] = (Byte)Name[I];
P[I++] = 0;
for (; ValueLength > J; J++)
P[I + J] = Value[J];
}
}
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct FullContext internal struct FullContext
{ {
@ -478,6 +513,42 @@ namespace Fsp.Interop
ref FullContext FullContext, ref FullContext FullContext,
[MarshalAs(UnmanagedType.LPWStr)] String FileName, [MarshalAs(UnmanagedType.LPWStr)] String FileName,
[MarshalAs(UnmanagedType.U1)] Boolean DeleteFile); [MarshalAs(UnmanagedType.U1)] Boolean DeleteFile);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 CreateEx(
IntPtr FileSystem,
[MarshalAs(UnmanagedType.LPWStr)] String FileName,
UInt32 CreateOptions,
UInt32 GrantedAccess,
UInt32 FileAttributes,
IntPtr SecurityDescriptor,
UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
ref FullContext FullContext,
ref OpenFileInfo OpenFileInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 OverwriteEx(
IntPtr FileSystem,
ref FullContext FullContext,
UInt32 FileAttributes,
[MarshalAs(UnmanagedType.U1)] Boolean ReplaceFileAttributes,
UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
out FileInfo FileInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 GetEa(
IntPtr FileSystem,
ref FullContext FullContext,
IntPtr Ea,
UInt32 EaLength,
out UInt32 PBytesTransferred);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 SetEa(
IntPtr FileSystem,
ref FullContext FullContext,
IntPtr Ea,
UInt32 EaLength);
} }
internal static int Size = IntPtr.Size * 64; internal static int Size = IntPtr.Size * 64;
@ -509,7 +580,11 @@ namespace Fsp.Interop
internal Proto.GetDirInfoByName GetDirInfoByName; internal Proto.GetDirInfoByName GetDirInfoByName;
internal Proto.Control Control; internal Proto.Control Control;
internal Proto.SetDelete SetDelete; internal Proto.SetDelete SetDelete;
/* NTSTATUS (*Reserved[37])(); */ internal Proto.CreateEx CreateEx;
internal Proto.OverwriteEx OverwriteEx;
internal Proto.GetEa GetEa;
internal Proto.SetEa SetEa;
/* NTSTATUS (*Reserved[33])(); */
} }
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
@ -604,6 +679,13 @@ namespace Fsp.Interop
out UInt32 PBytesTransferred); out UInt32 PBytesTransferred);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)] [return: MarshalAs(UnmanagedType.U1)]
internal delegate Boolean FspFileSystemAddEa(
IntPtr SingleEa,
IntPtr Ea,
UInt32 EaLength,
out UInt32 PBytesTransferred);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
internal delegate Boolean FspFileSystemAcquireDirectoryBuffer( internal delegate Boolean FspFileSystemAcquireDirectoryBuffer(
ref IntPtr PDirBuffer, ref IntPtr PDirBuffer,
[MarshalAs(UnmanagedType.U1)] Boolean Reset, [MarshalAs(UnmanagedType.U1)] Boolean Reset,
@ -737,6 +819,7 @@ namespace Fsp.Interop
internal static Proto.FspFileSystemResolveReparsePoints FspFileSystemResolveReparsePoints; internal static Proto.FspFileSystemResolveReparsePoints FspFileSystemResolveReparsePoints;
internal static Proto.FspFileSystemCanReplaceReparsePoint _FspFileSystemCanReplaceReparsePoint; internal static Proto.FspFileSystemCanReplaceReparsePoint _FspFileSystemCanReplaceReparsePoint;
internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo; internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo;
internal static Proto.FspFileSystemAddEa _FspFileSystemAddEa;
internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer; internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer;
internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer; internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer;
internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer; internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer;
@ -806,6 +889,62 @@ namespace Fsp.Interop
return _FspFileSystemAddStreamInfo(IntPtr.Zero, Buffer, Length, out PBytesTransferred); return _FspFileSystemAddStreamInfo(IntPtr.Zero, Buffer, Length, out PBytesTransferred);
} }
internal delegate Int32 EnumerateEa(
Object FileNode,
Object FileDesc,
ref Object Context,
String EaName,
Byte[] EaValue,
Boolean NeedEa);
internal static unsafe Int32 FspFileSystemEnumerateEa(
Object FileNode,
Object FileDesc,
EnumerateEa EnumerateEa,
IntPtr Ea,
UInt32 EaLength)
{
Object Context = null;
FullEaInformation *P = (FullEaInformation *)Ea;
FullEaInformation *EndP = (FullEaInformation *)(Ea.ToInt64() + EaLength);
Int32 Result;
Result = 0/*STATUS_SUCCESS*/;
for (; EndP > P;
P = 0 != P->NextEntryOffset ?
(FullEaInformation *)(((IntPtr)P).ToInt64() + P->NextEntryOffset) :
EndP)
{
String EaName = Marshal.PtrToStringAnsi((IntPtr)P->EaName, P->EaNameLength);
Byte[] EaValue = null;
if (0 != P->EaValueLength)
{
EaValue = new Byte[P->EaValueLength];
Marshal.Copy((IntPtr)(((IntPtr)P->EaName).ToInt64() + P->EaNameLength + 1),
EaValue, 0, P->EaValueLength);
}
Boolean NeedEa = 0 != (0x80/*FILE_NEED_EA*/ & P->Flags);
Result = EnumerateEa(FileNode, FileDesc, ref Context, EaName, EaValue, NeedEa);
if (0 > Result)
break;
}
return Result;
}
internal static unsafe Boolean FspFileSystemAddEa(
ref FullEaInformation EaInfo,
IntPtr Buffer,
UInt32 Length,
out UInt32 PBytesTransferred)
{
fixed (FullEaInformation *P = &EaInfo)
return _FspFileSystemAddEa((IntPtr)P, Buffer, Length, out PBytesTransferred);
}
internal static unsafe Boolean FspFileSystemEndEa(
IntPtr Buffer,
UInt32 Length,
out UInt32 PBytesTransferred)
{
return _FspFileSystemAddEa(IntPtr.Zero, Buffer, Length, out PBytesTransferred);
}
internal unsafe static Object GetUserContext( internal unsafe static Object GetUserContext(
IntPtr NativePtr) IntPtr NativePtr)
{ {
@ -1074,6 +1213,7 @@ namespace Fsp.Interop
FspFileSystemResolveReparsePoints = GetEntryPoint<Proto.FspFileSystemResolveReparsePoints>(Module); FspFileSystemResolveReparsePoints = GetEntryPoint<Proto.FspFileSystemResolveReparsePoints>(Module);
_FspFileSystemCanReplaceReparsePoint = GetEntryPoint<Proto.FspFileSystemCanReplaceReparsePoint>(Module); _FspFileSystemCanReplaceReparsePoint = GetEntryPoint<Proto.FspFileSystemCanReplaceReparsePoint>(Module);
_FspFileSystemAddStreamInfo = GetEntryPoint<Proto.FspFileSystemAddStreamInfo>(Module); _FspFileSystemAddStreamInfo = GetEntryPoint<Proto.FspFileSystemAddStreamInfo>(Module);
_FspFileSystemAddEa = GetEntryPoint<Proto.FspFileSystemAddEa>(Module);
FspFileSystemAcquireDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemAcquireDirectoryBuffer>(Module); FspFileSystemAcquireDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemAcquireDirectoryBuffer>(Module);
FspFileSystemFillDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemFillDirectoryBuffer>(Module); FspFileSystemFillDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemFillDirectoryBuffer>(Module);
FspFileSystemReleaseDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemReleaseDirectoryBuffer>(Module); FspFileSystemReleaseDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemReleaseDirectoryBuffer>(Module);

View File

@ -278,7 +278,7 @@ static NTSTATUS FspFsvolCreateNoLock(
ACCESS_MASK GrantedAccess = AccessState->PreviouslyGrantedAccess; ACCESS_MASK GrantedAccess = AccessState->PreviouslyGrantedAccess;
USHORT ShareAccess = IrpSp->Parameters.Create.ShareAccess; USHORT ShareAccess = IrpSp->Parameters.Create.ShareAccess;
PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer; PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer;
//ULONG EaLength = IrpSp->Parameters.Create.EaLength; ULONG EaLength = IrpSp->Parameters.Create.EaLength;
ULONG Flags = IrpSp->Flags; ULONG Flags = IrpSp->Flags;
KPROCESSOR_MODE RequestorMode = KPROCESSOR_MODE RequestorMode =
FlagOn(Flags, SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode; FlagOn(Flags, SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode;
@ -302,10 +302,23 @@ static NTSTATUS FspFsvolCreateNoLock(
if (FlagOn(CreateOptions, FILE_OPEN_BY_FILE_ID)) if (FlagOn(CreateOptions, FILE_OPEN_BY_FILE_ID))
return STATUS_NOT_IMPLEMENTED; return STATUS_NOT_IMPLEMENTED;
/* no EA support currently */ /* was an EA buffer specified? */
if (0 != EaBuffer) if (0 != EaBuffer)
{
/* does the file system support EA? */
if (!FsvolDeviceExtension->VolumeParams.ExtendedAttributes)
return STATUS_EAS_NOT_SUPPORTED; return STATUS_EAS_NOT_SUPPORTED;
/* do we need EA knowledge? */
if (FlagOn(CreateOptions, FILE_NO_EA_KNOWLEDGE))
return STATUS_ACCESS_DENIED;
/* is the EA buffer valid? */
Result = FspEaBufferAndNamesValid(EaBuffer, EaLength, (PULONG)&Irp->IoStatus.Information);
if (!NT_SUCCESS(Result))
return Result;
}
/* cannot open a paging file */ /* cannot open a paging file */
if (FlagOn(Flags, SL_OPEN_PAGING_FILE)) if (FlagOn(Flags, SL_OPEN_PAGING_FILE))
return STATUS_ACCESS_DENIED; return STATUS_ACCESS_DENIED;
@ -541,6 +554,10 @@ static NTSTATUS FspFsvolCreateNoLock(
SecurityDescriptorSize = 0; SecurityDescriptorSize = 0;
FileAttributes = 0; FileAttributes = 0;
/* cannot set EA on named stream */
EaBuffer = 0;
EaLength = 0;
/* remember the main file node */ /* remember the main file node */
ASSERT(0 == FileNode->MainFileNode); ASSERT(0 == FileNode->MainFileNode);
FileNode->MainFileNode = FileDesc->MainFileObject->FsContext; FileNode->MainFileNode = FileDesc->MainFileObject->FsContext;
@ -558,7 +575,9 @@ static NTSTATUS FspFsvolCreateNoLock(
} }
/* create the user-mode file system request */ /* create the user-mode file system request */
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, SecurityDescriptorSize, Result = FspIopCreateRequestEx(Irp, &FileNode->FileName,
0 != EaBuffer ?
FSP_FSCTL_DEFAULT_ALIGN_UP(SecurityDescriptorSize) + EaLength : SecurityDescriptorSize,
FspFsvolCreateRequestFini, &Request); FspFsvolCreateRequestFini, &Request);
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
{ {
@ -584,19 +603,22 @@ static NTSTATUS FspFsvolCreateNoLock(
FspIopRequestContext(Request, RequestFileDesc) = FileDesc; FspIopRequestContext(Request, RequestFileDesc) = FileDesc;
/* populate the Create request */ /* populate the Create request */
#define NEXTOFS(B) ((B).Offset + FSP_FSCTL_DEFAULT_ALIGN_UP((B).Size))
Request->Kind = FspFsctlTransactCreateKind; Request->Kind = FspFsctlTransactCreateKind;
Request->Req.Create.CreateOptions = CreateOptions; Request->Req.Create.CreateOptions = CreateOptions;
Request->Req.Create.FileAttributes = FileAttributes; Request->Req.Create.FileAttributes = FileAttributes;
Request->Req.Create.SecurityDescriptor.Offset = 0 == SecurityDescriptorSize ? 0 : Request->Req.Create.SecurityDescriptor.Offset = 0 != SecurityDescriptorSize ?
FSP_FSCTL_DEFAULT_ALIGN_UP(Request->FileName.Size); NEXTOFS(Request->FileName) : 0;
Request->Req.Create.SecurityDescriptor.Size = (UINT16)SecurityDescriptorSize; Request->Req.Create.SecurityDescriptor.Size = (UINT16)SecurityDescriptorSize;
Request->Req.Create.AllocationSize = AllocationSize; Request->Req.Create.AllocationSize = AllocationSize;
Request->Req.Create.AccessToken = 0; Request->Req.Create.AccessToken = 0;
Request->Req.Create.DesiredAccess = DesiredAccess; Request->Req.Create.DesiredAccess = DesiredAccess;
Request->Req.Create.GrantedAccess = GrantedAccess; Request->Req.Create.GrantedAccess = GrantedAccess;
Request->Req.Create.ShareAccess = ShareAccess; Request->Req.Create.ShareAccess = ShareAccess;
Request->Req.Create.Ea.Offset = 0; Request->Req.Create.Ea.Offset = 0 != EaBuffer ?
Request->Req.Create.Ea.Size = 0; (0 != Request->Req.Create.SecurityDescriptor.Offset ?
NEXTOFS(Request->Req.Create.SecurityDescriptor) : NEXTOFS(Request->FileName)) : 0;
Request->Req.Create.Ea.Size = 0 != EaBuffer ? (UINT16)EaLength : 0;
Request->Req.Create.UserMode = UserMode == RequestorMode; Request->Req.Create.UserMode = UserMode == RequestorMode;
Request->Req.Create.HasTraversePrivilege = HasTraversePrivilege; Request->Req.Create.HasTraversePrivilege = HasTraversePrivilege;
Request->Req.Create.HasBackupPrivilege = HasBackupPrivilege; Request->Req.Create.HasBackupPrivilege = HasBackupPrivilege;
@ -605,6 +627,7 @@ static NTSTATUS FspFsvolCreateNoLock(
Request->Req.Create.CaseSensitive = CaseSensitive; Request->Req.Create.CaseSensitive = CaseSensitive;
Request->Req.Create.HasTrailingBackslash = HasTrailingBackslash; Request->Req.Create.HasTrailingBackslash = HasTrailingBackslash;
Request->Req.Create.NamedStream = MainFileName.Length; Request->Req.Create.NamedStream = MainFileName.Length;
#undef APPEND
Request->Req.Create.AcceptsSecurityDescriptor = 0 == Request->Req.Create.NamedStream && Request->Req.Create.AcceptsSecurityDescriptor = 0 == Request->Req.Create.NamedStream &&
!!FsvolDeviceExtension->VolumeParams.AllowOpenInKernelMode; !!FsvolDeviceExtension->VolumeParams.AllowOpenInKernelMode;
@ -618,6 +641,11 @@ static NTSTATUS FspFsvolCreateNoLock(
RtlCopyMemory(Request->Buffer + Request->Req.Create.SecurityDescriptor.Offset, RtlCopyMemory(Request->Buffer + Request->Req.Create.SecurityDescriptor.Offset,
SecurityDescriptor, SecurityDescriptorSize); SecurityDescriptor, SecurityDescriptorSize);
/* copy the EA buffer (if any) into the request */
if (0 != EaBuffer)
RtlCopyMemory(Request->Buffer + Request->Req.Create.Ea.Offset,
EaBuffer, EaLength);
/* fix FileNode->FileName if we are doing SL_OPEN_TARGET_DIRECTORY */ /* fix FileNode->FileName if we are doing SL_OPEN_TARGET_DIRECTORY */
if (Request->Req.Create.OpenTargetDirectory) if (Request->Req.Create.OpenTargetDirectory)
{ {
@ -1081,6 +1109,7 @@ NTSTATUS FspFsvolCreateComplete(
} }
PVOID RequestDeviceObjectValue = FspIopRequestContext(Request, RequestDeviceObject); PVOID RequestDeviceObjectValue = FspIopRequestContext(Request, RequestDeviceObject);
FSP_FSCTL_TRANSACT_BUF Ea = Request->Req.Create.Ea;
/* disassociate the FileDesc momentarily from the Request */ /* disassociate the FileDesc momentarily from the Request */
FspIopRequestContext(Request, RequestDeviceObject) = 0; FspIopRequestContext(Request, RequestDeviceObject) = 0;
@ -1101,6 +1130,7 @@ NTSTATUS FspFsvolCreateComplete(
Request->Req.Overwrite.FileAttributes = FileAttributes; Request->Req.Overwrite.FileAttributes = FileAttributes;
Request->Req.Overwrite.AllocationSize = AllocationSize; Request->Req.Overwrite.AllocationSize = AllocationSize;
Request->Req.Overwrite.Supersede = FILE_SUPERSEDED == Response->IoStatus.Information; Request->Req.Overwrite.Supersede = FILE_SUPERSEDED == Response->IoStatus.Information;
Request->Req.Overwrite.Ea = Ea;
/* /*
* Post it as BestEffort. * Post it as BestEffort.
@ -1141,8 +1171,16 @@ NTSTATUS FspFsvolCreateComplete(
if (0 == FileNode->MainFileNode) if (0 == FileNode->MainFileNode)
FspFileNodeOverwriteStreams(FileNode); FspFileNodeOverwriteStreams(FileNode);
FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.Overwrite.FileInfo, TRUE); FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.Overwrite.FileInfo, TRUE);
if (0 == FileNode->MainFileNode && FsvolDeviceExtension->VolumeParams.ExtendedAttributes)
{
/* invalidate any existing EA and increment the EA change count */
FspFileNodeSetEa(FileNode, 0, 0);
FileNode->EaChangeCount++;
}
FspFileNodeNotifyChange(FileNode, FspFileNodeNotifyChange(FileNode,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |
(0 == FileNode->MainFileNode && FsvolDeviceExtension->VolumeParams.ExtendedAttributes ?
FILE_NOTIFY_CHANGE_EA : 0),
FILE_ACTION_MODIFIED, FILE_ACTION_MODIFIED,
FALSE); FALSE);

View File

@ -317,7 +317,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject)
NTSTATUS Result; NTSTATUS Result;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
LARGE_INTEGER IrpTimeout; LARGE_INTEGER IrpTimeout;
LARGE_INTEGER SecurityTimeout, DirInfoTimeout, StreamInfoTimeout; LARGE_INTEGER SecurityTimeout, DirInfoTimeout, StreamInfoTimeout, EaTimeout;
/* /*
* Volume device initialization is a mess, because of the different ways of * Volume device initialization is a mess, because of the different ways of
@ -379,6 +379,16 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject)
return Result; return Result;
FsvolDeviceExtension->InitDoneStrm = 1; FsvolDeviceExtension->InitDoneStrm = 1;
/* create our EA meta cache */
EaTimeout.QuadPart = FspTimeoutFromMillis(FsvolDeviceExtension->VolumeParams.EaTimeout);
/* convert millis to nanos */
Result = FspMetaCacheCreate(
FspFsvolDeviceEaCacheCapacity, FspFsvolDeviceEaCacheItemSizeMax, &EaTimeout,
&FsvolDeviceExtension->EaCache);
if (!NT_SUCCESS(Result))
return Result;
FsvolDeviceExtension->InitDoneEa = 1;
/* initialize the FSRTL Notify mechanism */ /* initialize the FSRTL Notify mechanism */
Result = FspNotifyInitializeSync(&FsvolDeviceExtension->NotifySync); Result = FspNotifyInitializeSync(&FsvolDeviceExtension->NotifySync);
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
@ -449,6 +459,10 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject)
FspNotifyUninitializeSync(&FsvolDeviceExtension->NotifySync); FspNotifyUninitializeSync(&FsvolDeviceExtension->NotifySync);
} }
/* delete the EA meta cache */
if (FsvolDeviceExtension->InitDoneEa)
FspMetaCacheDelete(FsvolDeviceExtension->EaCache);
/* delete the stream info meta cache */ /* delete the stream info meta cache */
if (FsvolDeviceExtension->InitDoneStrm) if (FsvolDeviceExtension->InitDoneStrm)
FspMetaCacheDelete(FsvolDeviceExtension->StreamInfoCache); FspMetaCacheDelete(FsvolDeviceExtension->StreamInfoCache);

View File

@ -452,12 +452,17 @@ enum
BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength, BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength,
PUNICODE_STRING StreamPart, PULONG StreamType); PUNICODE_STRING StreamPart, PULONG StreamType);
BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength); BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength);
BOOLEAN FspEaNameIsValid(PSTRING Name);
VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix);
#if 0 #if 0
NTSTATUS FspFileNameUpcase( NTSTATUS FspFileNameUpcase(
PUNICODE_STRING DestinationName, PUNICODE_STRING DestinationName,
PUNICODE_STRING SourceName, PUNICODE_STRING SourceName,
PCWCH UpcaseTable); PCWCH UpcaseTable);
VOID FspEaNameUpcase(
PSTRING DestinationName,
PSTRING SourceName,
PCWCH UpcaseTable);
LONG FspFileNameCompare( LONG FspFileNameCompare(
PUNICODE_STRING Name1, PUNICODE_STRING Name1,
PUNICODE_STRING Name2, PUNICODE_STRING Name2,
@ -470,6 +475,7 @@ BOOLEAN FspFileNameIsPrefix(
PCWCH UpcaseTable); PCWCH UpcaseTable);
#else #else
#define FspFileNameUpcase(D,S,U) (ASSERT(0 == (U)), RtlUpcaseUnicodeString(D,S,FALSE)) #define FspFileNameUpcase(D,S,U) (ASSERT(0 == (U)), RtlUpcaseUnicodeString(D,S,FALSE))
#define FspEaNameUpcase(D,S,U) (ASSERT(0 == (U)), RtlUpperString(D,S))
#define FspFileNameCompare(N1,N2,I,U) (ASSERT(0 == (U)), RtlCompareUnicodeString(N1,N2,I)) #define FspFileNameCompare(N1,N2,I,U) (ASSERT(0 == (U)), RtlCompareUnicodeString(N1,N2,I))
#define FspFileNameIsPrefix(N1,N2,I,U) (ASSERT(0 == (U)), RtlPrefixUnicodeString(N1,N2,I)) #define FspFileNameIsPrefix(N1,N2,I,U) (ASSERT(0 == (U)), RtlPrefixUnicodeString(N1,N2,I))
#endif #endif
@ -509,6 +515,10 @@ NTSTATUS FspCcFlushCache(PSECTION_OBJECT_POINTERS SectionObjectPointer,
NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength,
PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor); PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor);
NTSTATUS FspEaBufferAndNamesValid(
PFILE_FULL_EA_INFORMATION Buffer,
ULONG Length,
PULONG PErrorOffset);
NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync); NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync);
NTSTATUS FspNotifyFullChangeDirectory( NTSTATUS FspNotifyFullChangeDirectory(
PNOTIFY_SYNC NotifySync, PNOTIFY_SYNC NotifySync,
@ -567,6 +577,8 @@ NTSTATUS FspOplockFsctrl(
FspNotifyFullChangeDirectory(NS, NL, FC, 0, 0, FALSE, 0, 0, 0, 0) FspNotifyFullChangeDirectory(NS, NL, FC, 0, 0, FALSE, 0, 0, 0, 0)
#define FspNotifyReportChange(NS, NL, FN, FO, NP, F, A)\ #define FspNotifyReportChange(NS, NL, FN, FO, NP, F, A)\
FspNotifyFullReportChange(NS, NL, (PSTRING)(FN), FO, 0, (PSTRING)(NP), F, A, 0) FspNotifyFullReportChange(NS, NL, (PSTRING)(FN), FO, 0, (PSTRING)(NP), F, A, 0)
#define FSP_NEXT_EA(Ea, EaEnd) \
(0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd))
/* utility: synchronous work queue */ /* utility: synchronous work queue */
typedef struct typedef struct
@ -997,6 +1009,8 @@ enum
FspFsvolDeviceDirInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), FspFsvolDeviceDirInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE),
FspFsvolDeviceStreamInfoCacheCapacity = 100, FspFsvolDeviceStreamInfoCacheCapacity = 100,
FspFsvolDeviceStreamInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), FspFsvolDeviceStreamInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE),
FspFsvolDeviceEaCacheCapacity = 100,
FspFsvolDeviceEaCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE),
}; };
typedef struct typedef struct
{ {
@ -1029,7 +1043,7 @@ typedef struct
typedef struct typedef struct
{ {
FSP_DEVICE_EXTENSION Base; FSP_DEVICE_EXTENSION Base;
UINT32 InitDoneFsvrt:1, InitDoneIoq:1, InitDoneSec:1, InitDoneDir:1, InitDoneStrm:1, UINT32 InitDoneFsvrt:1, InitDoneIoq:1, InitDoneSec:1, InitDoneDir:1, InitDoneStrm:1, InitDoneEa:1,
InitDoneCtxTab:1, InitDoneTimer:1, InitDoneInfo:1, InitDoneNotify:1, InitDoneStat:1; InitDoneCtxTab:1, InitDoneTimer:1, InitDoneInfo:1, InitDoneNotify:1, InitDoneStat:1;
PDEVICE_OBJECT FsctlDeviceObject; PDEVICE_OBJECT FsctlDeviceObject;
PDEVICE_OBJECT FsvrtDeviceObject; PDEVICE_OBJECT FsvrtDeviceObject;
@ -1043,6 +1057,7 @@ typedef struct
FSP_META_CACHE *SecurityCache; FSP_META_CACHE *SecurityCache;
FSP_META_CACHE *DirInfoCache; FSP_META_CACHE *DirInfoCache;
FSP_META_CACHE *StreamInfoCache; FSP_META_CACHE *StreamInfoCache;
FSP_META_CACHE *EaCache;
KSPIN_LOCK ExpirationLock; KSPIN_LOCK ExpirationLock;
WORK_QUEUE_ITEM ExpirationWorkItem; WORK_QUEUE_ITEM ExpirationWorkItem;
BOOLEAN ExpirationInProgress; BOOLEAN ExpirationInProgress;
@ -1271,6 +1286,9 @@ typedef struct FSP_FILE_NODE
ULONG SecurityChangeNumber; ULONG SecurityChangeNumber;
ULONG DirInfoChangeNumber; ULONG DirInfoChangeNumber;
ULONG StreamInfoChangeNumber; ULONG StreamInfoChangeNumber;
UINT64 Ea;
ULONG EaChangeNumber;
ULONG EaChangeCount;
BOOLEAN TruncateOnClose; BOOLEAN TruncateOnClose;
FILE_LOCK FileLock; FILE_LOCK FileLock;
#if (NTDDI_VERSION < NTDDI_WIN8) #if (NTDDI_VERSION < NTDDI_WIN8)
@ -1309,6 +1327,8 @@ typedef struct
UNICODE_STRING DirectoryMarker; UNICODE_STRING DirectoryMarker;
UINT64 DirInfo; UINT64 DirInfo;
ULONG DirInfoCacheHint; ULONG DirInfoCacheHint;
ULONG EaIndex;
ULONG EaChangeCount;
/* stream support */ /* stream support */
HANDLE MainFileHandle; HANDLE MainFileHandle;
PFILE_OBJECT MainFileObject; PFILE_OBJECT MainFileObject;
@ -1439,6 +1459,17 @@ ULONG FspFileNodeStreamInfoChangeNumber(FSP_FILE_NODE *FileNode)
return FileNode->StreamInfoChangeNumber; return FileNode->StreamInfoChangeNumber;
} }
VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode); VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode);
BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize);
VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
ULONG EaChangeNumber);
static inline
ULONG FspFileNodeEaChangeNumber(FSP_FILE_NODE *FileNode)
{
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
return FileNode->EaChangeNumber;
}
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
BOOLEAN InvalidateCaches); BOOLEAN InvalidateCaches);
NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp);
@ -1471,6 +1502,7 @@ NTSTATUS FspMainFileClose(
#define FspFileNodeDereferenceSecurity(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceSecurity(P) FspMetaCacheDereferenceItemBuffer(P)
#define FspFileNodeDereferenceDirInfo(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceDirInfo(P) FspMetaCacheDereferenceItemBuffer(P)
#define FspFileNodeDereferenceStreamInfo(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceStreamInfo(P) FspMetaCacheDereferenceItemBuffer(P)
#define FspFileNodeDereferenceEa(P) FspMetaCacheDereferenceItemBuffer(P)
#define FspFileNodeUnlockAll(N,F,P) FsRtlFastUnlockAll(&(N)->FileLock, F, P, N) #define FspFileNodeUnlockAll(N,F,P) FsRtlFastUnlockAll(&(N)->FileLock, F, P, N)
#if (NTDDI_VERSION < NTDDI_WIN8) #if (NTDDI_VERSION < NTDDI_WIN8)
#define FspFileNodeAddrOfOplock(N) (&(N)->Oplock) #define FspFileNodeAddrOfOplock(N) (&(N)->Oplock)

View File

@ -21,30 +21,385 @@
#include <sys/driver.h> #include <sys/driver.h>
static VOID FspFsvolQueryEaGetCopy(
BOOLEAN CasePreservedExtendedAttributes,
BOOLEAN ReturnSingleEntry,
PFILE_GET_EA_INFORMATION GetBufBgn, ULONG GetSize,
PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize,
PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize,
PIO_STATUS_BLOCK IoStatus);
static VOID FspFsvolQueryEaIndexCopy(
BOOLEAN CasePreservedExtendedAttributes,
BOOLEAN ReturnSingleEntry,
BOOLEAN IndexSpecified, PULONG PEaIndex,
PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize,
PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize,
PIO_STATUS_BLOCK IoStatus);
static VOID FspFsvolQueryEaCopy(
BOOLEAN CasePreservedExtendedAttributes,
PIO_STACK_LOCATION IrpSp,
PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize,
PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize,
PIO_STATUS_BLOCK IoStatus);
static NTSTATUS FspFsvolQueryEa( static NTSTATUS FspFsvolQueryEa(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_IOCMPL_DISPATCH FspFsvolQueryEaComplete; FSP_IOCMPL_DISPATCH FspFsvolQueryEaComplete;
static FSP_IOP_REQUEST_FINI FspFsvolQueryEaRequestFini;
static NTSTATUS FspFsvolSetEa( static NTSTATUS FspFsvolSetEa(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_IOCMPL_DISPATCH FspFsvolSetEaComplete; FSP_IOCMPL_DISPATCH FspFsvolSetEaComplete;
static FSP_IOP_REQUEST_FINI FspFsvolSetEaRequestFini;
FSP_DRIVER_DISPATCH FspQueryEa; FSP_DRIVER_DISPATCH FspQueryEa;
FSP_DRIVER_DISPATCH FspSetEa; FSP_DRIVER_DISPATCH FspSetEa;
#ifdef ALLOC_PRAGMA #ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFsvolQueryEaGetCopy)
#pragma alloc_text(PAGE, FspFsvolQueryEaIndexCopy)
#pragma alloc_text(PAGE, FspFsvolQueryEa) #pragma alloc_text(PAGE, FspFsvolQueryEa)
#pragma alloc_text(PAGE, FspFsvolQueryEaComplete) #pragma alloc_text(PAGE, FspFsvolQueryEaComplete)
#pragma alloc_text(PAGE, FspFsvolQueryEaRequestFini)
#pragma alloc_text(PAGE, FspFsvolSetEa) #pragma alloc_text(PAGE, FspFsvolSetEa)
#pragma alloc_text(PAGE, FspFsvolSetEaComplete) #pragma alloc_text(PAGE, FspFsvolSetEaComplete)
#pragma alloc_text(PAGE, FspFsvolSetEaRequestFini)
#pragma alloc_text(PAGE, FspQueryEa) #pragma alloc_text(PAGE, FspQueryEa)
#pragma alloc_text(PAGE, FspSetEa) #pragma alloc_text(PAGE, FspSetEa)
#endif #endif
static NTSTATUS FspFsvolQueryEa( enum
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) {
/* QueryEa */
RequestFileNode = 0,
RequestEaChangeNumber = 1,
/* SetEa */
//RequestFileNode = 0,
};
static VOID FspFsvolQueryEaGetCopy(
BOOLEAN CasePreservedExtendedAttributes,
BOOLEAN ReturnSingleEntry,
PFILE_GET_EA_INFORMATION GetBufBgn, ULONG GetSize,
PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize,
PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize,
PIO_STATUS_BLOCK IoStatus)
{ {
PAGED_CODE(); PAGED_CODE();
PFILE_GET_EA_INFORMATION GetBuf, GetBufEnd = (PVOID)((PUINT8)GetBufBgn + GetSize);
PFILE_GET_EA_INFORMATION Get;
PFILE_FULL_EA_INFORMATION SrcBuf, SrcBufEnd = (PVOID)((PUINT8)SrcBufBgn + SrcSize);
PFILE_FULL_EA_INFORMATION DstBuf, DstBufEnd = (PVOID)((PUINT8)DstBufBgn + DstSize);
PFILE_FULL_EA_INFORMATION PrevDstBuf;
PFILE_FULL_EA_INFORMATION Src;
STRING GetName, Name;
ULONG CopyLength;
IoStatus->Information = 0;
DstBuf = DstBufBgn, PrevDstBuf = 0;
for (GetBuf = GetBufBgn; GetBufEnd > GetBuf; GetBuf = FSP_NEXT_EA(GetBuf, GetBufEnd))
{
GetName.Length = GetName.MaximumLength = GetBuf->EaNameLength;
GetName.Buffer = GetBuf->EaName;
/* ignore duplicate names */
for (Get = GetBufBgn; GetBuf > Get; Get = (PVOID)((PUINT8)Get + Get->NextEntryOffset))
{
Name.Length = Name.MaximumLength = Get->EaNameLength;
Name.Buffer = Get->EaName;
if (RtlEqualString(&GetName, &Name, TRUE/* always case-insensitive */))
break;
}
if (GetBuf > Get)
continue;
if (!FspEaNameIsValid(&GetName))
{
IoStatus->Status = STATUS_INVALID_EA_NAME;
IoStatus->Information = (ULONG)((PUINT8)GetBuf - (PUINT8)GetBufBgn);
return;
}
Src = 0;
for (SrcBuf = SrcBufBgn; SrcBufEnd > SrcBuf; SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd))
{
Name.Length = Name.MaximumLength = SrcBuf->EaNameLength;
Name.Buffer = SrcBuf->EaName;
if (RtlEqualString(&GetName, &Name, TRUE/* always case-insensitive */))
{
Src = SrcBuf;
break;
}
}
if (0 != Src)
CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
Src->EaNameLength + 1 + Src->EaValueLength;
else
CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
GetBuf->EaNameLength + 1;
if ((PUINT8)DstBuf + CopyLength > (PUINT8)DstBufEnd)
{
IoStatus->Status = STATUS_BUFFER_OVERFLOW;
IoStatus->Information = 0;
return;
}
if (0 != Src)
RtlMoveMemory(DstBuf, Src, CopyLength);
else
{
DstBuf->Flags = 0;
DstBuf->EaNameLength = GetBuf->EaNameLength;
DstBuf->EaValueLength = 0;
RtlCopyMemory(DstBuf->EaName, GetBuf->EaName, GetBuf->EaNameLength + 1);
}
DstBuf->NextEntryOffset = 0;
if (!CasePreservedExtendedAttributes)
{
Name.Length = Name.MaximumLength = DstBuf->EaNameLength;
Name.Buffer = DstBuf->EaName;
FspEaNameUpcase(&Name, &Name, 0);
}
if (0 != PrevDstBuf)
PrevDstBuf->NextEntryOffset = (ULONG)((PUINT8)DstBuf - (PUINT8)PrevDstBuf);
PrevDstBuf = DstBuf;
IoStatus->Information = (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn + CopyLength);
DstBuf = (PVOID)((PUINT8)DstBuf + FSP_FSCTL_ALIGN_UP(CopyLength, sizeof(ULONG)));
if (ReturnSingleEntry)
break;
}
IoStatus->Status = STATUS_SUCCESS;
}
static VOID FspFsvolQueryEaIndexCopy(
BOOLEAN CasePreservedExtendedAttributes,
BOOLEAN ReturnSingleEntry,
BOOLEAN IndexSpecified, PULONG PEaIndex,
PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize,
PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize,
PIO_STATUS_BLOCK IoStatus)
{
PAGED_CODE();
ULONG EaIndex = 1;
PFILE_FULL_EA_INFORMATION SrcBuf, SrcBufEnd = (PVOID)((PUINT8)SrcBufBgn + SrcSize);
PFILE_FULL_EA_INFORMATION DstBuf, DstBufEnd = (PVOID)((PUINT8)DstBufBgn + DstSize);
PFILE_FULL_EA_INFORMATION PrevDstBuf;
STRING Name;
ULONG CopyLength;
if (IndexSpecified && 0 == *PEaIndex)
{
IoStatus->Status = STATUS_NONEXISTENT_EA_ENTRY;
IoStatus->Information = 0;
return;
}
for (SrcBuf = SrcBufBgn; EaIndex < *PEaIndex && SrcBufEnd > SrcBuf;
SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd))
EaIndex++;
IoStatus->Information = 0;
DstBuf = DstBufBgn, PrevDstBuf = 0;
for (; SrcBufEnd > SrcBuf; SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd))
{
CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
((PFILE_FULL_EA_INFORMATION)SrcBuf)->EaNameLength + 1 +
((PFILE_FULL_EA_INFORMATION)SrcBuf)->EaValueLength;
if ((PUINT8)DstBuf + CopyLength > (PUINT8)DstBufEnd)
{
if (0 != PrevDstBuf)
break;
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
IoStatus->Information = 0;
return;
}
RtlMoveMemory(DstBuf, SrcBuf, CopyLength);
DstBuf->NextEntryOffset = 0;
if (!CasePreservedExtendedAttributes)
{
Name.Length = Name.MaximumLength = DstBuf->EaNameLength;
Name.Buffer = DstBuf->EaName;
FspEaNameUpcase(&Name, &Name, 0);
}
if (0 != PrevDstBuf)
PrevDstBuf->NextEntryOffset = (ULONG)((PUINT8)DstBuf - (PUINT8)PrevDstBuf);
PrevDstBuf = DstBuf;
IoStatus->Information = (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn + CopyLength);
DstBuf = (PVOID)((PUINT8)DstBuf + FSP_FSCTL_ALIGN_UP(CopyLength, sizeof(ULONG)));
EaIndex++;
if (ReturnSingleEntry)
break;
}
if (IndexSpecified)
{
if (0 != PrevDstBuf)
{
*PEaIndex = EaIndex;
IoStatus->Status = SrcBufEnd > SrcBuf && !ReturnSingleEntry ?
STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
}
else
{
IoStatus->Status = *PEaIndex == EaIndex ?
STATUS_NO_MORE_EAS : STATUS_NONEXISTENT_EA_ENTRY;
IoStatus->Information = 0;
}
}
else
{
if (0 != PrevDstBuf)
{
*PEaIndex = EaIndex;
IoStatus->Status = SrcBufEnd > SrcBuf && !ReturnSingleEntry ?
STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
}
else
{
IoStatus->Status = SrcBufBgn == SrcBuf ?
STATUS_NO_EAS_ON_FILE : STATUS_NO_MORE_EAS;
IoStatus->Information = 0;
}
}
}
static VOID FspFsvolQueryEaCopy(
BOOLEAN CasePreservedExtendedAttributes,
PIO_STACK_LOCATION IrpSp,
PFILE_FULL_EA_INFORMATION SrcBufBgn, ULONG SrcSize,
PFILE_FULL_EA_INFORMATION DstBufBgn, ULONG DstSize,
PIO_STATUS_BLOCK IoStatus)
{
PAGED_CODE();
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
BOOLEAN RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
BOOLEAN IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
BOOLEAN ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
PFILE_GET_EA_INFORMATION EaList = IrpSp->Parameters.QueryEa.EaList;
ULONG EaListLength = IrpSp->Parameters.QueryEa.EaListLength;
ULONG EaIndex;
ASSERT(FileNode == FileDesc->FileNode);
if (0 != EaList)
{
FspFsvolQueryEaGetCopy(
CasePreservedExtendedAttributes,
ReturnSingleEntry,
EaList, EaListLength,
SrcBufBgn, SrcSize,
DstBufBgn, DstSize,
IoStatus);
}
else
{
if (!IndexSpecified &&
!RestartScan &&
0 != FileDesc->EaIndex &&
FileNode->EaChangeCount != FileDesc->EaChangeCount)
{
IoStatus->Status = STATUS_EA_CORRUPT_ERROR;
IoStatus->Information = 0;
return;
}
if (IndexSpecified)
EaIndex = IrpSp->Parameters.QueryEa.EaIndex;
else if (RestartScan)
EaIndex = 0;
else
EaIndex = FileDesc->EaIndex;
FspFsvolQueryEaIndexCopy(
CasePreservedExtendedAttributes,
ReturnSingleEntry,
IndexSpecified, &EaIndex,
SrcBufBgn, SrcSize,
DstBufBgn, DstSize,
IoStatus);
if (NT_SUCCESS(IoStatus->Status) || STATUS_BUFFER_OVERFLOW == IoStatus->Status)
{
FileDesc->EaIndex = EaIndex;
FileDesc->EaChangeCount = FileNode->EaChangeCount;
}
}
}
static NTSTATUS FspFsvolQueryEa(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
/* is this a valid FileObject? */
if (!FspFileNodeIsValid(IrpSp->FileObject->FsContext))
return STATUS_INVALID_DEVICE_REQUEST; return STATUS_INVALID_DEVICE_REQUEST;
NTSTATUS Result;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
PVOID Buffer = Irp->UserBuffer;
ULONG Length = IrpSp->Parameters.QueryEa.Length;
PVOID EaBuffer;
ULONG EaBufferSize;
FSP_FSCTL_TRANSACT_REQ *Request;
ASSERT(FileNode == FileDesc->FileNode);
FspFileNodeAcquireExclusive(FileNode, Main);
if (FspFileNodeReferenceEa(FileNode, &EaBuffer, &EaBufferSize))
{
FspFsvolQueryEaCopy(
!!FsvolDeviceExtension->VolumeParams.CasePreservedExtendedAttributes,
IrpSp,
EaBuffer, EaBufferSize,
Buffer, Length,
&Irp->IoStatus);
FspFileNodeDereferenceEa(EaBuffer);
FspFileNodeRelease(FileNode, Main);
return Irp->IoStatus.Status;
}
FspFileNodeConvertExclusiveToShared(FileNode, Main);
FspFileNodeAcquireShared(FileNode, Pgio);
Result = FspBufferUserBuffer(Irp, Length, IoWriteAccess);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolQueryEaRequestFini, &Request);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Request->Kind = FspFsctlTransactQueryEaKind;
Request->Req.QueryEa.UserContext = FileNode->UserContext;
Request->Req.QueryEa.UserContext2 = FileDesc->UserContext2;
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestFileNode) = FileNode;
return FSP_STATUS_IOQ_POST;
} }
NTSTATUS FspFsvolQueryEaComplete( NTSTATUS FspFsvolQueryEaComplete(
@ -52,15 +407,147 @@ NTSTATUS FspFsvolQueryEaComplete(
{ {
FSP_ENTER_IOC(PAGED_CODE()); FSP_ENTER_IOC(PAGED_CODE());
FSP_LEAVE_IOC("%s", ""); if (!NT_SUCCESS(Response->IoStatus.Status))
{
Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status;
FSP_RETURN();
} }
static NTSTATUS FspFsvolSetEa( PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject;
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
PVOID Buffer = Irp->AssociatedIrp.SystemBuffer;
ULONG Length = IrpSp->Parameters.QueryEa.Length;
PVOID EaBuffer = 0;
ULONG EaBufferSize = 0;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
BOOLEAN Success;
if (0 != FspIopRequestContext(Request, RequestFileNode))
{
/* check that the EA buffer we got back is valid */
if (Response->Buffer + Response->Rsp.QueryEa.Ea.Size >
(PUINT8)Response + Response->Size)
{
Irp->IoStatus.Information = 0;
Result = STATUS_EA_LIST_INCONSISTENT;
FSP_RETURN();
}
Irp->IoStatus.Information = 0;
Result = IoCheckEaBufferValidity((PVOID)Response->Buffer, Response->Rsp.QueryEa.Ea.Size,
(PULONG)&Irp->IoStatus.Information);
if (!NT_SUCCESS(Result))
FSP_RETURN();
FspIopRequestContext(Request, RequestEaChangeNumber) = (PVOID)
FspFileNodeEaChangeNumber(FileNode);
FspIopRequestContext(Request, RequestFileNode) = 0;
FspFileNodeReleaseOwner(FileNode, Full, Request);
}
Success = DEBUGTEST(90) && FspFileNodeTryAcquireExclusive(FileNode, Main);
if (!Success)
{
FspIopRetryCompleteIrp(Irp, Response, &Result);
FSP_RETURN();
}
Success = !FspFileNodeTrySetEa(FileNode,
Response->Buffer, Response->Rsp.QueryEa.Ea.Size,
(ULONG)(UINT_PTR)FspIopRequestContext(Request, RequestEaChangeNumber));
Success = Success && FspFileNodeReferenceEa(FileNode, &EaBuffer, &EaBufferSize);
if (Success)
{
FspFsvolQueryEaCopy(
!!FsvolDeviceExtension->VolumeParams.CasePreservedExtendedAttributes,
IrpSp,
EaBuffer, EaBufferSize,
Buffer, Length,
&Irp->IoStatus);
FspFileNodeDereferenceEa(EaBuffer);
}
else
{
EaBuffer = (PVOID)Response->Buffer;
EaBufferSize = Response->Rsp.QueryEa.Ea.Size;
FspFsvolQueryEaCopy(
!!FsvolDeviceExtension->VolumeParams.CasePreservedExtendedAttributes,
IrpSp,
EaBuffer, EaBufferSize,
Buffer, Length,
&Irp->IoStatus);
}
FspFileNodeRelease(FileNode, Main);
Result = Irp->IoStatus.Status;
FSP_LEAVE_IOC("FileObject=%p",
IrpSp->FileObject);
}
static VOID FspFsvolQueryEaRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4])
{ {
PAGED_CODE(); PAGED_CODE();
FSP_FILE_NODE *FileNode = Context[RequestFileNode];
if (0 != FileNode)
FspFileNodeReleaseOwner(FileNode, Full, Request);
}
static NTSTATUS FspFsvolSetEa(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
/* is this a valid FileObject? */
if (!FspFileNodeIsValid(IrpSp->FileObject->FsContext))
return STATUS_INVALID_DEVICE_REQUEST; return STATUS_INVALID_DEVICE_REQUEST;
NTSTATUS Result;
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
PVOID Buffer;
ULONG Length = IrpSp->Parameters.SetEa.Length;
FSP_FSCTL_TRANSACT_REQ *Request;
ASSERT(FileNode == FileDesc->FileNode);
Result = FspBufferUserBuffer(Irp, Length, IoReadAccess);
if (!NT_SUCCESS(Result))
return Result;
Buffer = Irp->AssociatedIrp.SystemBuffer;
Result = FspEaBufferAndNamesValid(Buffer, Length, (PULONG)&Irp->IoStatus.Information);
if (!NT_SUCCESS(Result))
return Result;
FspFileNodeAcquireExclusive(FileNode, Full);
Result = FspIopCreateRequestEx(Irp, 0, Length, FspFsvolSetEaRequestFini,
&Request);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Request->Kind = FspFsctlTransactSetEaKind;
Request->Req.SetEa.UserContext = FileNode->UserContext;
Request->Req.SetEa.UserContext2 = FileDesc->UserContext2;
Request->Req.SetEa.Ea.Offset = 0;
Request->Req.SetEa.Ea.Size = (UINT16)Length;
RtlCopyMemory(Request->Buffer, Buffer, Length);
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestFileNode) = FileNode;
return FSP_STATUS_IOQ_POST;
} }
NTSTATUS FspFsvolSetEaComplete( NTSTATUS FspFsvolSetEaComplete(
@ -68,7 +555,64 @@ NTSTATUS FspFsvolSetEaComplete(
{ {
FSP_ENTER_IOC(PAGED_CODE()); FSP_ENTER_IOC(PAGED_CODE());
FSP_LEAVE_IOC("%s", ""); if (!NT_SUCCESS(Response->IoStatus.Status))
{
Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status;
FSP_RETURN();
}
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
BOOLEAN Valid;
Valid = FALSE;
if (0 < Response->Rsp.SetEa.Ea.Size &&
Response->Buffer + Response->Rsp.SetEa.Ea.Size <=
(PUINT8)Response + Response->Size)
{
Irp->IoStatus.Information = 0;
Result = IoCheckEaBufferValidity((PVOID)Response->Buffer, Response->Rsp.QueryEa.Ea.Size,
(PULONG)&Irp->IoStatus.Information);
Valid = NT_SUCCESS(Result);
}
/* if the EA buffer that we got back is valid */
if (Valid)
{
/* update the cached EA */
FspFileNodeSetEa(FileNode,
Response->Buffer, Response->Rsp.SetEa.Ea.Size);
}
else
{
/* invalidate the cached EA */
FspFileNodeSetEa(FileNode, 0, 0);
}
FileNode->EaChangeCount++;
FspFileNodeNotifyChange(FileNode, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, FALSE);
FspIopRequestContext(Request, RequestFileNode) = 0;
FspFileNodeReleaseOwner(FileNode, Full, Request);
Irp->IoStatus.Information = 0;
Result = STATUS_SUCCESS;
FSP_LEAVE_IOC("FileObject=%p",
IrpSp->FileObject);
}
static VOID FspFsvolSetEaRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4])
{
PAGED_CODE();
FSP_FILE_NODE *FileNode = Context[RequestFileNode];
if (0 != FileNode)
FspFileNodeReleaseOwner(FileNode, Full, Request);
} }
NTSTATUS FspQueryEa( NTSTATUS FspQueryEa(
@ -84,7 +628,8 @@ NTSTATUS FspQueryEa(
FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST);
} }
FSP_LEAVE_MJ("%s", ""); FSP_LEAVE_MJ("FileObject=%p",
IrpSp->FileObject);
} }
NTSTATUS FspSetEa( NTSTATUS FspSetEa(
@ -100,5 +645,6 @@ NTSTATUS FspSetEa(
FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST);
} }
FSP_LEAVE_MJ("%s", ""); FSP_LEAVE_MJ("FileObject=%p",
IrpSp->FileObject);
} }

View File

@ -89,6 +89,10 @@ VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size
BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
ULONG StreamInfoChangeNumber); ULONG StreamInfoChangeNumber);
VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode); VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode);
BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize);
VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
ULONG EaChangeNumber);
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
BOOLEAN InvalidateCaches); BOOLEAN InvalidateCaches);
NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp);
@ -158,6 +162,9 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
// !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo)
// !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo)
// !#pragma alloc_text(PAGE, FspFileNodeInvalidateStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeInvalidateStreamInfo)
#pragma alloc_text(PAGE, FspFileNodeReferenceEa)
#pragma alloc_text(PAGE, FspFileNodeSetEa)
#pragma alloc_text(PAGE, FspFileNodeTrySetEa)
#pragma alloc_text(PAGE, FspFileNodeNotifyChange) #pragma alloc_text(PAGE, FspFileNodeNotifyChange)
#pragma alloc_text(PAGE, FspFileNodeProcessLockIrp) #pragma alloc_text(PAGE, FspFileNodeProcessLockIrp)
#pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp) #pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp)
@ -361,6 +368,7 @@ VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode)
FsRtlTeardownPerStreamContexts(&FileNode->Header); FsRtlTeardownPerStreamContexts(&FileNode->Header);
FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea);
FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo);
FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo);
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security); FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security);
@ -2115,6 +2123,48 @@ VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode)
FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, StreamInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, StreamInfo);
} }
BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize)
{
PAGED_CODE();
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->EaCache,
FileNode->Ea, PBuffer, PSize);
}
VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size)
{
PAGED_CODE();
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea);
FileNode->Ea = 0 != Buffer ?
FspMetaCacheAddItem(FsvolDeviceExtension->EaCache, Buffer, Size) : 0;
FileNode->EaChangeNumber++;
}
BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
ULONG EaChangeNumber)
{
PAGED_CODE();
if (FspFileNodeEaChangeNumber(FileNode) != EaChangeNumber)
return FALSE;
FspFileNodeSetEa(FileNode, Buffer, Size);
return TRUE;
}
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
BOOLEAN InvalidateCaches) BOOLEAN InvalidateCaches)
{ {

View File

@ -24,6 +24,7 @@
BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength, BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength,
PUNICODE_STRING StreamPart, PULONG StreamType); PUNICODE_STRING StreamPart, PULONG StreamType);
BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength); BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength);
BOOLEAN FspEaNameIsValid(PSTRING Name);
VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix);
NTSTATUS FspFileNameInExpression( NTSTATUS FspFileNameInExpression(
PUNICODE_STRING Expression, PUNICODE_STRING Expression,
@ -35,6 +36,7 @@ NTSTATUS FspFileNameInExpression(
#ifdef ALLOC_PRAGMA #ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFileNameIsValid) #pragma alloc_text(PAGE, FspFileNameIsValid)
#pragma alloc_text(PAGE, FspFileNameIsValidPattern) #pragma alloc_text(PAGE, FspFileNameIsValidPattern)
#pragma alloc_text(PAGE, FspEaNameIsValid)
#pragma alloc_text(PAGE, FspFileNameSuffix) #pragma alloc_text(PAGE, FspFileNameSuffix)
#pragma alloc_text(PAGE, FspFileNameInExpression) #pragma alloc_text(PAGE, FspFileNameInExpression)
#endif #endif
@ -191,6 +193,37 @@ BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Path, ULONG MaxComponentLength
return TRUE; return TRUE;
} }
BOOLEAN FspEaNameIsValid(PSTRING Name)
{
PAGED_CODE();
/* see FastFat's FatIsEaNameValid */
if (0 == Name->Length || Name->Length > 254)
return FALSE;
PSTR NameEnd, NamePtr;
CHAR Char;
NamePtr = Name->Buffer;
NameEnd = NamePtr + Name->Length;
while (NameEnd > NamePtr)
{
Char = *NamePtr;
if (FsRtlIsLeadDbcsCharacter(Char))
NamePtr++;
else
if (!FsRtlIsAnsiCharacterLegalFat(Char, FALSE))
return FALSE;
NamePtr++;
}
return TRUE;
}
VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix) VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix)
{ {
PAGED_CODE(); PAGED_CODE();

View File

@ -49,6 +49,10 @@ NTSTATUS FspCcFlushCache(PSECTION_OBJECT_POINTERS SectionObjectPointer,
NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength,
PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor); PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor);
NTSTATUS FspEaBufferAndNamesValid(
PFILE_FULL_EA_INFORMATION Buffer,
ULONG Length,
PULONG PErrorOffset);
NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync); NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync);
NTSTATUS FspNotifyFullChangeDirectory( NTSTATUS FspNotifyFullChangeDirectory(
PNOTIFY_SYNC NotifySync, PNOTIFY_SYNC NotifySync,
@ -129,6 +133,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
#pragma alloc_text(PAGE, FspCcMdlWriteComplete) #pragma alloc_text(PAGE, FspCcMdlWriteComplete)
#pragma alloc_text(PAGE, FspCcFlushCache) #pragma alloc_text(PAGE, FspCcFlushCache)
#pragma alloc_text(PAGE, FspQuerySecurityDescriptorInfo) #pragma alloc_text(PAGE, FspQuerySecurityDescriptorInfo)
#pragma alloc_text(PAGE, FspEaBufferAndNamesValid)
#pragma alloc_text(PAGE, FspNotifyInitializeSync) #pragma alloc_text(PAGE, FspNotifyInitializeSync)
#pragma alloc_text(PAGE, FspNotifyFullChangeDirectory) #pragma alloc_text(PAGE, FspNotifyFullChangeDirectory)
#pragma alloc_text(PAGE, FspNotifyFullReportChange) #pragma alloc_text(PAGE, FspNotifyFullReportChange)
@ -578,6 +583,39 @@ NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation
return STATUS_BUFFER_TOO_SMALL == Result ? STATUS_BUFFER_OVERFLOW : Result; return STATUS_BUFFER_TOO_SMALL == Result ? STATUS_BUFFER_OVERFLOW : Result;
} }
NTSTATUS FspEaBufferAndNamesValid(
PFILE_FULL_EA_INFORMATION Buffer,
ULONG Length,
PULONG PErrorOffset)
{
PAGED_CODE();
NTSTATUS Result;
*PErrorOffset = 0;
Result = IoCheckEaBufferValidity(Buffer, Length, PErrorOffset);
if (!NT_SUCCESS(Result))
return Result;
for (PFILE_FULL_EA_INFORMATION Ea = Buffer, EaEnd = (PVOID)((PUINT8)Ea + Length);
EaEnd > Ea; Ea = FSP_NEXT_EA(Ea, EaEnd))
{
STRING Name;
Name.Length = Name.MaximumLength = Ea->EaNameLength;
Name.Buffer = Ea->EaName;
if (!FspEaNameIsValid(&Name))
{
*PErrorOffset = (ULONG)((PUINT8)Ea - (PUINT8)Buffer);
return STATUS_INVALID_EA_NAME;
}
}
return STATUS_SUCCESS;
}
NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync) NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync)
{ {
PAGED_CODE(); PAGED_CODE();

View File

@ -96,7 +96,7 @@ static NTSTATUS FspFsvolQueryFsAttributeInformation(
(FsvolDeviceExtension->VolumeParams.ReparsePoints ? FILE_SUPPORTS_REPARSE_POINTS : 0) | (FsvolDeviceExtension->VolumeParams.ReparsePoints ? FILE_SUPPORTS_REPARSE_POINTS : 0) |
(FsvolDeviceExtension->VolumeParams.NamedStreams ? FILE_NAMED_STREAMS : 0) | (FsvolDeviceExtension->VolumeParams.NamedStreams ? FILE_NAMED_STREAMS : 0) |
//(FsvolDeviceExtension->VolumeParams.HardLinks ? FILE_SUPPORTS_HARD_LINKS : 0) | //(FsvolDeviceExtension->VolumeParams.HardLinks ? FILE_SUPPORTS_HARD_LINKS : 0) |
//(FsvolDeviceExtension->VolumeParams.ExtendedAttributes ? FILE_SUPPORTS_EXTENDED_ATTRIBUTES : 0) | (FsvolDeviceExtension->VolumeParams.ExtendedAttributes ? FILE_SUPPORTS_EXTENDED_ATTRIBUTES : 0) |
(FsvolDeviceExtension->VolumeParams.ReadOnlyVolume ? FILE_READ_ONLY_VOLUME : 0); (FsvolDeviceExtension->VolumeParams.ReadOnlyVolume ? FILE_READ_ONLY_VOLUME : 0);
Info->MaximumComponentNameLength = FsvolDeviceExtension->VolumeParams.MaxComponentLength; Info->MaximumComponentNameLength = FsvolDeviceExtension->VolumeParams.MaxComponentLength;

View File

@ -151,6 +151,7 @@ static NTSTATUS FspVolumeCreateNoLock(
VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout;
VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout;
VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout;
VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout;
} }
else else
{ {
@ -162,11 +163,14 @@ static NTSTATUS FspVolumeCreateNoLock(
VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout;
if (!VolumeParams.StreamInfoTimeoutValid) if (!VolumeParams.StreamInfoTimeoutValid)
VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout;
if (!VolumeParams.EaTimeoutValid)
VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout;
} }
VolumeParams.VolumeInfoTimeoutValid = 1; VolumeParams.VolumeInfoTimeoutValid = 1;
VolumeParams.DirInfoTimeoutValid = 1; VolumeParams.DirInfoTimeoutValid = 1;
VolumeParams.SecurityTimeoutValid = 1; VolumeParams.SecurityTimeoutValid = 1;
VolumeParams.StreamInfoTimeoutValid = 1; VolumeParams.StreamInfoTimeoutValid = 1;
VolumeParams.EaTimeoutValid = 1;
if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType)
{ {
VolumeParams.Prefix[sizeof VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0'; VolumeParams.Prefix[sizeof VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0';

7
tools/cloc.bat Executable file
View File

@ -0,0 +1,7 @@
@echo off
setlocal
cd %~dp0..
cloc --force-lang=c,i --fullpath "--not-match-d=ext/test|build/VStudio/.vs|build/VStudio/build" .

View File

@ -55,6 +55,12 @@ namespace memfs
} }
} }
struct EaValueData
{
public Byte[] EaValue;
public Boolean NeedEa;
}
class FileNode class FileNode
{ {
public FileNode(String FileName) public FileNode(String FileName)
@ -80,6 +86,13 @@ namespace memfs
return FileInfo; return FileInfo;
} }
} }
public SortedDictionary<String, EaValueData> GetEaMap(Boolean Force)
{
FileNode FileNode = null == MainFileNode ? this : MainFileNode;
if (null == EaMap && Force)
EaMap = new SortedDictionary<String, EaValueData>(StringComparer.OrdinalIgnoreCase);
return EaMap;
}
private static UInt64 IndexNumber = 1; private static UInt64 IndexNumber = 1;
public String FileName; public String FileName;
@ -87,6 +100,7 @@ namespace memfs
public Byte[] FileSecurity; public Byte[] FileSecurity;
public Byte[] FileData; public Byte[] FileData;
public Byte[] ReparseData; public Byte[] ReparseData;
private SortedDictionary<String, EaValueData> EaMap;
public FileNode MainFileNode; public FileNode MainFileNode;
public int OpenCount; public int OpenCount;
} }
@ -259,6 +273,7 @@ namespace memfs
Host.NamedStreams = true; Host.NamedStreams = true;
Host.PostCleanupWhenModifiedOnly = true; Host.PostCleanupWhenModifiedOnly = true;
Host.PassQueryDirectoryFileName = true; Host.PassQueryDirectoryFileName = true;
Host.ExtendedAttributes = true;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -309,13 +324,15 @@ namespace memfs
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
public override Int32 Create( public override Int32 CreateEx(
String FileName, String FileName,
UInt32 CreateOptions, UInt32 CreateOptions,
UInt32 GrantedAccess, UInt32 GrantedAccess,
UInt32 FileAttributes, UInt32 FileAttributes,
Byte[] SecurityDescriptor, Byte[] SecurityDescriptor,
UInt64 AllocationSize, UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
out Object FileNode0, out Object FileNode0,
out Object FileDesc, out Object FileDesc,
out FileInfo FileInfo, out FileInfo FileInfo,
@ -352,6 +369,12 @@ namespace memfs
FileNode.FileInfo.FileAttributes = 0 != (FileAttributes & (UInt32)System.IO.FileAttributes.Directory) ? FileNode.FileInfo.FileAttributes = 0 != (FileAttributes & (UInt32)System.IO.FileAttributes.Directory) ?
FileAttributes : FileAttributes | (UInt32)System.IO.FileAttributes.Archive; FileAttributes : FileAttributes | (UInt32)System.IO.FileAttributes.Archive;
FileNode.FileSecurity = SecurityDescriptor; FileNode.FileSecurity = SecurityDescriptor;
if (IntPtr.Zero != Ea)
{
Result = SetEa(FileNode, null, Ea, EaLength);
if (0 > Result)
return Result;
}
if (0 != AllocationSize) if (0 != AllocationSize)
{ {
Result = SetFileSizeInternal(FileNode, AllocationSize, true); Result = SetFileSizeInternal(FileNode, AllocationSize, true);
@ -393,6 +416,18 @@ namespace memfs
return Result; return Result;
} }
if (0 != (CreateOptions & FILE_NO_EA_KNOWLEDGE) &&
null == FileNode.MainFileNode)
{
SortedDictionary<String, EaValueData> EaMap = FileNode.GetEaMap(false);
if (null != EaMap)
{
foreach (KeyValuePair<String, EaValueData> Pair in EaMap)
if (Pair.Value.NeedEa)
return STATUS_ACCESS_DENIED;
}
}
Interlocked.Increment(ref FileNode.OpenCount); Interlocked.Increment(ref FileNode.OpenCount);
FileNode0 = FileNode; FileNode0 = FileNode;
FileInfo = FileNode.GetFileInfo(); FileInfo = FileNode.GetFileInfo();
@ -401,12 +436,14 @@ namespace memfs
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
public override Int32 Overwrite( public override Int32 OverwriteEx(
Object FileNode0, Object FileNode0,
Object FileDesc, Object FileDesc,
UInt32 FileAttributes, UInt32 FileAttributes,
Boolean ReplaceFileAttributes, Boolean ReplaceFileAttributes,
UInt64 AllocationSize, UInt64 AllocationSize,
IntPtr Ea,
UInt32 EaLength,
out FileInfo FileInfo) out FileInfo FileInfo)
{ {
FileInfo = default(FileInfo); FileInfo = default(FileInfo);
@ -424,6 +461,16 @@ namespace memfs
FileNodeMap.Remove(StreamNode); FileNodeMap.Remove(StreamNode);
} }
SortedDictionary<String, EaValueData> EaMap = FileNode.GetEaMap(false);
if (null != EaMap)
EaMap.Clear();
if (IntPtr.Zero != Ea)
{
Result = SetEa(FileNode, null, Ea, EaLength);
if (0 > Result)
return Result;
}
Result = SetFileSizeInternal(FileNode, AllocationSize, true); Result = SetFileSizeInternal(FileNode, AllocationSize, true);
if (0 > Result) if (0 > Result)
return Result; return Result;
@ -1019,6 +1066,67 @@ namespace memfs
StreamAllocationSize = default(UInt64); StreamAllocationSize = default(UInt64);
return false; return false;
} }
public override Boolean GetEaEntry(
Object FileNode0,
Object FileDesc,
ref Object Context,
out String EaName,
out Byte[] EaValue,
out Boolean NeedEa)
{
FileNode FileNode = (FileNode)FileNode0;
IEnumerator<KeyValuePair<String, EaValueData>> Enumerator =
(IEnumerator<KeyValuePair<String, EaValueData>>)Context;
if (null == Enumerator)
{
SortedDictionary<String, EaValueData> EaMap = FileNode.GetEaMap(false);
if (null == EaMap)
{
EaName = default(String);
EaValue = default(Byte[]);
NeedEa = default(Boolean);
return false;
}
Context = Enumerator = EaMap.GetEnumerator();
}
while (Enumerator.MoveNext())
{
KeyValuePair<String, EaValueData> Pair = Enumerator.Current;
EaName = Pair.Key;
EaValue = Pair.Value.EaValue;
NeedEa = Pair.Value.NeedEa;
return true;
}
EaName = default(String);
EaValue = default(Byte[]);
NeedEa = default(Boolean);
return false;
}
public override Int32 SetEaEntry(
Object FileNode0,
Object FileDesc,
ref Object Context,
String EaName,
Byte[] EaValue,
Boolean NeedEa)
{
FileNode FileNode = (FileNode)FileNode0;
SortedDictionary<String, EaValueData> EaMap = FileNode.GetEaMap(true);
if (null != EaValue)
{
EaValueData Data;
Data.EaValue = EaValue;
Data.NeedEa = NeedEa;
EaMap[EaName] = Data;
}
else
EaMap.Remove(EaName);
return STATUS_SUCCESS;
}
private FileNodeMap FileNodeMap; private FileNodeMap FileNodeMap;
private UInt32 MaxFileNodes; private UInt32 MaxFileNodes;

View File

@ -64,6 +64,11 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH,
*/ */
#define MEMFS_CONTROL #define MEMFS_CONTROL
/*
* Define the MEMFS_EA macro to include extended attributes support.
*/
#define MEMFS_EA
/* /*
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes * Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
* a check for the Write buffer to ensure that it is read-only. * a check for the Write buffer to ensure that it is read-only.
@ -237,6 +242,36 @@ BOOLEAN MemfsFileNameHasPrefix(PWSTR a, PWSTR b, BOOLEAN CaseInsensitive)
#endif #endif
} }
#if defined(MEMFS_EA)
static inline
int MemfsEaNameCompare(PSTR a, PSTR b)
{
/* EA names are always case-insensitive in MEMFS (to be inline with NTFS) */
int res;
res = CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, a, -1, b, -1);
if (0 != res)
res -= 2;
else
res = _stricmp(a, b);
return res;
}
struct MEMFS_FILE_NODE_EA_LESS
{
MEMFS_FILE_NODE_EA_LESS()
{
}
bool operator()(PSTR a, PSTR b) const
{
return 0 > MemfsEaNameCompare(a, b);
}
};
typedef std::map<PSTR, FILE_FULL_EA_INFORMATION *, MEMFS_FILE_NODE_EA_LESS> MEMFS_FILE_NODE_EA_MAP;
#endif
typedef struct _MEMFS_FILE_NODE typedef struct _MEMFS_FILE_NODE
{ {
WCHAR FileName[MEMFS_MAX_PATH]; WCHAR FileName[MEMFS_MAX_PATH];
@ -247,6 +282,9 @@ typedef struct _MEMFS_FILE_NODE
#if defined(MEMFS_REPARSE_POINTS) #if defined(MEMFS_REPARSE_POINTS)
SIZE_T ReparseDataSize; SIZE_T ReparseDataSize;
PVOID ReparseData; PVOID ReparseData;
#endif
#if defined(MEMFS_EA)
MEMFS_FILE_NODE_EA_MAP *EaMap;
#endif #endif
volatile LONG RefCount; volatile LONG RefCount;
#if defined(MEMFS_NAMED_STREAMS) #if defined(MEMFS_NAMED_STREAMS)
@ -308,9 +346,27 @@ NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode)
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
#if defined(MEMFS_EA)
static inline
VOID MemfsFileNodeDeleteEaMap(MEMFS_FILE_NODE *FileNode)
{
if (0 != FileNode->EaMap)
{
for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end();
p != q; ++p)
free(p->second);
delete FileNode->EaMap;
FileNode->EaMap = 0;
}
}
#endif
static inline static inline
VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode)
{ {
#if defined(MEMFS_EA)
MemfsFileNodeDeleteEaMap(FileNode);
#endif
#if defined(MEMFS_REPARSE_POINTS) #if defined(MEMFS_REPARSE_POINTS)
free(FileNode->ReparseData); free(FileNode->ReparseData);
#endif #endif
@ -351,6 +407,121 @@ VOID MemfsFileNodeGetFileInfo(MEMFS_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *Fi
#endif #endif
} }
#if defined(MEMFS_EA)
static inline
NTSTATUS MemfsFileNodeGetEaMap(MEMFS_FILE_NODE *FileNode, MEMFS_FILE_NODE_EA_MAP **PEaMap)
{
#if defined(MEMFS_NAMED_STREAMS)
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
#endif
*PEaMap = FileNode->EaMap;
if (0 != *PEaMap)
return STATUS_SUCCESS;
try
{
*PEaMap = FileNode->EaMap = new MEMFS_FILE_NODE_EA_MAP(MEMFS_FILE_NODE_EA_LESS());
return STATUS_SUCCESS;
}
catch (...)
{
*PEaMap = 0;
return STATUS_INSUFFICIENT_RESOURCES;
}
}
static inline
NTSTATUS MemfsFileNodeSetEa(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PFILE_FULL_EA_INFORMATION Ea)
{
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)Context;
MEMFS_FILE_NODE_EA_MAP *EaMap;
FILE_FULL_EA_INFORMATION *FileNodeEa = 0;
MEMFS_FILE_NODE_EA_MAP::iterator p;
NTSTATUS Result;
Result = MemfsFileNodeGetEaMap(FileNode, &EaMap);
if (!NT_SUCCESS(Result))
return Result;
if (0 != Ea->EaValueLength)
{
FileNodeEa = (FILE_FULL_EA_INFORMATION *)malloc(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + Ea->EaNameLength + 1 + Ea->EaValueLength);
if (0 == FileNodeEa)
return STATUS_INSUFFICIENT_RESOURCES;
memcpy(FileNodeEa, Ea,
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + Ea->EaNameLength + 1 + Ea->EaValueLength);
FileNodeEa->NextEntryOffset = 0;
}
p = EaMap->find(Ea->EaName);
if (p != EaMap->end())
{
free(p->second);
EaMap->erase(p);
}
if (0 != Ea->EaValueLength)
{
try
{
EaMap->insert(MEMFS_FILE_NODE_EA_MAP::value_type(FileNodeEa->EaName, FileNodeEa));
return STATUS_SUCCESS;
}
catch (...)
{
free(FileNodeEa);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
return STATUS_SUCCESS;
}
static inline
BOOLEAN MemfsFileNodeNeedEa(MEMFS_FILE_NODE *FileNode)
{
#if defined(MEMFS_NAMED_STREAMS)
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
#endif
if (0 != FileNode->EaMap)
{
for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end();
p != q; ++p)
if (0 != (p->second->Flags & FILE_NEED_EA))
return TRUE;
}
return FALSE;
}
static inline
BOOLEAN MemfsFileNodeEnumerateEa(MEMFS_FILE_NODE *FileNode,
BOOLEAN (*EnumFn)(PFILE_FULL_EA_INFORMATION Ea, PVOID), PVOID Context)
{
#if defined(MEMFS_NAMED_STREAMS)
if (0 != FileNode->MainFileNode)
FileNode = FileNode->MainFileNode;
#endif
if (0 != FileNode->EaMap)
{
for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end();
p != q; ++p)
if (!EnumFn(p->second, Context))
return FALSE;
}
return TRUE;
}
#endif
static inline static inline
VOID MemfsFileNodeMapDump(MEMFS_FILE_NODE_MAP *FileNodeMap) VOID MemfsFileNodeMapDump(MEMFS_FILE_NODE_MAP *FileNodeMap)
{ {
@ -869,6 +1040,9 @@ static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,
#if defined(MEMFS_EA)
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
#endif
PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo) PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo)
{ {
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
@ -948,6 +1122,18 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
memcpy(FileNode->FileSecurity, SecurityDescriptor, FileNode->FileSecuritySize); memcpy(FileNode->FileSecurity, SecurityDescriptor, FileNode->FileSecuritySize);
} }
#if defined(MEMFS_EA)
if (0 != Ea)
{
Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength);
if (!NT_SUCCESS(Result))
{
MemfsFileNodeDelete(FileNode);
return Result;
}
}
#endif
FileNode->FileInfo.AllocationSize = AllocationSize; FileNode->FileInfo.AllocationSize = AllocationSize;
if (0 != FileNode->FileInfo.AllocationSize) if (0 != FileNode->FileInfo.AllocationSize)
{ {
@ -1005,6 +1191,22 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
return Result; return Result;
} }
#if defined(MEMFS_EA)
/* if the OP specified no EA's check the need EA count, but only if accessing main stream */
if (0 != (CreateOptions & FILE_NO_EA_KNOWLEDGE)
#if defined(MEMFS_NAMED_STREAMS)
&& (0 == FileNode->MainFileNode)
#endif
)
{
if (MemfsFileNodeNeedEa(FileNode))
{
Result = STATUS_ACCESS_DENIED;
return Result;
}
}
#endif
MemfsFileNodeReference(FileNode); MemfsFileNodeReference(FileNode);
*PFileNode = FileNode; *PFileNode = FileNode;
MemfsFileNodeGetFileInfo(FileNode, FileInfo); MemfsFileNodeGetFileInfo(FileNode, FileInfo);
@ -1025,6 +1227,9 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, PVOID FileNode0, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
#if defined(MEMFS_EA)
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
#endif
FSP_FSCTL_FILE_INFO *FileInfo) FSP_FSCTL_FILE_INFO *FileInfo)
{ {
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
@ -1047,6 +1252,16 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
MemfsFileNodeMapEnumerateFree(&Context); MemfsFileNodeMapEnumerateFree(&Context);
#endif #endif
#if defined(MEMFS_EA)
MemfsFileNodeDeleteEaMap(FileNode);
if (0 != Ea)
{
Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength);
if (!NT_SUCCESS(Result))
return Result;
}
#endif
Result = SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); Result = SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE);
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
return Result; return Result;
@ -1881,7 +2096,7 @@ static NTSTATUS GetStreamInfo(FSP_FILE_SYSTEM *FileSystem,
#if defined(MEMFS_CONTROL) #if defined(MEMFS_CONTROL)
static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, UINT32 ControlCode, PVOID FileNode, UINT32 ControlCode,
PVOID InputBuffer, ULONG InputBufferLength, PVOID InputBuffer, ULONG InputBufferLength,
PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred) PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred)
{ {
@ -1911,14 +2126,63 @@ static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem,
} }
#endif #endif
#if defined(MEMFS_EA)
typedef struct _MEMFS_GET_EA_CONTEXT
{
PFILE_FULL_EA_INFORMATION Ea;
ULONG EaLength;
PULONG PBytesTransferred;
} MEMFS_GET_EA_CONTEXT;
static BOOLEAN GetEaEnumFn(PFILE_FULL_EA_INFORMATION Ea, PVOID Context0)
{
MEMFS_GET_EA_CONTEXT *Context = (MEMFS_GET_EA_CONTEXT *)Context0;
return FspFileSystemAddEa(Ea,
Context->Ea, Context->EaLength, Context->PBytesTransferred);
}
static NTSTATUS GetEa(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred)
{
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
MEMFS_GET_EA_CONTEXT Context;
Context.Ea = Ea;
Context.EaLength = EaLength;
Context.PBytesTransferred = PBytesTransferred;
if (MemfsFileNodeEnumerateEa(FileNode, GetEaEnumFn, &Context))
FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred);
return STATUS_SUCCESS;
}
static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength)
{
return FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength);
}
#endif
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
{ {
GetVolumeInfo, GetVolumeInfo,
SetVolumeLabel, SetVolumeLabel,
GetSecurityByName, GetSecurityByName,
#if defined(MEMFS_EA)
0,
#else
Create, Create,
#endif
Open, Open,
#if defined(MEMFS_EA)
0,
#else
Overwrite, Overwrite,
#endif
Cleanup, Cleanup,
Close, Close,
Read, Read,
@ -1957,6 +2221,18 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
Control, Control,
#else #else
0, 0,
#endif
0,
#if defined(MEMFS_EA)
Create,
Overwrite,
GetEa,
SetEa
#else
0,
0,
0,
0,
#endif #endif
}; };
@ -2051,6 +2327,9 @@ NTSTATUS MemfsCreateFunnel(
VolumeParams.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup; VolumeParams.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup;
#if defined(MEMFS_CONTROL) #if defined(MEMFS_CONTROL)
VolumeParams.DeviceControl = 1; VolumeParams.DeviceControl = 1;
#endif
#if defined(MEMFS_EA)
VolumeParams.ExtendedAttributes = 1;
#endif #endif
VolumeParams.AllowOpenInKernelMode = 1; VolumeParams.AllowOpenInKernelMode = 1;
if (0 != VolumePrefix) if (0 != VolumePrefix)

988
tst/winfsp-tests/ea-test.c Normal file
View File

@ -0,0 +1,988 @@
/**
* @file ea-test.c
*
* @copyright 2015-2019 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#include <winfsp/winfsp.h>
#include <tlib/testsuite.h>
#include <strsafe.h>
#include "memfs.h"
#include "winfsp-tests.h"
typedef struct _FILE_GET_EA_INFORMATION
{
ULONG NextEntryOffset;
UCHAR EaNameLength;
CHAR EaName[1];
} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION;
NTSYSAPI NTSTATUS NTAPI NtQueryEaFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN BOOLEAN ReturnSingleEntry,
IN PVOID EaList OPTIONAL,
IN ULONG EaListLength,
IN PULONG EaIndex OPTIONAL,
IN BOOLEAN RestartScan);
NTSYSAPI NTSTATUS NTAPI NtSetEaFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID EaBuffer,
IN ULONG EaBufferSize);
BOOLEAN AddGetEa(PFILE_GET_EA_INFORMATION SingleEa,
PFILE_GET_EA_INFORMATION Ea, ULONG Length, PULONG PBytesTransferred)
{
if (0 != SingleEa)
{
PUINT8 EaPtr = (PUINT8)Ea + FSP_FSCTL_ALIGN_UP(*PBytesTransferred, sizeof(ULONG));
PUINT8 EaEnd = (PUINT8)Ea + Length;
ULONG EaLen = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) +
SingleEa->EaNameLength + 1;
if (EaEnd < EaPtr + EaLen)
return FALSE;
memcpy(EaPtr, SingleEa, EaLen);
((PFILE_GET_EA_INFORMATION)EaPtr)->NextEntryOffset = FSP_FSCTL_ALIGN_UP(EaLen, sizeof(ULONG));
*PBytesTransferred = (ULONG)(EaPtr + EaLen - (PUINT8)Ea);
}
else if ((ULONG)FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) <= *PBytesTransferred)
{
PUINT8 EaEnd = (PUINT8)Ea + *PBytesTransferred;
while (EaEnd > (PUINT8)Ea + Ea->NextEntryOffset)
Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset);
Ea->NextEntryOffset = 0;
}
return TRUE;
}
static void ea_init_ea(
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred)
{
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[128];
} SingleEa;
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("Aname1");
SingleEa.V.EaValueLength = (USHORT)strlen("first");
lstrcpyA(SingleEa.V.EaName, "Aname1");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "first", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.Flags = FILE_NEED_EA;
SingleEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
SingleEa.V.EaValueLength = (USHORT)strlen("second");
lstrcpyA(SingleEa.V.EaName, "bnameTwo");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "second", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("Cn3");
SingleEa.V.EaValueLength = (USHORT)strlen("third");
lstrcpyA(SingleEa.V.EaName, "Cn3");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "third", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred);
}
static void ea_init_bad_ea(
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred)
{
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[128];
} SingleEa;
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("Aname1");
SingleEa.V.EaValueLength = (USHORT)strlen("first");
lstrcpyA(SingleEa.V.EaName, "Aname1");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "first", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.Flags = FILE_NEED_EA;
SingleEa.V.EaNameLength = (UCHAR)strlen("bnameTwo*");
SingleEa.V.EaValueLength = (USHORT)strlen("second");
lstrcpyA(SingleEa.V.EaName, "bnameTwo*");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "second", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("Cn3");
SingleEa.V.EaValueLength = (USHORT)strlen("third");
lstrcpyA(SingleEa.V.EaName, "Cn3");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "third", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred);
}
struct ea_check_ea_context
{
ULONG Count;
ULONG EaCount[4];
};
static NTSTATUS ea_check_ea_enumerate(
FSP_FILE_SYSTEM *FileSystem, PVOID Context0,
PFILE_FULL_EA_INFORMATION SingleEa)
{
struct ea_check_ea_context *Context = Context0;
if (0 == strcmp(SingleEa->EaName, "ANAME1"))
{
ASSERT(0 == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("ANAME1"));
ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("first"));
ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "first", SingleEa->EaValueLength));
Context->EaCount[0]++;
}
if (0 == strcmp(SingleEa->EaName, "BNAMETWO"))
{
ASSERT(FILE_NEED_EA == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("BNAMETWO"));
ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("second"));
ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "second", SingleEa->EaValueLength));
Context->EaCount[1]++;
}
if (0 == strcmp(SingleEa->EaName, "CN3"))
{
ASSERT(0 == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("CN3"));
ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("third"));
ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "third", SingleEa->EaValueLength));
Context->EaCount[2]++;
}
if (0 == strcmp(SingleEa->EaName, "NONEXISTENT"))
{
ASSERT(0 == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("NONEXISTENT"));
ASSERT(SingleEa->EaValueLength == 0);
Context->EaCount[3]++;
}
Context->Count++;
return STATUS_SUCCESS;
}
static void ea_check_ea(HANDLE Handle)
{
NTSTATUS Result;
IO_STATUS_BLOCK Iosb;
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[512];
} Ea;
union
{
FILE_GET_EA_INFORMATION V;
UINT8 B[512];
} GetEa;
union
{
FILE_GET_EA_INFORMATION V;
UINT8 B[128];
} SingleGetEa;
ULONG EaLength = 0;
ULONG EaIndex;
struct ea_check_ea_context Context;
EaLength = 0;
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
lstrcpyA(SingleGetEa.V.EaName, "bnameTwo");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
AddGetEa(0, &GetEa.V, sizeof GetEa, &EaLength);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("bnameTwo") + 1),
FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_BUFFER_OVERFLOW == Result);
ASSERT(0 == Iosb.Information);
EaLength = 0;
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("nonexistent");
lstrcpyA(SingleGetEa.V.EaName, "nonexistent");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
AddGetEa(0, &GetEa.V, sizeof GetEa, &EaLength);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(0 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(1 == Context.EaCount[3]);
EaLength = 0;
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
lstrcpyA(SingleGetEa.V.EaName, "bnameTwo");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("nonexistent");
lstrcpyA(SingleGetEa.V.EaName, "nonexistent");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
AddGetEa(0, &GetEa.V, sizeof GetEa, &EaLength);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(2 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(1 == Context.EaCount[3]);
EaLength = 0;
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("Aname1");
lstrcpyA(SingleGetEa.V.EaName, "Aname1");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
lstrcpyA(SingleGetEa.V.EaName, "bnameTwo");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
AddGetEa(0, &GetEa.V, sizeof GetEa, &EaLength);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(2 == Context.Count);
ASSERT(1 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1 + strlen("first")),
FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_BUFFER_OVERFLOW == Result);
ASSERT(0 == Iosb.Information);
EaLength = 0;
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
lstrcpyA(SingleGetEa.V.EaName, "bnameTwo");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
lstrcpyA(SingleGetEa.V.EaName, "bnameTwo");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
AddGetEa(0, &GetEa.V, sizeof GetEa, &EaLength);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
EaLength = 0;
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
lstrcpyA(SingleGetEa.V.EaName, "bnameTwo");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
memset(&SingleGetEa, 0, sizeof SingleGetEa);
SingleGetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo*");
lstrcpyA(SingleGetEa.V.EaName, "bnameTwo*");
AddGetEa(&SingleGetEa.V, &GetEa.V, sizeof GetEa, &EaLength);
AddGetEa(0, &GetEa.V, sizeof GetEa, &EaLength);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, &GetEa.V, EaLength, 0, FALSE);
ASSERT(STATUS_INVALID_EA_NAME == Result);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(3 == Context.Count);
ASSERT(1 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(1 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_NO_MORE_EAS == Result);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, TRUE, 0, 0, 0, TRUE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(1 == Context.EaCount[0]);
ASSERT(0 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, TRUE, 0, 0, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1),
FALSE, 0, 0, 0, TRUE);
ASSERT(STATUS_BUFFER_TOO_SMALL == Result);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1 + strlen("first")),
FALSE, 0, 0, 0, TRUE);
ASSERT(STATUS_BUFFER_OVERFLOW == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(1 == Context.EaCount[0]);
ASSERT(0 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1 + strlen("first")),
FALSE, 0, 0, 0, TRUE);
ASSERT(STATUS_BUFFER_OVERFLOW == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(1 == Context.EaCount[0]);
ASSERT(0 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("bnameTwo") + 1),
FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_BUFFER_TOO_SMALL == Result);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("bnameTwo") + 1 + strlen("bnameTwo")),
FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_BUFFER_OVERFLOW == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Cn3") + 1),
FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_BUFFER_TOO_SMALL == Result);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Cn3") + 1 + strlen("third")),
FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(0 == Context.EaCount[1]);
ASSERT(1 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_NO_MORE_EAS == Result);
memset(&Context, 0, sizeof Context);
EaIndex = 0;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, sizeof Ea,
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_NONEXISTENT_EA_ENTRY == Result);
memset(&Context, 0, sizeof Context);
EaIndex = 1;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1),
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_BUFFER_TOO_SMALL == Result);
memset(&Context, 0, sizeof Context);
EaIndex = 1;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1 + strlen("first")),
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_BUFFER_OVERFLOW == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(1 == Context.EaCount[0]);
ASSERT(0 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
EaIndex = 2;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("bnameTwo") + 1),
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_BUFFER_TOO_SMALL == Result);
memset(&Context, 0, sizeof Context);
EaIndex = 2;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("bnameTwo") + 1 + strlen("bnameTwo")),
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_BUFFER_OVERFLOW == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
EaIndex = 3;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Cn3") + 1),
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_BUFFER_TOO_SMALL == Result);
memset(&Context, 0, sizeof Context);
EaIndex = 3;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Cn3") + 1 + strlen("third")),
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(1 == Context.Count);
ASSERT(0 == Context.EaCount[0]);
ASSERT(0 == Context.EaCount[1]);
ASSERT(1 == Context.EaCount[2]);
ASSERT(0 == Context.EaCount[3]);
memset(&Context, 0, sizeof Context);
EaIndex = 4;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, sizeof Ea,
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_NO_MORE_EAS == Result);
memset(&Context, 0, sizeof Context);
EaIndex = 5;
Result = NtQueryEaFile(Handle, &Iosb,
&Ea, sizeof Ea,
FALSE, 0, 0, &EaIndex, FALSE);
ASSERT(STATUS_NONEXISTENT_EA_ENTRY == Result);
}
static void ea_init_ea2(
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred)
{
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[128];
} SingleEa;
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("Aname1");
SingleEa.V.EaValueLength = (USHORT)strlen("ValueForAname1");
lstrcpyA(SingleEa.V.EaName, "Aname1");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "ValueForAname1", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("bnameTwo");
SingleEa.V.EaValueLength = (USHORT)strlen("ValueForBNameTwo");
lstrcpyA(SingleEa.V.EaName, "bnameTwo");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "ValueForBNameTwo", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("Cn3");
SingleEa.V.EaValueLength = 0;
lstrcpyA(SingleEa.V.EaName, "Cn3");
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
memset(&SingleEa, 0, sizeof SingleEa);
SingleEa.V.EaNameLength = (UCHAR)strlen("dn4");
SingleEa.V.EaValueLength = (USHORT)strlen("ValueForDn4");
lstrcpyA(SingleEa.V.EaName, "dn4");
memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "ValueForDn4", SingleEa.V.EaValueLength);
FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred);
FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred);
}
static NTSTATUS ea_check_ea2_enumerate(
FSP_FILE_SYSTEM *FileSystem, PVOID Context0,
PFILE_FULL_EA_INFORMATION SingleEa)
{
struct ea_check_ea_context *Context = Context0;
if (0 == strcmp(SingleEa->EaName, "ANAME1"))
{
ASSERT(0 == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("ANAME1"));
ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("ValueForAname1"));
ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "ValueForAname1", SingleEa->EaValueLength));
Context->EaCount[0]++;
}
if (0 == strcmp(SingleEa->EaName, "BNAMETWO"))
{
ASSERT(0 == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("BNAMETWO"));
ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("ValueForBNameTwo"));
ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "ValueForBNameTwo", SingleEa->EaValueLength));
Context->EaCount[1]++;
}
if (0 == strcmp(SingleEa->EaName, "CN3"))
{
ASSERT(0 == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("CN3"));
ASSERT(SingleEa->EaValueLength == 0);
Context->EaCount[2]++;
}
if (0 == strcmp(SingleEa->EaName, "DN4"))
{
ASSERT(0 == SingleEa->Flags);
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("DN4"));
ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("ValueForDn4"));
ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "ValueForDn4", SingleEa->EaValueLength));
Context->EaCount[3]++;
}
Context->Count++;
return STATUS_SUCCESS;
}
static void ea_check_ea2(HANDLE Handle)
{
NTSTATUS Result;
IO_STATUS_BLOCK Iosb;
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[512];
} Ea;
struct ea_check_ea_context Context;
memset(&Context, 0, sizeof Context);
Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, TRUE);
ASSERT(STATUS_SUCCESS == Result);
Result = FspFileSystemEnumerateEa(0, ea_check_ea2_enumerate, &Context, &Ea.V, (ULONG)Iosb.Information);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(3 == Context.Count);
ASSERT(1 == Context.EaCount[0]);
ASSERT(1 == Context.EaCount[1]);
ASSERT(0 == Context.EaCount[2]);
ASSERT(1 == Context.EaCount[3]);
}
static void ea_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
HANDLE DirHandle, FileHandle;
NTSTATUS Result;
BOOLEAN Success;
WCHAR FilePath[MAX_PATH];
WCHAR UnicodePathBuf[MAX_PATH] = L"file2";
UNICODE_STRING UnicodePath;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK Iosb;
LARGE_INTEGER LargeZero = { 0 };
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[512];
} Ea;
ULONG EaLength;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Success = CreateDirectoryW(FilePath, 0);
ASSERT(Success);
DirHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != DirHandle);
UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR);
UnicodePath.MaximumLength = sizeof UnicodePathBuf;
UnicodePath.Buffer = UnicodePathBuf;
InitializeObjectAttributes(&Obja, &UnicodePath, 0, DirHandle, 0);
EaLength = 0;
ea_init_bad_ea(&Ea.V, sizeof Ea, &EaLength);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_CREATE, 0,
&Ea, EaLength);
ASSERT(STATUS_INVALID_EA_NAME == Result);
EaLength = 0;
ea_init_ea(&Ea.V, sizeof Ea, &EaLength);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_CREATE, FILE_NO_EA_KNOWLEDGE,
&Ea, EaLength);
ASSERT(STATUS_ACCESS_DENIED == Result);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_CREATE, 0,
&Ea, EaLength);
ASSERT(STATUS_SUCCESS == Result);
CloseHandle(FileHandle);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OPEN, FILE_NO_EA_KNOWLEDGE,
0, 0);
ASSERT(STATUS_ACCESS_DENIED == Result);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OPEN, FILE_DELETE_ON_CLOSE,
0, 0);
ASSERT(STATUS_SUCCESS == Result);
ea_check_ea(FileHandle);
CloseHandle(FileHandle);
CloseHandle(DirHandle);
DirHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE == DirHandle);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
memfs_stop(memfs);
}
static void ea_create_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
ea_create_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
ea_create_dotest(MemfsDisk, 0, 0);
ea_create_dotest(MemfsDisk, 0, 1000);
}
if (WinFspNetTests)
{
ea_create_dotest(MemfsNet, L"\\\\memfs\\share", 0);
ea_create_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
}
}
static void ea_overwrite_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
HANDLE DirHandle, FileHandle;
NTSTATUS Result;
BOOLEAN Success;
WCHAR FilePath[MAX_PATH];
WCHAR UnicodePathBuf[MAX_PATH] = L"file2";
UNICODE_STRING UnicodePath;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK Iosb;
LARGE_INTEGER LargeZero = { 0 };
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[512];
} Ea;
ULONG EaLength;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Success = CreateDirectoryW(FilePath, 0);
ASSERT(Success);
DirHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != DirHandle);
UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR);
UnicodePath.MaximumLength = sizeof UnicodePathBuf;
UnicodePath.Buffer = UnicodePathBuf;
InitializeObjectAttributes(&Obja, &UnicodePath, 0, DirHandle, 0);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_CREATE, 0,
0, 0);
ASSERT(STATUS_SUCCESS == Result);
CloseHandle(FileHandle);
EaLength = 0;
ea_init_ea(&Ea.V, sizeof Ea, &EaLength);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OVERWRITE, 0,
&Ea, EaLength);
ASSERT(STATUS_SUCCESS == Result);
ea_check_ea(FileHandle);
CloseHandle(FileHandle);
EaLength = 0;
ea_init_ea2(&Ea.V, sizeof Ea, &EaLength);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OVERWRITE, FILE_DELETE_ON_CLOSE,
&Ea, EaLength);
ASSERT(STATUS_SUCCESS == Result);
ea_check_ea2(FileHandle);
CloseHandle(FileHandle);
CloseHandle(DirHandle);
DirHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE == DirHandle);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
memfs_stop(memfs);
}
static void ea_overwrite_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
ea_overwrite_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
ea_overwrite_dotest(MemfsDisk, 0, 0);
ea_overwrite_dotest(MemfsDisk, 0, 1000);
}
if (WinFspNetTests)
{
ea_overwrite_dotest(MemfsNet, L"\\\\memfs\\share", 0);
ea_overwrite_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
}
}
static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
HANDLE DirHandle, FileHandle;
NTSTATUS Result;
BOOLEAN Success;
IO_STATUS_BLOCK Iosb;
WCHAR FilePath[MAX_PATH];
union
{
FILE_FULL_EA_INFORMATION V;
UINT8 B[512];
} Ea;
ULONG EaLength;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Success = CreateDirectoryW(FilePath, 0);
ASSERT(Success);
DirHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != DirHandle);
EaLength = 0;
ea_init_bad_ea(&Ea.V, sizeof Ea, &EaLength);
Result = NtSetEaFile(DirHandle, &Iosb, &Ea, EaLength);
ASSERT(STATUS_INVALID_EA_NAME == Result);
EaLength = 0;
ea_init_ea(&Ea.V, sizeof Ea, &EaLength);
Result = NtSetEaFile(DirHandle, &Iosb, &Ea, EaLength);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(0 == Iosb.Information);
ea_check_ea(DirHandle);
EaLength = 0;
ea_init_ea2(&Ea.V, sizeof Ea, &EaLength);
Result = NtSetEaFile(DirHandle, &Iosb, &Ea, EaLength);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(0 == Iosb.Information);
Result = NtQueryEaFile(DirHandle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_EA_CORRUPT_ERROR == Result);
ea_check_ea2(DirHandle);
CloseHandle(DirHandle);
DirHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE == DirHandle);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
FileHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != FileHandle);
EaLength = 0;
ea_init_bad_ea(&Ea.V, sizeof Ea, &EaLength);
Result = NtSetEaFile(FileHandle, &Iosb, &Ea, EaLength);
ASSERT(STATUS_INVALID_EA_NAME == Result);
EaLength = 0;
ea_init_ea(&Ea.V, sizeof Ea, &EaLength);
Result = NtSetEaFile(FileHandle, &Iosb, &Ea, EaLength);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(0 == Iosb.Information);
ea_check_ea(FileHandle);
EaLength = 0;
ea_init_ea2(&Ea.V, sizeof Ea, &EaLength);
Result = NtSetEaFile(FileHandle, &Iosb, &Ea, EaLength);
ASSERT(STATUS_SUCCESS == Result);
ASSERT(0 == Iosb.Information);
Result = NtQueryEaFile(FileHandle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE);
ASSERT(STATUS_EA_CORRUPT_ERROR == Result);
ea_check_ea2(FileHandle);
CloseHandle(FileHandle);
FileHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE == FileHandle);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
memfs_stop(memfs);
}
static void ea_getset_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
ea_getset_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
ea_getset_dotest(MemfsDisk, 0, 0);
ea_getset_dotest(MemfsDisk, 0, 1000);
}
if (WinFspNetTests)
{
ea_getset_dotest(MemfsNet, L"\\\\memfs\\share", 0);
ea_getset_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
}
}
void ea_tests(void)
{
TEST_OPT(ea_create_test);
TEST_OPT(ea_overwrite_test);
TEST_OPT(ea_getset_test);
}

View File

@ -206,6 +206,7 @@ int main(int argc, char *argv[])
TESTSUITE(exec_tests); TESTSUITE(exec_tests);
TESTSUITE(devctl_tests); TESTSUITE(devctl_tests);
TESTSUITE(reparse_tests); TESTSUITE(reparse_tests);
TESTSUITE(ea_tests);
TESTSUITE(stream_tests); TESTSUITE(stream_tests);
TESTSUITE(oplock_tests); TESTSUITE(oplock_tests);