diff --git a/appveyor.yml b/appveyor.yml index 3380730e..ba087bbe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,6 +11,9 @@ environment: #- CONFIGURATION: Release # TESTING: Perf +init: +#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) + install: - git submodule update --init --recursive - appveyor AddMessage "Change boot configuration and reboot" -Category Information @@ -42,3 +45,4 @@ test_script: on_finish: - if exist %SystemRoot%\memory.dmp (7z a memory.dmp.zip %SystemRoot%\memory.dmp && appveyor PushArtifact memory.dmp.zip) - verifier /query +#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj index c1d7825e..d4f78a30 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj +++ b/build/VStudio/testing/winfsp-tests.vcxproj @@ -185,6 +185,7 @@ + diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters index 493a812a..26eb09a9 100644 --- a/build/VStudio/testing/winfsp-tests.vcxproj.filters +++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters @@ -94,6 +94,9 @@ Source + + Source + diff --git a/build/VStudio/winfsp.sln b/build/VStudio/winfsp.sln index d726e30a..288f222c 100644 --- a/build/VStudio/winfsp.sln +++ b/build/VStudio/winfsp.sln @@ -60,232 +60,168 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fsptool", "tools\fsptool.vc EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Installer.Debug|Any CPU = Installer.Debug|Any CPU Installer.Debug|x64 = Installer.Debug|x64 Installer.Debug|x86 = Installer.Debug|x86 - Installer.Release|Any CPU = Installer.Release|Any CPU Installer.Release|x64 = Installer.Release|x64 Installer.Release|x86 = Installer.Release|x86 - Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection 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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {4A7C0B21-9E10-4C81-92DE-1493EFCF24EB}.Release|x86.ActiveCfg = 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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {C85C26BA-8C22-4D30-83DA-46C3548E6332}.Release|x86.ActiveCfg = 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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.ActiveCfg = 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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.ActiveCfg = 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|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.Build.0 = Release|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.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.Build.0 = 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}.Release|Any CPU.ActiveCfg = Release|x86 {D53AAC39-4C57-4CA5-A4F3-C2B24888C594}.Release|x64.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|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.Build.0 = Release|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.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.Build.0 = 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}.Release|Any CPU.ActiveCfg = Release|Win32 {95C223E6-B5F1-4FD0-9376-41CDBC824445}.Release|x64.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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {10757011-749D-4954-873B-AE38D8145472}.Release|x86.ActiveCfg = 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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB}.Release|x86.ActiveCfg = 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.Build.0 = 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}.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|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|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.Build.0 = Release|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x86.ActiveCfg = Release|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x86.Build.0 = Release|Any CPU - {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|Any CPU.Build.0 = Debug|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x64.ActiveCfg = Debug|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x64.Build.0 = Debug|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x86.ActiveCfg = Debug|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x86.Build.0 = Debug|Any CPU - {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x64.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|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.Build.0 = Release|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.ActiveCfg = Release|Any CPU {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.Build.0 = Release|Any CPU - {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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {6CDF9411-B852-4EAC-822D-8F930675F17B}.Release|x86.ActiveCfg = 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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {264A5D09-126F-4760-A3F1-4B3B95C925AA}.Release|x86.ActiveCfg = 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.Build.0 = Debug|x64 {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}.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|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|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.Build.0 = Release|x64 {1E997BEC-1642-4A5C-B252-852DA094E11E}.Release|x86.ActiveCfg = Release|Win32 diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 67f1a630..6d7d99be 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -152,7 +152,7 @@ enum UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */\ UINT32 NamedStreams:1; /* file system supports named streams */\ 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;\ /* kernel-mode flags */\ UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */\ @@ -167,7 +167,8 @@ enum UINT32 UmReservedFlags:6;\ /* additional kernel-mode flags */\ 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 FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; #define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\ @@ -176,22 +177,28 @@ enum UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\ UINT32 SecurityTimeoutValid:1; /* SecurityTimeout 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 DirInfoTimeout; /* dir info timeout (millis); overrides FileInfoTimeout */\ UINT32 SecurityTimeout; /* security 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]; typedef struct { FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN } 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 { FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN } 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 { UINT64 TotalSize; @@ -261,7 +268,7 @@ typedef struct UINT32 DesiredAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ UINT32 GrantedAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ 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 HasTraversePrivilege:1; /* requestor has TOKEN_HAS_TRAVERSE_PRIVILEGE */ UINT32 HasBackupPrivilege:1; /* requestor has TOKEN_HAS_BACKUP_PRIVILEGE */ @@ -280,6 +287,7 @@ typedef struct UINT32 FileAttributes; /* file attributes for overwritten/superseded files */ UINT64 AllocationSize; /* allocation size for overwritten/superseded files */ UINT32 Supersede:1; /* 0: FILE_OVERWRITE operation, 1: FILE_SUPERSEDE operation */ + FSP_FSCTL_TRANSACT_BUF Ea; /* extended attributes buffer */ } Overwrite; struct { @@ -356,6 +364,17 @@ typedef struct } Info; } SetInformation; struct + { + UINT64 UserContext; + UINT64 UserContext2; + } QueryEa; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + FSP_FSCTL_TRANSACT_BUF Ea; + } SetEa; + struct { UINT64 UserContext; UINT64 UserContext2; @@ -470,6 +489,14 @@ typedef struct FSP_FSCTL_FILE_INFO FileInfo; /* valid: File{Allocation,Basic,EndOfFile}Information */ } SetInformation; 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) */ } FlushBuffers; diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 300aa267..2d08893f 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -85,6 +85,21 @@ typedef struct _REPARSE_DATA_BUFFER } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #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 * @@ -894,12 +909,134 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE */ NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem, 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. * Please update when changing the interface as it is important for future compatibility. */ - NTSTATUS (*Reserved[37])(); + NTSTATUS (*Reserved[33])(); } FSP_FILE_SYSTEM_INTERFACE; FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()), "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_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, 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_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); 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, 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 diff --git a/src/dll/fs.c b/src/dll/fs.c index 9a0810b0..43b20d74 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -161,6 +161,8 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath, FileSystem->Operations[FspFsctlTransactWriteKind] = FspFileSystemOpWrite; FileSystem->Operations[FspFsctlTransactQueryInformationKind] = FspFileSystemOpQueryInformation; FileSystem->Operations[FspFsctlTransactSetInformationKind] = FspFileSystemOpSetInformation; + FileSystem->Operations[FspFsctlTransactQueryEaKind] = FspFileSystemOpQueryEa; + FileSystem->Operations[FspFsctlTransactSetEaKind] = FspFileSystemOpSetEa; FileSystem->Operations[FspFsctlTransactFlushBuffersKind] = FspFileSystemOpFlushBuffers; FileSystem->Operations[FspFsctlTransactQueryVolumeInformationKind] = FspFileSystemOpQueryVolumeInformation; FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation; diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 1a302fc6..dd50a308 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -436,10 +436,19 @@ static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem, memset(&OpenFileInfo, 0, sizeof OpenFileInfo); OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; - Result = FileSystem->Interface->Create(FileSystem, - (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, - Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, - AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + 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, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) { FspDeleteSecurityDescriptor(OpenDescriptor, FspCreateSecurityDescriptor); @@ -574,10 +583,19 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem, memset(&OpenFileInfo, 0, sizeof OpenFileInfo); OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; - Result = FileSystem->Interface->Create(FileSystem, - (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, - Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, - AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + 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, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, OpenDescriptor, Request->Req.Create.AllocationSize, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); if (!NT_SUCCESS(Result)) { FspDeleteSecurityDescriptor(OpenDescriptor, FspCreateSecurityDescriptor); @@ -699,10 +717,19 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste memset(&OpenFileInfo, 0, sizeof OpenFileInfo); OpenFileInfo.NormalizedName = (PVOID)Response->Buffer; OpenFileInfo.NormalizedNameSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; - Result = FileSystem->Interface->Create(FileSystem, - (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, - Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, - AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); + 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, + (PWSTR)Request->Buffer, Request->Req.Create.CreateOptions, GrantedAccess, + Request->Req.Create.FileAttributes, ObjectDescriptor, Request->Req.Create.AllocationSize, + AddrOfFileContext(FullContext), &OpenFileInfo.FileInfo); FspDeleteSecurityDescriptor(ObjectDescriptor, FspCreateSecurityDescriptor); if (!NT_SUCCESS(Result)) return Result; @@ -865,9 +892,9 @@ FSP_API NTSTATUS FspFileSystemOpCreate(FSP_FILE_SYSTEM *FileSystem, { NTSTATUS Result; - if (0 == FileSystem->Interface->Create || + if ((0 == FileSystem->Interface->Create && 0 == FileSystem->Interface->CreateEx) || 0 == FileSystem->Interface->Open || - 0 == FileSystem->Interface->Overwrite) + (0 == FileSystem->Interface->Overwrite && 0 == FileSystem->Interface->OverwriteEx)) return STATUS_INVALID_DEVICE_REQUEST; if (Request->Req.Create.OpenTargetDirectory) @@ -910,16 +937,27 @@ FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem, NTSTATUS Result; FSP_FSCTL_FILE_INFO FileInfo; - if (0 == FileSystem->Interface->Overwrite) + if (0 == FileSystem->Interface->Overwrite && 0 == FileSystem->Interface->OverwriteEx) return STATUS_INVALID_DEVICE_REQUEST; memset(&FileInfo, 0, sizeof FileInfo); - Result = FileSystem->Interface->Overwrite(FileSystem, - (PVOID)ValOfFileContext(Request->Req.Overwrite), - Request->Req.Overwrite.FileAttributes, - Request->Req.Overwrite.Supersede, - Request->Req.Overwrite.AllocationSize, - &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, + (PVOID)ValOfFileContext(Request->Req.Overwrite), + Request->Req.Overwrite.FileAttributes, + Request->Req.Overwrite.Supersede, + Request->Req.Overwrite.AllocationSize, + &FileInfo); if (!NT_SUCCESS(Result)) { if (0 != FileSystem->Interface->Close) @@ -1145,6 +1183,39 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, 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_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); } + +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; +} diff --git a/src/dll/library.h b/src/dll/library.h index 17a67f7c..9970d31d 100644 --- a/src/dll/library.h +++ b/src/dll/library.h @@ -92,4 +92,7 @@ static inline BOOLEAN FspPathIsDrive(PWSTR FileName) L':' == FileName[1] && L'\0' == FileName[2]; } +#define FSP_NEXT_EA(Ea, EaEnd) \ + (0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd)) + #endif diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index 87403ac9..1991d188 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -1078,6 +1078,108 @@ namespace Fsp else 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 */ /// diff --git a/src/dotnet/FileSystemHost.cs b/src/dotnet/FileSystemHost.cs index 3f9140ac..a30fce20 100644 --- a/src/dotnet/FileSystemHost.cs +++ b/src/dotnet/FileSystemHost.cs @@ -183,6 +183,14 @@ namespace Fsp get { return 0 != (_VolumeParams.Flags & VolumeParams.NamedStreams); } set { _VolumeParams.Flags |= (value ? VolumeParams.NamedStreams : 0); } } + /// + /// Gets or sets a value that determines whether the file system supports extended attributes. + /// + public Boolean ExtendedAttributes + { + get { return 0 != (_VolumeParams.Flags & VolumeParams.ExtendedAttributes); } + set { _VolumeParams.Flags |= (value ? VolumeParams.ExtendedAttributes : 0); } + } public Boolean PostCleanupWhenModifiedOnly { get { return 0 != (_VolumeParams.Flags & VolumeParams.PostCleanupWhenModifiedOnly); } @@ -464,6 +472,8 @@ namespace Fsp UInt32 FileAttributes, IntPtr SecurityDescriptor, UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, ref FullContext FullContext, ref OpenFileInfo OpenFileInfo) { @@ -473,13 +483,15 @@ namespace Fsp Object FileNode, FileDesc; String NormalizedName; Int32 Result; - Result = FileSystem.Create( + Result = FileSystem.CreateEx( FileName, CreateOptions, GrantedAccess, FileAttributes, Api.MakeSecurityDescriptor(SecurityDescriptor), AllocationSize, + Ea, + EaLength, out FileNode, out FileDesc, out OpenFileInfo.FileInfo, @@ -538,6 +550,8 @@ namespace Fsp UInt32 FileAttributes, Boolean ReplaceFileAttributes, UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, out FileInfo FileInfo) { FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr); @@ -545,12 +559,14 @@ namespace Fsp { Object FileNode, FileDesc; Api.GetFullContext(ref FullContext, out FileNode, out FileDesc); - return FileSystem.Overwrite( + return FileSystem.OverwriteEx( FileNode, FileDesc, FileAttributes, ReplaceFileAttributes, AllocationSize, + Ea, + EaLength, out FileInfo); } catch (Exception ex) @@ -1077,15 +1093,62 @@ namespace Fsp 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() { _FileSystemInterface.GetVolumeInfo = GetVolumeInfo; _FileSystemInterface.SetVolumeLabel = SetVolumeLabel; _FileSystemInterface.GetSecurityByName = GetSecurityByName; - _FileSystemInterface.Create = Create; + _FileSystemInterface.CreateEx = Create; _FileSystemInterface.Open = Open; - _FileSystemInterface.Overwrite = Overwrite; + _FileSystemInterface.OverwriteEx = Overwrite; _FileSystemInterface.Cleanup = Cleanup; _FileSystemInterface.Close = Close; _FileSystemInterface.Read = Read; @@ -1106,6 +1169,8 @@ namespace Fsp _FileSystemInterface.GetDirInfoByName = GetDirInfoByName; _FileSystemInterface.Control = Control; _FileSystemInterface.SetDelete = SetDelete; + _FileSystemInterface.GetEa = GetEa; + _FileSystemInterface.SetEa = SetEa; _FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size); Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false); diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index 3945a034..7bc38599 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -52,6 +52,7 @@ namespace Fsp.Interop internal const UInt32 UmFileContextIsUserContext2 = 0x00010000; internal const UInt32 UmFileContextIsFullContext = 0x00020000; internal const UInt32 AllowOpenInKernelMode = 0x01000000; + internal const UInt32 CasePreservedExtendedAttributes = 0x02000000; internal const int PrefixSize = 192; 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)] internal struct FullContext { @@ -478,6 +513,42 @@ namespace Fsp.Interop ref FullContext FullContext, [MarshalAs(UnmanagedType.LPWStr)] String FileName, [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; @@ -509,7 +580,11 @@ namespace Fsp.Interop internal Proto.GetDirInfoByName GetDirInfoByName; internal Proto.Control Control; 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] @@ -604,6 +679,13 @@ namespace Fsp.Interop out UInt32 PBytesTransferred); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [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( ref IntPtr PDirBuffer, [MarshalAs(UnmanagedType.U1)] Boolean Reset, @@ -737,6 +819,7 @@ namespace Fsp.Interop internal static Proto.FspFileSystemResolveReparsePoints FspFileSystemResolveReparsePoints; internal static Proto.FspFileSystemCanReplaceReparsePoint _FspFileSystemCanReplaceReparsePoint; internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo; + internal static Proto.FspFileSystemAddEa _FspFileSystemAddEa; internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer; internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer; internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer; @@ -806,6 +889,62 @@ namespace Fsp.Interop 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( IntPtr NativePtr) { @@ -1074,6 +1213,7 @@ namespace Fsp.Interop FspFileSystemResolveReparsePoints = GetEntryPoint(Module); _FspFileSystemCanReplaceReparsePoint = GetEntryPoint(Module); _FspFileSystemAddStreamInfo = GetEntryPoint(Module); + _FspFileSystemAddEa = GetEntryPoint(Module); FspFileSystemAcquireDirectoryBuffer = GetEntryPoint(Module); FspFileSystemFillDirectoryBuffer = GetEntryPoint(Module); FspFileSystemReleaseDirectoryBuffer = GetEntryPoint(Module); diff --git a/src/sys/create.c b/src/sys/create.c index dabfe06e..1ce68201 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -278,7 +278,7 @@ static NTSTATUS FspFsvolCreateNoLock( ACCESS_MASK GrantedAccess = AccessState->PreviouslyGrantedAccess; USHORT ShareAccess = IrpSp->Parameters.Create.ShareAccess; PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer; - //ULONG EaLength = IrpSp->Parameters.Create.EaLength; + ULONG EaLength = IrpSp->Parameters.Create.EaLength; ULONG Flags = IrpSp->Flags; KPROCESSOR_MODE RequestorMode = FlagOn(Flags, SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode; @@ -302,9 +302,22 @@ static NTSTATUS FspFsvolCreateNoLock( if (FlagOn(CreateOptions, FILE_OPEN_BY_FILE_ID)) return STATUS_NOT_IMPLEMENTED; - /* no EA support currently */ + /* was an EA buffer specified? */ if (0 != EaBuffer) - return STATUS_EAS_NOT_SUPPORTED; + { + /* does the file system support EA? */ + if (!FsvolDeviceExtension->VolumeParams.ExtendedAttributes) + 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 */ if (FlagOn(Flags, SL_OPEN_PAGING_FILE)) @@ -541,6 +554,10 @@ static NTSTATUS FspFsvolCreateNoLock( SecurityDescriptorSize = 0; FileAttributes = 0; + /* cannot set EA on named stream */ + EaBuffer = 0; + EaLength = 0; + /* remember the main file node */ ASSERT(0 == FileNode->MainFileNode); FileNode->MainFileNode = FileDesc->MainFileObject->FsContext; @@ -558,7 +575,9 @@ static NTSTATUS FspFsvolCreateNoLock( } /* 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); if (!NT_SUCCESS(Result)) { @@ -584,19 +603,22 @@ static NTSTATUS FspFsvolCreateNoLock( FspIopRequestContext(Request, RequestFileDesc) = FileDesc; /* populate the Create request */ +#define NEXTOFS(B) ((B).Offset + FSP_FSCTL_DEFAULT_ALIGN_UP((B).Size)) Request->Kind = FspFsctlTransactCreateKind; Request->Req.Create.CreateOptions = CreateOptions; Request->Req.Create.FileAttributes = FileAttributes; - Request->Req.Create.SecurityDescriptor.Offset = 0 == SecurityDescriptorSize ? 0 : - FSP_FSCTL_DEFAULT_ALIGN_UP(Request->FileName.Size); + Request->Req.Create.SecurityDescriptor.Offset = 0 != SecurityDescriptorSize ? + NEXTOFS(Request->FileName) : 0; Request->Req.Create.SecurityDescriptor.Size = (UINT16)SecurityDescriptorSize; Request->Req.Create.AllocationSize = AllocationSize; Request->Req.Create.AccessToken = 0; Request->Req.Create.DesiredAccess = DesiredAccess; Request->Req.Create.GrantedAccess = GrantedAccess; Request->Req.Create.ShareAccess = ShareAccess; - Request->Req.Create.Ea.Offset = 0; - Request->Req.Create.Ea.Size = 0; + Request->Req.Create.Ea.Offset = 0 != EaBuffer ? + (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.HasTraversePrivilege = HasTraversePrivilege; Request->Req.Create.HasBackupPrivilege = HasBackupPrivilege; @@ -605,6 +627,7 @@ static NTSTATUS FspFsvolCreateNoLock( Request->Req.Create.CaseSensitive = CaseSensitive; Request->Req.Create.HasTrailingBackslash = HasTrailingBackslash; Request->Req.Create.NamedStream = MainFileName.Length; +#undef APPEND Request->Req.Create.AcceptsSecurityDescriptor = 0 == Request->Req.Create.NamedStream && !!FsvolDeviceExtension->VolumeParams.AllowOpenInKernelMode; @@ -618,6 +641,11 @@ static NTSTATUS FspFsvolCreateNoLock( RtlCopyMemory(Request->Buffer + Request->Req.Create.SecurityDescriptor.Offset, 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 */ if (Request->Req.Create.OpenTargetDirectory) { @@ -1081,6 +1109,7 @@ NTSTATUS FspFsvolCreateComplete( } PVOID RequestDeviceObjectValue = FspIopRequestContext(Request, RequestDeviceObject); + FSP_FSCTL_TRANSACT_BUF Ea = Request->Req.Create.Ea; /* disassociate the FileDesc momentarily from the Request */ FspIopRequestContext(Request, RequestDeviceObject) = 0; @@ -1101,6 +1130,7 @@ NTSTATUS FspFsvolCreateComplete( Request->Req.Overwrite.FileAttributes = FileAttributes; Request->Req.Overwrite.AllocationSize = AllocationSize; Request->Req.Overwrite.Supersede = FILE_SUPERSEDED == Response->IoStatus.Information; + Request->Req.Overwrite.Ea = Ea; /* * Post it as BestEffort. @@ -1141,8 +1171,16 @@ NTSTATUS FspFsvolCreateComplete( if (0 == FileNode->MainFileNode) FspFileNodeOverwriteStreams(FileNode); 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, - 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, FALSE); diff --git a/src/sys/device.c b/src/sys/device.c index d6dda01c..edf74336 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -317,7 +317,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) NTSTATUS Result; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject); 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 @@ -379,6 +379,16 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) return Result; 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 */ Result = FspNotifyInitializeSync(&FsvolDeviceExtension->NotifySync); if (!NT_SUCCESS(Result)) @@ -449,6 +459,10 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject) FspNotifyUninitializeSync(&FsvolDeviceExtension->NotifySync); } + /* delete the EA meta cache */ + if (FsvolDeviceExtension->InitDoneEa) + FspMetaCacheDelete(FsvolDeviceExtension->EaCache); + /* delete the stream info meta cache */ if (FsvolDeviceExtension->InitDoneStrm) FspMetaCacheDelete(FsvolDeviceExtension->StreamInfoCache); diff --git a/src/sys/driver.h b/src/sys/driver.h index fd00055f..391e1be9 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -452,12 +452,17 @@ enum BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength, PUNICODE_STRING StreamPart, PULONG StreamType); BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength); +BOOLEAN FspEaNameIsValid(PSTRING Name); VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); #if 0 NTSTATUS FspFileNameUpcase( PUNICODE_STRING DestinationName, PUNICODE_STRING SourceName, PCWCH UpcaseTable); +VOID FspEaNameUpcase( + PSTRING DestinationName, + PSTRING SourceName, + PCWCH UpcaseTable); LONG FspFileNameCompare( PUNICODE_STRING Name1, PUNICODE_STRING Name2, @@ -470,6 +475,7 @@ BOOLEAN FspFileNameIsPrefix( PCWCH UpcaseTable); #else #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 FspFileNameIsPrefix(N1,N2,I,U) (ASSERT(0 == (U)), RtlPrefixUnicodeString(N1,N2,I)) #endif @@ -509,6 +515,10 @@ NTSTATUS FspCcFlushCache(PSECTION_OBJECT_POINTERS SectionObjectPointer, NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor); +NTSTATUS FspEaBufferAndNamesValid( + PFILE_FULL_EA_INFORMATION Buffer, + ULONG Length, + PULONG PErrorOffset); NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync); NTSTATUS FspNotifyFullChangeDirectory( PNOTIFY_SYNC NotifySync, @@ -567,6 +577,8 @@ NTSTATUS FspOplockFsctrl( FspNotifyFullChangeDirectory(NS, NL, FC, 0, 0, FALSE, 0, 0, 0, 0) #define FspNotifyReportChange(NS, NL, FN, FO, NP, F, A)\ 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 */ typedef struct @@ -997,6 +1009,8 @@ enum FspFsvolDeviceDirInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), FspFsvolDeviceStreamInfoCacheCapacity = 100, FspFsvolDeviceStreamInfoCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), + FspFsvolDeviceEaCacheCapacity = 100, + FspFsvolDeviceEaCacheItemSizeMax = FSP_FSCTL_ALIGN_UP(16384, PAGE_SIZE), }; typedef struct { @@ -1029,7 +1043,7 @@ typedef struct typedef struct { 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; PDEVICE_OBJECT FsctlDeviceObject; PDEVICE_OBJECT FsvrtDeviceObject; @@ -1043,6 +1057,7 @@ typedef struct FSP_META_CACHE *SecurityCache; FSP_META_CACHE *DirInfoCache; FSP_META_CACHE *StreamInfoCache; + FSP_META_CACHE *EaCache; KSPIN_LOCK ExpirationLock; WORK_QUEUE_ITEM ExpirationWorkItem; BOOLEAN ExpirationInProgress; @@ -1271,6 +1286,9 @@ typedef struct FSP_FILE_NODE ULONG SecurityChangeNumber; ULONG DirInfoChangeNumber; ULONG StreamInfoChangeNumber; + UINT64 Ea; + ULONG EaChangeNumber; + ULONG EaChangeCount; BOOLEAN TruncateOnClose; FILE_LOCK FileLock; #if (NTDDI_VERSION < NTDDI_WIN8) @@ -1309,6 +1327,8 @@ typedef struct UNICODE_STRING DirectoryMarker; UINT64 DirInfo; ULONG DirInfoCacheHint; + ULONG EaIndex; + ULONG EaChangeCount; /* stream support */ HANDLE MainFileHandle; PFILE_OBJECT MainFileObject; @@ -1439,6 +1459,17 @@ ULONG FspFileNodeStreamInfoChangeNumber(FSP_FILE_NODE *FileNode) return FileNode->StreamInfoChangeNumber; } 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, BOOLEAN InvalidateCaches); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); @@ -1471,6 +1502,7 @@ NTSTATUS FspMainFileClose( #define FspFileNodeDereferenceSecurity(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceDirInfo(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceStreamInfo(P) FspMetaCacheDereferenceItemBuffer(P) +#define FspFileNodeDereferenceEa(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeUnlockAll(N,F,P) FsRtlFastUnlockAll(&(N)->FileLock, F, P, N) #if (NTDDI_VERSION < NTDDI_WIN8) #define FspFileNodeAddrOfOplock(N) (&(N)->Oplock) diff --git a/src/sys/ea.c b/src/sys/ea.c index a128dff7..0b5462b4 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -21,30 +21,385 @@ #include +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( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolQueryEaComplete; +static FSP_IOP_REQUEST_FINI FspFsvolQueryEaRequestFini; static NTSTATUS FspFsvolSetEa( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolSetEaComplete; +static FSP_IOP_REQUEST_FINI FspFsvolSetEaRequestFini; FSP_DRIVER_DISPATCH FspQueryEa; FSP_DRIVER_DISPATCH FspSetEa; #ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FspFsvolQueryEaGetCopy) +#pragma alloc_text(PAGE, FspFsvolQueryEaIndexCopy) #pragma alloc_text(PAGE, FspFsvolQueryEa) #pragma alloc_text(PAGE, FspFsvolQueryEaComplete) +#pragma alloc_text(PAGE, FspFsvolQueryEaRequestFini) #pragma alloc_text(PAGE, FspFsvolSetEa) #pragma alloc_text(PAGE, FspFsvolSetEaComplete) +#pragma alloc_text(PAGE, FspFsvolSetEaRequestFini) #pragma alloc_text(PAGE, FspQueryEa) #pragma alloc_text(PAGE, FspSetEa) #endif -static NTSTATUS FspFsvolQueryEa( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +enum +{ + /* 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(); - return STATUS_INVALID_DEVICE_REQUEST; + 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; + + 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( @@ -52,15 +407,147 @@ NTSTATUS FspFsvolQueryEaComplete( { 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(); + } + + PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject; + 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 NTSTATUS FspFsvolSetEa( - PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +static VOID FspFsvolQueryEaRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]) { PAGED_CODE(); - return STATUS_INVALID_DEVICE_REQUEST; + 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; + + 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( @@ -68,7 +555,64 @@ NTSTATUS FspFsvolSetEaComplete( { 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( @@ -84,7 +628,8 @@ NTSTATUS FspQueryEa( FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); } - FSP_LEAVE_MJ("%s", ""); + FSP_LEAVE_MJ("FileObject=%p", + IrpSp->FileObject); } NTSTATUS FspSetEa( @@ -100,5 +645,6 @@ NTSTATUS FspSetEa( FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); } - FSP_LEAVE_MJ("%s", ""); + FSP_LEAVE_MJ("FileObject=%p", + IrpSp->FileObject); } diff --git a/src/sys/file.c b/src/sys/file.c index 228a20cb..dc4c7ab7 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -89,6 +89,10 @@ VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG StreamInfoChangeNumber); 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, BOOLEAN InvalidateCaches); 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, FspFileNodeTrySetStreamInfo) // !#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, FspFileNodeProcessLockIrp) #pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp) @@ -361,6 +368,7 @@ VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode) FsRtlTeardownPerStreamContexts(&FileNode->Header); + FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea); FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security); @@ -2115,6 +2123,48 @@ VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode) 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, BOOLEAN InvalidateCaches) { diff --git a/src/sys/name.c b/src/sys/name.c index 37493219..ebf8838d 100644 --- a/src/sys/name.c +++ b/src/sys/name.c @@ -24,6 +24,7 @@ BOOLEAN FspFileNameIsValid(PUNICODE_STRING Path, ULONG MaxComponentLength, PUNICODE_STRING StreamPart, PULONG StreamType); BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Pattern, ULONG MaxComponentLength); +BOOLEAN FspEaNameIsValid(PSTRING Name); VOID FspFileNameSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); NTSTATUS FspFileNameInExpression( PUNICODE_STRING Expression, @@ -35,6 +36,7 @@ NTSTATUS FspFileNameInExpression( #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspFileNameIsValid) #pragma alloc_text(PAGE, FspFileNameIsValidPattern) +#pragma alloc_text(PAGE, FspEaNameIsValid) #pragma alloc_text(PAGE, FspFileNameSuffix) #pragma alloc_text(PAGE, FspFileNameInExpression) #endif @@ -191,6 +193,37 @@ BOOLEAN FspFileNameIsValidPattern(PUNICODE_STRING Path, ULONG MaxComponentLength 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) { PAGED_CODE(); diff --git a/src/sys/util.c b/src/sys/util.c index 7252aeb0..aabcf5c9 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -49,6 +49,10 @@ NTSTATUS FspCcFlushCache(PSECTION_OBJECT_POINTERS SectionObjectPointer, NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor, PULONG PLength, PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor); +NTSTATUS FspEaBufferAndNamesValid( + PFILE_FULL_EA_INFORMATION Buffer, + ULONG Length, + PULONG PErrorOffset); NTSTATUS FspNotifyInitializeSync(PNOTIFY_SYNC *NotifySync); NTSTATUS FspNotifyFullChangeDirectory( 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, FspCcFlushCache) #pragma alloc_text(PAGE, FspQuerySecurityDescriptorInfo) +#pragma alloc_text(PAGE, FspEaBufferAndNamesValid) #pragma alloc_text(PAGE, FspNotifyInitializeSync) #pragma alloc_text(PAGE, FspNotifyFullChangeDirectory) #pragma alloc_text(PAGE, FspNotifyFullReportChange) @@ -578,6 +583,39 @@ NTSTATUS FspQuerySecurityDescriptorInfo(SECURITY_INFORMATION SecurityInformation 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) { PAGED_CODE(); diff --git a/src/sys/volinfo.c b/src/sys/volinfo.c index 23bee90a..5ae2ea1c 100644 --- a/src/sys/volinfo.c +++ b/src/sys/volinfo.c @@ -96,7 +96,7 @@ static NTSTATUS FspFsvolQueryFsAttributeInformation( (FsvolDeviceExtension->VolumeParams.ReparsePoints ? FILE_SUPPORTS_REPARSE_POINTS : 0) | (FsvolDeviceExtension->VolumeParams.NamedStreams ? FILE_NAMED_STREAMS : 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); Info->MaximumComponentNameLength = FsvolDeviceExtension->VolumeParams.MaxComponentLength; diff --git a/src/sys/volume.c b/src/sys/volume.c index c5b31ec8..3ea77490 100644 --- a/src/sys/volume.c +++ b/src/sys/volume.c @@ -151,6 +151,7 @@ static NTSTATUS FspVolumeCreateNoLock( VolumeParams.DirInfoTimeout = VolumeParams.FileInfoTimeout; VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; + VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout; } else { @@ -162,11 +163,14 @@ static NTSTATUS FspVolumeCreateNoLock( VolumeParams.SecurityTimeout = VolumeParams.FileInfoTimeout; if (!VolumeParams.StreamInfoTimeoutValid) VolumeParams.StreamInfoTimeout = VolumeParams.FileInfoTimeout; + if (!VolumeParams.EaTimeoutValid) + VolumeParams.EaTimeout = VolumeParams.FileInfoTimeout; } VolumeParams.VolumeInfoTimeoutValid = 1; VolumeParams.DirInfoTimeoutValid = 1; VolumeParams.SecurityTimeoutValid = 1; VolumeParams.StreamInfoTimeoutValid = 1; + VolumeParams.EaTimeoutValid = 1; if (FILE_DEVICE_NETWORK_FILE_SYSTEM == FsctlDeviceObject->DeviceType) { VolumeParams.Prefix[sizeof VolumeParams.Prefix / sizeof(WCHAR) - 1] = L'\0'; diff --git a/tools/cloc.bat b/tools/cloc.bat new file mode 100755 index 00000000..6b8ccce3 --- /dev/null +++ b/tools/cloc.bat @@ -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" . diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index 70bce965..b3c2270e 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -55,6 +55,12 @@ namespace memfs } } + struct EaValueData + { + public Byte[] EaValue; + public Boolean NeedEa; + } + class FileNode { public FileNode(String FileName) @@ -80,6 +86,13 @@ namespace memfs return FileInfo; } } + public SortedDictionary GetEaMap(Boolean Force) + { + FileNode FileNode = null == MainFileNode ? this : MainFileNode; + if (null == EaMap && Force) + EaMap = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + return EaMap; + } private static UInt64 IndexNumber = 1; public String FileName; @@ -87,6 +100,7 @@ namespace memfs public Byte[] FileSecurity; public Byte[] FileData; public Byte[] ReparseData; + private SortedDictionary EaMap; public FileNode MainFileNode; public int OpenCount; } @@ -259,6 +273,7 @@ namespace memfs Host.NamedStreams = true; Host.PostCleanupWhenModifiedOnly = true; Host.PassQueryDirectoryFileName = true; + Host.ExtendedAttributes = true; return STATUS_SUCCESS; } @@ -309,13 +324,15 @@ namespace memfs return STATUS_SUCCESS; } - public override Int32 Create( + public override Int32 CreateEx( String FileName, UInt32 CreateOptions, UInt32 GrantedAccess, UInt32 FileAttributes, Byte[] SecurityDescriptor, UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, out Object FileNode0, out Object FileDesc, out FileInfo FileInfo, @@ -352,6 +369,12 @@ namespace memfs FileNode.FileInfo.FileAttributes = 0 != (FileAttributes & (UInt32)System.IO.FileAttributes.Directory) ? FileAttributes : FileAttributes | (UInt32)System.IO.FileAttributes.Archive; FileNode.FileSecurity = SecurityDescriptor; + if (IntPtr.Zero != Ea) + { + Result = SetEa(FileNode, null, Ea, EaLength); + if (0 > Result) + return Result; + } if (0 != AllocationSize) { Result = SetFileSizeInternal(FileNode, AllocationSize, true); @@ -393,6 +416,18 @@ namespace memfs return Result; } + if (0 != (CreateOptions & FILE_NO_EA_KNOWLEDGE) && + null == FileNode.MainFileNode) + { + SortedDictionary EaMap = FileNode.GetEaMap(false); + if (null != EaMap) + { + foreach (KeyValuePair Pair in EaMap) + if (Pair.Value.NeedEa) + return STATUS_ACCESS_DENIED; + } + } + Interlocked.Increment(ref FileNode.OpenCount); FileNode0 = FileNode; FileInfo = FileNode.GetFileInfo(); @@ -401,12 +436,14 @@ namespace memfs return STATUS_SUCCESS; } - public override Int32 Overwrite( + public override Int32 OverwriteEx( Object FileNode0, Object FileDesc, UInt32 FileAttributes, Boolean ReplaceFileAttributes, UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, out FileInfo FileInfo) { FileInfo = default(FileInfo); @@ -424,6 +461,16 @@ namespace memfs FileNodeMap.Remove(StreamNode); } + SortedDictionary 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); if (0 > Result) return Result; @@ -1019,6 +1066,67 @@ namespace memfs StreamAllocationSize = default(UInt64); 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> Enumerator = + (IEnumerator>)Context; + + if (null == Enumerator) + { + SortedDictionary 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 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 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 UInt32 MaxFileNodes; diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index f361b048..8dc4ba1e 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -64,6 +64,11 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH, */ #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 * 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 } +#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 MEMFS_FILE_NODE_EA_MAP; +#endif + typedef struct _MEMFS_FILE_NODE { WCHAR FileName[MEMFS_MAX_PATH]; @@ -247,6 +282,9 @@ typedef struct _MEMFS_FILE_NODE #if defined(MEMFS_REPARSE_POINTS) SIZE_T ReparseDataSize; PVOID ReparseData; +#endif +#if defined(MEMFS_EA) + MEMFS_FILE_NODE_EA_MAP *EaMap; #endif volatile LONG RefCount; #if defined(MEMFS_NAMED_STREAMS) @@ -308,9 +346,27 @@ NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode) 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 VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) { +#if defined(MEMFS_EA) + MemfsFileNodeDeleteEaMap(FileNode); +#endif #if defined(MEMFS_REPARSE_POINTS) free(FileNode->ReparseData); #endif @@ -351,6 +407,121 @@ VOID MemfsFileNodeGetFileInfo(MEMFS_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *Fi #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 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, PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, 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) { MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; @@ -948,6 +1122,18 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, 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; if (0 != FileNode->FileInfo.AllocationSize) { @@ -1005,6 +1191,22 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, 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); *PFileNode = FileNode; MemfsFileNodeGetFileInfo(FileNode, FileInfo); @@ -1025,6 +1227,9 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, 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) { MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; @@ -1047,6 +1252,16 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, MemfsFileNodeMapEnumerateFree(&Context); #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); if (!NT_SUCCESS(Result)) return Result; @@ -1881,7 +2096,7 @@ static NTSTATUS GetStreamInfo(FSP_FILE_SYSTEM *FileSystem, #if defined(MEMFS_CONTROL) static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem, - PVOID FileContext, UINT32 ControlCode, + PVOID FileNode, UINT32 ControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred) { @@ -1911,14 +2126,63 @@ static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem, } #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 = { GetVolumeInfo, SetVolumeLabel, GetSecurityByName, +#if defined(MEMFS_EA) + 0, +#else Create, +#endif Open, +#if defined(MEMFS_EA) + 0, +#else Overwrite, +#endif Cleanup, Close, Read, @@ -1957,6 +2221,18 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = Control, #else 0, +#endif + 0, +#if defined(MEMFS_EA) + Create, + Overwrite, + GetEa, + SetEa +#else + 0, + 0, + 0, + 0, #endif }; @@ -2051,6 +2327,9 @@ NTSTATUS MemfsCreateFunnel( VolumeParams.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup; #if defined(MEMFS_CONTROL) VolumeParams.DeviceControl = 1; +#endif +#if defined(MEMFS_EA) + VolumeParams.ExtendedAttributes = 1; #endif VolumeParams.AllowOpenInKernelMode = 1; if (0 != VolumePrefix) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c new file mode 100644 index 00000000..df29dd44 --- /dev/null +++ b/tst/winfsp-tests/ea-test.c @@ -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 +#include +#include +#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); +} diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c index 8e934338..e8be476c 100644 --- a/tst/winfsp-tests/winfsp-tests.c +++ b/tst/winfsp-tests/winfsp-tests.c @@ -206,6 +206,7 @@ int main(int argc, char *argv[]) TESTSUITE(exec_tests); TESTSUITE(devctl_tests); TESTSUITE(reparse_tests); + TESTSUITE(ea_tests); TESTSUITE(stream_tests); TESTSUITE(oplock_tests);