From 778f5f70dcc790399c1d11110f52fc5613bab0e3 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 11 Mar 2019 14:06:29 -0700 Subject: [PATCH 01/35] build: remove anycpu configuration --- build/VStudio/winfsp.sln | 64 ---------------------------------------- 1 file changed, 64 deletions(-) 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 From a811cd2cf848cc20c52d6b4f70535c7f039acf05 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 13 Mar 2019 14:29:49 -0700 Subject: [PATCH 02/35] sys, dll: extended attributes: checkpoint --- inc/winfsp/fsctl.h | 29 ++- inc/winfsp/winfsp.h | 137 ++++++++++- src/dll/fs.c | 2 + src/dll/fsop.c | 92 ++++++-- src/sys/create.c | 47 +++- src/sys/device.c | 16 +- src/sys/driver.h | 26 ++- src/sys/ea.c | 539 +++++++++++++++++++++++++++++++++++++++++++- src/sys/file.c | 50 ++++ src/sys/name.c | 33 +++ src/sys/volinfo.c | 2 +- src/sys/volume.c | 4 + 12 files changed, 931 insertions(+), 46 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 67f1a630..77ff3c81 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,6 +167,7 @@ enum UINT32 UmReservedFlags:6;\ /* additional kernel-mode flags */\ UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\ + UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\ UINT32 KmReservedFlags:7;\ WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\ WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; @@ -176,12 +177,14 @@ enum UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\ UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\ UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\ + UINT32 EaTimeoutValid:1; /* EaTimeout field is valid */\ UINT32 KmAdditionalReservedFlags:28;\ 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 { @@ -261,7 +264,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 +283,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 +360,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 +485,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..b02cd46e 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -85,6 +85,18 @@ 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. + */ +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 +906,131 @@ 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, SIZE_T 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, SIZE_T 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 [in,out] + * Extended attributes buffer length. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetEa + */ + NTSTATUS (*GetEa)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PFILE_FULL_EA_INFORMATION Ea, PSIZE_T PEaLength); + /** + * 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, SIZE_T 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 +1334,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, 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..58e5a94c 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,18 @@ 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) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + +FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { diff --git a/src/sys/create.c b/src/sys/create.c index dabfe06e..d60d2bbb 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,23 @@ 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? */ + Irp->IoStatus.Information = 0; + Result = IoCheckEaBufferValidity(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 +555,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 +576,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 +604,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 +628,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 +642,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 +1110,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 +1131,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. 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..02678013 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 @@ -997,6 +1003,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 +1037,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 +1051,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 +1280,8 @@ typedef struct FSP_FILE_NODE ULONG SecurityChangeNumber; ULONG DirInfoChangeNumber; ULONG StreamInfoChangeNumber; + UINT64 Ea; + ULONG EaChangeNumber; BOOLEAN TruncateOnClose; FILE_LOCK FileLock; #if (NTDDI_VERSION < NTDDI_WIN8) @@ -1309,6 +1320,7 @@ typedef struct UNICODE_STRING DirectoryMarker; UINT64 DirInfo; ULONG DirInfoCacheHint; + ULONG EaIndex; /* stream support */ HANDLE MainFileHandle; PFILE_OBJECT MainFileObject; @@ -1439,6 +1451,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 +1494,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..e17831a2 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -21,30 +21,359 @@ #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; + PVOID Src; + STRING GetName, Name; + ULONG CopyLength; + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = 0; + DstBuf = DstBufBgn, PrevDstBuf = 0; + for (GetBuf = GetBufBgn; + GetBufEnd > GetBuf && 0 != GetBuf->NextEntryOffset; + GetBuf = (PVOID)((PUINT8)GetBuf + GetBuf->NextEntryOffset)) + { + 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); + break; + } + + Src = GetBuf; + for (SrcBuf = SrcBufBgn; + SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; + SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset)) + { + Name.Length = Name.MaximumLength = SrcBuf->EaNameLength; + Name.Buffer = SrcBuf->EaName; + + if (RtlEqualString(&GetName, &Name, TRUE/* always case-insensitive */)) + { + Src = SrcBuf; + break; + } + } + + if (GetBuf != Src) + CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + ((PFILE_FULL_EA_INFORMATION)Src)->EaNameLength + 1 + + ((PFILE_FULL_EA_INFORMATION)Src)->EaValueLength; + else + CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + ((PFILE_GET_EA_INFORMATION)Src)->EaNameLength + 1; + + if ((PUINT8)DstBuf + CopyLength > (PUINT8)DstBufEnd) + { + IoStatus->Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlMoveMemory(DstBuf, Src, 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; + DstBuf = (PVOID)((PUINT8)DstBuf + CopyLength); + + if (ReturnSingleEntry) + break; + + DstBuf = (PVOID)FSP_FSCTL_ALIGN_UP((UINT_PTR)DstBuf, sizeof(ULONG)); + } + + IoStatus->Information = NT_SUCCESS(IoStatus->Status) ? + (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn) : 0; +} + +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 && 0 != SrcBuf->NextEntryOffset; + SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset), EaIndex++) + ; + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = 0; + DstBuf = DstBufBgn, PrevDstBuf = 0; + for (; + SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; + SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset), EaIndex++) + { + if ((PUINT8)DstBuf + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > (PUINT8)DstBufEnd) + break; + + 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) + { + CopyLength = (ULONG)((PUINT8)DstBufEnd - (PUINT8)DstBuf); + IoStatus->Status = STATUS_BUFFER_OVERFLOW; + } + + 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; + DstBuf = (PVOID)((PUINT8)DstBuf + CopyLength); + + if (!NT_SUCCESS(IoStatus->Status) || ReturnSingleEntry) + break; + + DstBuf = (PVOID)FSP_FSCTL_ALIGN_UP((UINT_PTR)DstBuf, sizeof(ULONG)); + } + + if (0 != PrevDstBuf) + { + *PEaIndex = EaIndex; + IoStatus->Information = (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn); + } + else + { + if (SrcBufBgn == SrcBuf) + IoStatus->Status = IndexSpecified ? + STATUS_NONEXISTENT_EA_ENTRY : STATUS_NO_EAS_ON_FILE; + else if (SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset) + IoStatus->Status = STATUS_BUFFER_TOO_SMALL; + else + IoStatus->Status = IndexSpecified && *PEaIndex != EaIndex ? + STATUS_NONEXISTENT_EA_ENTRY : STATUS_NO_MORE_EAS; + } +} + +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_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; + + if (0 != EaList) + { + FspFsvolQueryEaGetCopy( + CasePreservedExtendedAttributes, + ReturnSingleEntry, + EaList, EaListLength, + SrcBufBgn, SrcSize, + DstBufBgn, DstSize, + IoStatus); + } + else + { + 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; + } +} + +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 +381,148 @@ 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); + + 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; + + Irp->IoStatus.Information = 0; + Result = IoCheckEaBufferValidity(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 +530,62 @@ 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); + } + + 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 +601,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 +618,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/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'; From b2912460e09a020f99fcfc159625ff77e47d13bb Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 13 Mar 2019 15:04:39 -0700 Subject: [PATCH 03/35] tools: cloc.bat wraps cloc --- tools/cloc.bat | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 tools/cloc.bat 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" . From 58c67081234c5bdaee6db66a02aaa6c9af130161 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 13 Mar 2019 15:15:07 -0700 Subject: [PATCH 04/35] tools: cloc.bat wraps cloc --- src/dll/fsop.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 58e5a94c..b8c1aca5 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1186,13 +1186,34 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { - return STATUS_INVALID_DEVICE_REQUEST; + NTSTATUS Result; + SIZE_T EaSize; + + if (0 == FileSystem->Interface->GetEa) + return STATUS_INVALID_DEVICE_REQUEST; + + EaSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; + Result = FileSystem->Interface->GetEa(FileSystem, + (PVOID)ValOfFileContext(Request->Req.QueryEa), + (PVOID)Response->Buffer, &EaSize); + if (!NT_SUCCESS(Result)) + return STATUS_BUFFER_OVERFLOW != Result ? Result : STATUS_EA_LIST_INCONSISTENT; + + Response->Size = (UINT16)(sizeof *Response + EaSize); + Response->Rsp.QueryEa.Ea.Offset = 0; + Response->Rsp.QueryEa.Ea.Size = (UINT16)EaSize; + return STATUS_SUCCESS; } FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { - return STATUS_INVALID_DEVICE_REQUEST; + 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, From d8686a7726176f839796e341b6dafa5f5a9ebe48 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 13 Mar 2019 21:44:10 -0700 Subject: [PATCH 05/35] tst: memfs: extended attributes support --- inc/winfsp/winfsp.h | 66 ++++++++++- src/dll/fsop.c | 62 +++++++++- tst/memfs/memfs.cpp | 267 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 384 insertions(+), 11 deletions(-) diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index b02cd46e..2d08893f 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -88,6 +88,9 @@ typedef struct _REPARSE_DATA_BUFFER /* * 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; @@ -953,7 +956,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE NTSTATUS (*CreateEx)(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, - PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo); /** * Overwrite a file. @@ -985,7 +988,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE */ NTSTATUS (*OverwriteEx)(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, - PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, FSP_FSCTL_FILE_INFO *FileInfo); /** * Get extended attributes. @@ -996,16 +999,19 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * The file context of the file to get extended attributes for. * @param Ea * Extended attributes buffer. - * @param EaLength [in,out] + * @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, PSIZE_T PEaLength); + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred); /** * Set extended attributes. * @@ -1024,7 +1030,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE */ NTSTATUS (*SetEa)(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, - PFILE_FULL_EA_INFORMATION Ea, SIZE_T EaLength); + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength); /* * This ensures that this interface will always contain 64 function pointers. @@ -1568,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/fsop.c b/src/dll/fsop.c index b8c1aca5..54b25822 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1187,21 +1187,21 @@ FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { NTSTATUS Result; - SIZE_T EaSize; + ULONG BytesTransferred; if (0 == FileSystem->Interface->GetEa) return STATUS_INVALID_DEVICE_REQUEST; - EaSize = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; + BytesTransferred = 0; Result = FileSystem->Interface->GetEa(FileSystem, (PVOID)ValOfFileContext(Request->Req.QueryEa), - (PVOID)Response->Buffer, &EaSize); + (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 + EaSize); + Response->Size = (UINT16)(sizeof *Response + BytesTransferred); Response->Rsp.QueryEa.Ea.Offset = 0; - Response->Rsp.QueryEa.Ea.Size = (UINT16)EaSize; + Response->Rsp.QueryEa.Ea.Size = (UINT16)BytesTransferred; return STATUS_SUCCESS; } @@ -1797,3 +1797,55 @@ 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) +{ + PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + EaLength); + NTSTATUS Result; + + Result = STATUS_SUCCESS; + for (; + EaEnd > Ea && 0 != Ea->NextEntryOffset; + Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset)) + { + 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 EaEnd = (PUINT8)Ea + Length; + ULONG EaLength = sizeof *SingleEa + SingleEa->EaNameLength + SingleEa->EaValueLength; + + Ea = (PVOID)((PUINT8)Ea + *PBytesTransferred); + if ((PUINT8)Ea + EaLength > EaEnd) + return FALSE; + + memcpy(Ea, SingleEa, EaLength); + Ea->NextEntryOffset = FSP_FSCTL_ALIGN_UP(EaLength, sizeof(ULONG)); + *PBytesTransferred += EaLength; + } + else if (sizeof *SingleEa <= *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/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index f361b048..bc0dbecb 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]; @@ -252,6 +287,9 @@ typedef struct _MEMFS_FILE_NODE #if defined(MEMFS_NAMED_STREAMS) struct _MEMFS_FILE_NODE *MainFileNode; #endif +#if defined(MEMFS_EA) + MEMFS_FILE_NODE_EA_MAP *EaMap; +#endif } MEMFS_FILE_NODE; struct MEMFS_FILE_NODE_LESS @@ -311,6 +349,15 @@ NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode) static inline VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) { +#if defined(MEMFS_EA) + 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; + } +#endif #if defined(MEMFS_REPARSE_POINTS) free(FileNode->ReparseData); #endif @@ -351,6 +398,120 @@ 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( + sizeof *FileNodeEa + Ea->EaNameLength + Ea->EaValueLength); + if (0 == FileNodeEa) + return STATUS_INSUFFICIENT_RESOURCES; + memcpy(FileNodeEa, Ea, sizeof *FileNodeEa + Ea->EaNameLength + 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 +1030,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 +1112,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 +1181,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 +1217,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 +1242,15 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, MemfsFileNodeMapEnumerateFree(&Context); #endif +#if defined(MEMFS_EA) + 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 +2085,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 +2115,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 +2210,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 }; From 3dd8ae24a833ad09bc49c8eb13c67cf61f1bf3cb Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 13 Mar 2019 21:45:09 -0700 Subject: [PATCH 06/35] sys: FspFsvolSetEa: check EaName validity --- src/sys/ea.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sys/ea.c b/src/sys/ea.c index e17831a2..4b0f0224 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -502,6 +502,23 @@ static NTSTATUS FspFsvolSetEa( if (!NT_SUCCESS(Result)) return Result; + for (PFILE_FULL_EA_INFORMATION Ea = Buffer, EaEnd = (PVOID)((PUINT8)Ea + Length); + EaEnd > Ea && 0 != Ea->NextEntryOffset; + Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset)) + { + STRING Name; + + Name.Length = Name.MaximumLength = Ea->EaNameLength; + Name.Buffer = Ea->EaName; + + if (!FspEaNameIsValid(&Name)) + { + Result = STATUS_INVALID_EA_NAME; + Irp->IoStatus.Information = (ULONG)((PUINT8)Ea - (PUINT8)Buffer); + return Result; + } + } + FspFileNodeAcquireExclusive(FileNode, Full); Result = FspIopCreateRequestEx(Irp, 0, Length, FspFsvolSetEaRequestFini, From 795caec6792e9be4120ea0911d2833e4bfeda40e Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 13 Mar 2019 21:59:27 -0700 Subject: [PATCH 07/35] tst: memfs: VolumeParams.ExtendedAttributes --- tst/memfs/memfs.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index bc0dbecb..a3bb6b64 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -2316,6 +2316,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) From 3553aec99242f18d5c6d88dadb03caa911db728d Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 14 Mar 2019 15:05:17 -0700 Subject: [PATCH 08/35] dotnet: extended attributes support --- src/dotnet/FileSystemBase.cs | 114 +++++++++++++++++++++++++++++ src/dotnet/FileSystemHost.cs | 73 ++++++++++++++++++- src/dotnet/Interop.cs | 137 ++++++++++++++++++++++++++++++++++- 3 files changed, 319 insertions(+), 5 deletions(-) diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index 87403ac9..6fb0e68d 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -1078,6 +1078,120 @@ 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) + { + Int32 Result; + Result = Create( + FileName, + CreateOptions, + GrantedAccess, + FileAttributes, + SecurityDescriptor, + AllocationSize, + out FileNode, + out FileDesc, + out FileInfo, + out NormalizedName); + if (0 > Result) + return Result; + if (IntPtr.Zero != Ea) + Result = SetEa(FileNode, FileDesc, Ea, EaLength); /* ignore Result */ + return STATUS_SUCCESS; + } + public virtual Int32 OverwriteEx( + Object FileNode, + Object FileDesc, + UInt32 FileAttributes, + Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, + out FileInfo FileInfo) + { + Int32 Result; + Result = Overwrite( + FileNode, + FileDesc, + FileAttributes, + ReplaceFileAttributes, + AllocationSize, + out FileInfo); + if (0 > Result) + return Result; + if (IntPtr.Zero != Ea) + Result = SetEa(FileNode, FileDesc, Ea, EaLength); /* ignore Result */ + return STATUS_SUCCESS; + } + 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..f69e991f 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; + for (; NameLength > I; I++) + P[I] = (Byte)Name[I]; + P[I] = 0; + for (; ValueLength > I; I++) + P[I] = Value[I]; + } + } + } + [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,57 @@ 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 && 0 != P->NextEntryOffset; + P = (FullEaInformation *)(((IntPtr)P).ToInt64() + P->NextEntryOffset)) + { + String EaName = Marshal.PtrToStringAnsi((IntPtr)P->EaName, P->EaNameLength); + Byte[] 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 +1208,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); From d59976bd5d2b13ec6cf61b1e98a8a277314a803e Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 14 Mar 2019 21:36:29 -0700 Subject: [PATCH 09/35] tst: winfsp-tests: ea_create_test --- build/VStudio/testing/winfsp-tests.vcxproj | 1 + .../testing/winfsp-tests.vcxproj.filters | 3 + src/dll/fsop.c | 15 +- tst/memfs/memfs.cpp | 5 +- tst/winfsp-tests/ea-test.c | 128 ++++++++++++++++++ tst/winfsp-tests/winfsp-tests.c | 1 + 6 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 tst/winfsp-tests/ea-test.c 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/src/dll/fsop.c b/src/dll/fsop.c index 54b25822..7621e565 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1826,18 +1826,19 @@ FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa, { if (0 != SingleEa) { + PUINT8 EaPtr = (PUINT8)Ea + FSP_FSCTL_ALIGN_UP(*PBytesTransferred, sizeof(ULONG)); PUINT8 EaEnd = (PUINT8)Ea + Length; - ULONG EaLength = sizeof *SingleEa + SingleEa->EaNameLength + SingleEa->EaValueLength; + ULONG EaLen = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + SingleEa->EaNameLength + 1 + SingleEa->EaValueLength; - Ea = (PVOID)((PUINT8)Ea + *PBytesTransferred); - if ((PUINT8)Ea + EaLength > EaEnd) + if (EaEnd < EaPtr + EaLen) return FALSE; - memcpy(Ea, SingleEa, EaLength); - Ea->NextEntryOffset = FSP_FSCTL_ALIGN_UP(EaLength, sizeof(ULONG)); - *PBytesTransferred += EaLength; + 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 (sizeof *SingleEa <= *PBytesTransferred) + else if ((ULONG)FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) <= *PBytesTransferred) { PUINT8 EaEnd = (PUINT8)Ea + *PBytesTransferred; diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index a3bb6b64..b3fa297b 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -441,10 +441,11 @@ NTSTATUS MemfsFileNodeSetEa( if (0 != Ea->EaValueLength) { FileNodeEa = (FILE_FULL_EA_INFORMATION *)malloc( - sizeof *FileNodeEa + Ea->EaNameLength + Ea->EaValueLength); + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + Ea->EaNameLength + 1 + Ea->EaValueLength); if (0 == FileNodeEa) return STATUS_INSUFFICIENT_RESOURCES; - memcpy(FileNodeEa, Ea, sizeof *FileNodeEa + Ea->EaNameLength + Ea->EaValueLength); + memcpy(FileNodeEa, Ea, + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + Ea->EaNameLength + 1 + Ea->EaValueLength); FileNodeEa->NextEntryOffset = 0; } diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c new file mode 100644 index 00000000..6966b2fe --- /dev/null +++ b/tst/winfsp-tests/ea-test.c @@ -0,0 +1,128 @@ +/** + * @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" + +static void ea_create_dotest(ULONG Flags, PWSTR Prefix) +{ + void *memfs = memfs_start(Flags); + + 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[128]; + } SingleEa; + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[512]; + } Ea; + ULONG EaLength = 0; + + 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); + + memset(&SingleEa, 0, sizeof SingleEa); + SingleEa.V.EaNameLength = (UCHAR)strlen("name1"); + SingleEa.V.EaValueLength = (USHORT)strlen("first"); + lstrcpyA(SingleEa.V.EaName, "name1"); + memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "first", SingleEa.V.EaValueLength); + FspFileSystemAddEa(&SingleEa.V, &Ea.V, sizeof Ea, &EaLength); + + memset(&SingleEa, 0, sizeof SingleEa); + SingleEa.V.EaNameLength = (UCHAR)strlen("nameTwo"); + SingleEa.V.EaValueLength = (USHORT)strlen("second"); + lstrcpyA(SingleEa.V.EaName, "nameTwo"); + memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "second", SingleEa.V.EaValueLength); + FspFileSystemAddEa(&SingleEa.V, &Ea.V, sizeof Ea, &EaLength); + + memset(&SingleEa, 0, sizeof SingleEa); + SingleEa.V.EaNameLength = (UCHAR)strlen("n3"); + SingleEa.V.EaValueLength = (USHORT)strlen("third"); + lstrcpyA(SingleEa.V.EaName, "n3"); + memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "third", SingleEa.V.EaValueLength); + FspFileSystemAddEa(&SingleEa.V, &Ea.V, sizeof Ea, &EaLength); + + FspFileSystemAddEa(0, &Ea.V, sizeof Ea, &EaLength); + + 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, FILE_DELETE_ON_CLOSE, + &Ea, EaLength); + ASSERT(STATUS_SUCCESS == Result); + 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); + } + if (WinFspDiskTests) + ea_create_dotest(MemfsDisk, 0); + if (WinFspNetTests) + ea_create_dotest(MemfsNet, L"\\\\memfs\\share"); +} + +void ea_tests(void) +{ + TEST(ea_create_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); From d3efdd9219fe35149858864f009bbdcc9848811f Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 14 Mar 2019 22:04:31 -0700 Subject: [PATCH 10/35] dll, sys: FSP_NEXT_EA --- src/dll/fsop.c | 11 +++-------- src/dll/library.h | 3 +++ src/dotnet/Interop.cs | 7 ++++--- src/sys/driver.h | 2 ++ src/sys/ea.c | 27 ++++++++------------------- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 7621e565..41e9d2e3 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1805,19 +1805,14 @@ FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem, PVOID Context, PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) { - PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + EaLength); - NTSTATUS Result; - - Result = STATUS_SUCCESS; - for (; - EaEnd > Ea && 0 != Ea->NextEntryOffset; - Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset)) + NTSTATUS Result = STATUS_SUCCESS; + for (PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + EaLength); + EaEnd > Ea; FSP_NEXT_EA(Ea, EaEnd)) { Result = EnumerateEa(FileSystem, Context, Ea); if (!NT_SUCCESS(Result)) break; } - return Result; } 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/Interop.cs b/src/dotnet/Interop.cs index f69e991f..01e1a6af 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -908,9 +908,10 @@ namespace Fsp.Interop FullEaInformation *EndP = (FullEaInformation *)(Ea.ToInt64() + EaLength); Int32 Result; Result = 0/*STATUS_SUCCESS*/; - for (; - EndP > P && 0 != P->NextEntryOffset; - P = (FullEaInformation *)(((IntPtr)P).ToInt64() + P->NextEntryOffset)) + 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 = new Byte[P->EaValueLength]; diff --git a/src/sys/driver.h b/src/sys/driver.h index 02678013..03aa976f 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -573,6 +573,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 diff --git a/src/sys/ea.c b/src/sys/ea.c index 4b0f0224..0edfcee5 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -97,17 +97,13 @@ static VOID FspFsvolQueryEaGetCopy( IoStatus->Status = STATUS_SUCCESS; IoStatus->Information = 0; DstBuf = DstBufBgn, PrevDstBuf = 0; - for (GetBuf = GetBufBgn; - GetBufEnd > GetBuf && 0 != GetBuf->NextEntryOffset; - GetBuf = (PVOID)((PUINT8)GetBuf + GetBuf->NextEntryOffset)) + 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)) + for (Get = GetBufBgn; GetBuf > Get; Get = (PVOID)((PUINT8)Get + Get->NextEntryOffset)) { Name.Length = Name.MaximumLength = Get->EaNameLength; Name.Buffer = Get->EaName; @@ -126,9 +122,7 @@ static VOID FspFsvolQueryEaGetCopy( } Src = GetBuf; - for (SrcBuf = SrcBufBgn; - SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; - SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset)) + for (SrcBuf = SrcBufBgn; SrcBufEnd > SrcBuf; SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd)) { Name.Length = Name.MaximumLength = SrcBuf->EaNameLength; Name.Buffer = SrcBuf->EaName; @@ -201,18 +195,14 @@ static VOID FspFsvolQueryEaIndexCopy( return; } - for (SrcBuf = SrcBufBgn; - EaIndex < *PEaIndex && - SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; - SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset), EaIndex++) + for (SrcBuf = SrcBufBgn; EaIndex < *PEaIndex && SrcBufEnd > SrcBuf; + SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd), EaIndex++) ; IoStatus->Status = STATUS_SUCCESS; IoStatus->Information = 0; DstBuf = DstBufBgn, PrevDstBuf = 0; - for (; - SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset; - SrcBuf = (PVOID)((PUINT8)SrcBuf + SrcBuf->NextEntryOffset), EaIndex++) + for (; SrcBufEnd > SrcBuf; SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd), EaIndex++) { if ((PUINT8)DstBuf + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > (PUINT8)DstBufEnd) break; @@ -256,7 +246,7 @@ static VOID FspFsvolQueryEaIndexCopy( if (SrcBufBgn == SrcBuf) IoStatus->Status = IndexSpecified ? STATUS_NONEXISTENT_EA_ENTRY : STATUS_NO_EAS_ON_FILE; - else if (SrcBufEnd > SrcBuf && 0 != SrcBuf->NextEntryOffset) + else if (SrcBufEnd > SrcBuf) IoStatus->Status = STATUS_BUFFER_TOO_SMALL; else IoStatus->Status = IndexSpecified && *PEaIndex != EaIndex ? @@ -503,8 +493,7 @@ static NTSTATUS FspFsvolSetEa( return Result; for (PFILE_FULL_EA_INFORMATION Ea = Buffer, EaEnd = (PVOID)((PUINT8)Ea + Length); - EaEnd > Ea && 0 != Ea->NextEntryOffset; - Ea = (PVOID)((PUINT8)Ea + Ea->NextEntryOffset)) + EaEnd > Ea; Ea = FSP_NEXT_EA(Ea, EaEnd)) { STRING Name; From 9fd491fa3dbb7e656862ca39c704b768704b39d0 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 11:21:11 -0700 Subject: [PATCH 11/35] sys, dll: ea testing --- src/dll/fsop.c | 2 +- src/sys/create.c | 2 +- tst/winfsp-tests/ea-test.c | 26 +++++++++++++++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 41e9d2e3..dd50a308 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1807,7 +1807,7 @@ FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem, { NTSTATUS Result = STATUS_SUCCESS; for (PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + EaLength); - EaEnd > Ea; FSP_NEXT_EA(Ea, EaEnd)) + EaEnd > Ea; Ea = FSP_NEXT_EA(Ea, EaEnd)) { Result = EnumerateEa(FileSystem, Context, Ea); if (!NT_SUCCESS(Result)) diff --git a/src/sys/create.c b/src/sys/create.c index d60d2bbb..4f8c53d2 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -306,7 +306,7 @@ static NTSTATUS FspFsvolCreateNoLock( if (0 != EaBuffer) { /* does the file system support EA? */ - if (FsvolDeviceExtension->VolumeParams.ExtendedAttributes) + if (!FsvolDeviceExtension->VolumeParams.ExtendedAttributes) return STATUS_EAS_NOT_SUPPORTED; /* do we need EA knowledge? */ diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 6966b2fe..df0ce7f4 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -70,6 +70,7 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix) FspFileSystemAddEa(&SingleEa.V, &Ea.V, sizeof Ea, &EaLength); memset(&SingleEa, 0, sizeof SingleEa); + SingleEa.V.Flags = FILE_NEED_EA; SingleEa.V.EaNameLength = (UCHAR)strlen("nameTwo"); SingleEa.V.EaValueLength = (USHORT)strlen("second"); lstrcpyA(SingleEa.V.EaName, "nameTwo"); @@ -89,11 +90,34 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix) 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, FILE_DELETE_ON_CLOSE, + 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); CloseHandle(FileHandle); From 5aa06358fce327a00ed32aab5e20110d1eabfcfd Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 12:43:14 -0700 Subject: [PATCH 12/35] tst: winfsp-tests: ea_create_test --- tst/winfsp-tests/ea-test.c | 171 +++++++++++++++++++++++++++++++------ 1 file changed, 143 insertions(+), 28 deletions(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index df0ce7f4..b68d1ef9 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -26,6 +26,147 @@ #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); + +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("name1"); + SingleEa.V.EaValueLength = (USHORT)strlen("first"); + lstrcpyA(SingleEa.V.EaName, "name1"); + 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("nameTwo"); + SingleEa.V.EaValueLength = (USHORT)strlen("second"); + lstrcpyA(SingleEa.V.EaName, "nameTwo"); + 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("n3"); + SingleEa.V.EaValueLength = (USHORT)strlen("third"); + lstrcpyA(SingleEa.V.EaName, "n3"); + 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, "NAME1")) + { + ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("NAME1")); + 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, "NAMETWO")) + { + ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("NAMETWO")); + 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, "N3")) + { + ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("N3")); + ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("third")); + ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "third", SingleEa->EaValueLength)); + Context->EaCount[2]++; + } + + 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[128]; + } GetEa; + ULONG EaLength = 0; + struct ea_check_ea_context Context; + + 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]); + + memset(&GetEa, 0, sizeof GetEa); + GetEa.V.EaNameLength = (UCHAR)strlen("nameTwo"); + lstrcpyA(GetEa.V.EaName, "nameTwo"); + EaLength = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + GetEa.V.EaNameLength + 1; + + 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]); +} + static void ea_create_dotest(ULONG Flags, PWSTR Prefix) { void *memfs = memfs_start(Flags); @@ -40,11 +181,6 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix) IO_STATUS_BLOCK Iosb; LARGE_INTEGER LargeZero = { 0 }; union - { - FILE_FULL_EA_INFORMATION V; - UINT8 B[128]; - } SingleEa; - union { FILE_FULL_EA_INFORMATION V; UINT8 B[512]; @@ -62,29 +198,7 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix) FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0); ASSERT(INVALID_HANDLE_VALUE != DirHandle); - memset(&SingleEa, 0, sizeof SingleEa); - SingleEa.V.EaNameLength = (UCHAR)strlen("name1"); - SingleEa.V.EaValueLength = (USHORT)strlen("first"); - lstrcpyA(SingleEa.V.EaName, "name1"); - memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "first", SingleEa.V.EaValueLength); - FspFileSystemAddEa(&SingleEa.V, &Ea.V, sizeof Ea, &EaLength); - - memset(&SingleEa, 0, sizeof SingleEa); - SingleEa.V.Flags = FILE_NEED_EA; - SingleEa.V.EaNameLength = (UCHAR)strlen("nameTwo"); - SingleEa.V.EaValueLength = (USHORT)strlen("second"); - lstrcpyA(SingleEa.V.EaName, "nameTwo"); - memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "second", SingleEa.V.EaValueLength); - FspFileSystemAddEa(&SingleEa.V, &Ea.V, sizeof Ea, &EaLength); - - memset(&SingleEa, 0, sizeof SingleEa); - SingleEa.V.EaNameLength = (UCHAR)strlen("n3"); - SingleEa.V.EaValueLength = (USHORT)strlen("third"); - lstrcpyA(SingleEa.V.EaName, "n3"); - memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "third", SingleEa.V.EaValueLength); - FspFileSystemAddEa(&SingleEa.V, &Ea.V, sizeof Ea, &EaLength); - - FspFileSystemAddEa(0, &Ea.V, sizeof Ea, &EaLength); + ea_init_ea(&Ea.V, sizeof Ea, &EaLength); UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR); UnicodePath.MaximumLength = sizeof UnicodePathBuf; @@ -119,6 +233,7 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix) FILE_OPEN, FILE_DELETE_ON_CLOSE, 0, 0); ASSERT(STATUS_SUCCESS == Result); + ea_check_ea(FileHandle); CloseHandle(FileHandle); CloseHandle(DirHandle); From c4ef64e31f891810c6ffdacf6f7f9f9757a667cf Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 12:47:36 -0700 Subject: [PATCH 13/35] tst: winfsp-tests: ea_create_test --- tst/winfsp-tests/ea-test.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index b68d1ef9..787288f1 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -167,9 +167,9 @@ static void ea_check_ea(HANDLE Handle) ASSERT(0 == Context.EaCount[2]); } -static void ea_create_dotest(ULONG Flags, PWSTR Prefix) +static void ea_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) { - void *memfs = memfs_start(Flags); + void *memfs = memfs_start_ex(Flags, FileInfoTimeout); HANDLE DirHandle, FileHandle; NTSTATUS Result; @@ -253,12 +253,18 @@ static void ea_create_test(void) { WCHAR DirBuf[MAX_PATH]; GetTestDirectory(DirBuf); - ea_create_dotest(-1, DirBuf); + ea_create_dotest(-1, DirBuf, 0); } if (WinFspDiskTests) - ea_create_dotest(MemfsDisk, 0); + { + ea_create_dotest(MemfsDisk, 0, 0); + ea_create_dotest(MemfsDisk, 0, 1000); + } if (WinFspNetTests) - ea_create_dotest(MemfsNet, L"\\\\memfs\\share"); + { + ea_create_dotest(MemfsNet, L"\\\\memfs\\share", 0); + ea_create_dotest(MemfsNet, L"\\\\memfs\\share", 1000); + } } void ea_tests(void) From 351285f5c6ab0f4f4d28191413f82f78f5fb2b10 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 13:44:45 -0700 Subject: [PATCH 14/35] tst: winfsp-tests: ea_create_test --- tst/winfsp-tests/ea-test.c | 76 +++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 787288f1..a17cc8e4 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -59,24 +59,24 @@ static void ea_init_ea( } SingleEa; memset(&SingleEa, 0, sizeof SingleEa); - SingleEa.V.EaNameLength = (UCHAR)strlen("name1"); + SingleEa.V.EaNameLength = (UCHAR)strlen("Aname1"); SingleEa.V.EaValueLength = (USHORT)strlen("first"); - lstrcpyA(SingleEa.V.EaName, "name1"); + 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("nameTwo"); + SingleEa.V.EaNameLength = (UCHAR)strlen("bnameTwo"); SingleEa.V.EaValueLength = (USHORT)strlen("second"); - lstrcpyA(SingleEa.V.EaName, "nameTwo"); + 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("n3"); + SingleEa.V.EaNameLength = (UCHAR)strlen("Cn3"); SingleEa.V.EaValueLength = (USHORT)strlen("third"); - lstrcpyA(SingleEa.V.EaName, "n3"); + lstrcpyA(SingleEa.V.EaName, "Cn3"); memcpy(SingleEa.V.EaName + SingleEa.V.EaNameLength + 1, "third", SingleEa.V.EaValueLength); FspFileSystemAddEa(&SingleEa.V, Ea, EaLength, PBytesTransferred); @@ -95,25 +95,25 @@ static NTSTATUS ea_check_ea_enumerate( { struct ea_check_ea_context *Context = Context0; - if (0 == strcmp(SingleEa->EaName, "NAME1")) + if (0 == strcmp(SingleEa->EaName, "ANAME1")) { - ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("NAME1")); + 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, "NAMETWO")) + if (0 == strcmp(SingleEa->EaName, "BNAMETWO")) { - ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("NAMETWO")); + 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, "N3")) + if (0 == strcmp(SingleEa->EaName, "CN3")) { - ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("N3")); + 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]++; @@ -152,8 +152,8 @@ static void ea_check_ea(HANDLE Handle) ASSERT(1 == Context.EaCount[2]); memset(&GetEa, 0, sizeof GetEa); - GetEa.V.EaNameLength = (UCHAR)strlen("nameTwo"); - lstrcpyA(GetEa.V.EaName, "nameTwo"); + GetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo"); + lstrcpyA(GetEa.V.EaName, "bnameTwo"); EaLength = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + GetEa.V.EaNameLength + 1; memset(&Context, 0, sizeof Context); @@ -165,6 +165,54 @@ static void ea_check_ea(HANDLE Handle) ASSERT(0 == Context.EaCount[0]); ASSERT(1 == Context.EaCount[1]); ASSERT(0 == Context.EaCount[2]); + + 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]); + + 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]); + + 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]); + + 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]); } static void ea_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) From 1aa2353ca682ab6edeec11726150c1b41e5d4f28 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 14:04:03 -0700 Subject: [PATCH 15/35] tst: winfsp-tests: ea_create_test --- tst/winfsp-tests/ea-test.c | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index a17cc8e4..9f460733 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -151,6 +151,30 @@ static void ea_check_ea(HANDLE Handle) ASSERT(1 == Context.EaCount[1]); ASSERT(1 == Context.EaCount[2]); + 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]); + + 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]); + memset(&GetEa, 0, sizeof GetEa); GetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo"); lstrcpyA(GetEa.V.EaName, "bnameTwo"); @@ -166,6 +190,12 @@ static void ea_check_ea(HANDLE Handle) ASSERT(1 == Context.EaCount[1]); ASSERT(0 == Context.EaCount[2]); + 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")), @@ -190,6 +220,12 @@ static void ea_check_ea(HANDLE Handle) ASSERT(0 == Context.EaCount[1]); ASSERT(0 == Context.EaCount[2]); + 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")), @@ -202,6 +238,12 @@ static void ea_check_ea(HANDLE Handle) ASSERT(1 == Context.EaCount[1]); ASSERT(0 == Context.EaCount[2]); + 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")), @@ -213,6 +255,10 @@ static void ea_check_ea(HANDLE Handle) ASSERT(0 == Context.EaCount[0]); ASSERT(0 == Context.EaCount[1]); ASSERT(1 == Context.EaCount[2]); + + memset(&Context, 0, sizeof Context); + Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE); + ASSERT(STATUS_NO_MORE_EAS == Result); } static void ea_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) From ef6df51b5efab13f59bcc72a6471dd118cbac072 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 14:21:17 -0700 Subject: [PATCH 16/35] tst: winfsp-tests: ea_create_test --- tst/winfsp-tests/ea-test.c | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 9f460733..b2cad7fd 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -139,6 +139,7 @@ static void ea_check_ea(HANDLE Handle) UINT8 B[128]; } GetEa; ULONG EaLength = 0; + ULONG EaIndex; struct ea_check_ea_context Context; memset(&Context, 0, sizeof Context); @@ -190,6 +191,13 @@ static void ea_check_ea(HANDLE Handle) ASSERT(1 == Context.EaCount[1]); ASSERT(0 == Context.EaCount[2]); + 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); + memset(&Context, 0, sizeof Context); Result = NtQueryEaFile(Handle, &Iosb, &Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1), @@ -259,6 +267,87 @@ static void ea_check_ea(HANDLE Handle) 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]); + + 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]); + + 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]); + + 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_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) From cdb1ca22fcd065cc071d2bcc6ad44299344580d4 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 15:29:37 -0700 Subject: [PATCH 17/35] tst: winfsp-tests: ea_getset_test --- tst/winfsp-tests/ea-test.c | 89 +++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index b2cad7fd..63438a31 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -450,7 +450,94 @@ static void ea_create_test(void) } } +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 = 0; + + ea_init_ea(&Ea.V, sizeof Ea, &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); + + Result = NtSetEaFile(DirHandle, &Iosb, &Ea, EaLength); + ASSERT(STATUS_SUCCESS == Result); + ASSERT(0 == Iosb.Information); + ea_check_ea(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); + + Result = NtSetEaFile(FileHandle, &Iosb, &Ea, EaLength); + ASSERT(STATUS_SUCCESS == Result); + ASSERT(0 == Iosb.Information); + ea_check_ea(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(ea_create_test); + TEST_OPT(ea_create_test); + TEST_OPT(ea_getset_test); } From 67bd49d5d43b666e55ed4fb165de3a1cdaa72a37 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 15 Mar 2019 16:43:54 -0700 Subject: [PATCH 18/35] tst: winfsp-tests: ea_getset_test --- tst/winfsp-tests/ea-test.c | 59 ++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 63438a31..f074bc0e 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -142,6 +142,43 @@ static void ea_check_ea(HANDLE Handle) ULONG EaIndex; struct ea_check_ea_context Context; + memset(&GetEa, 0, sizeof GetEa); + GetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo"); + lstrcpyA(GetEa.V.EaName, "bnameTwo"); + EaLength = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + GetEa.V.EaNameLength + 1; + + 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]); + + 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); + + memset(&GetEa, 0, sizeof GetEa); + GetEa.V.EaNameLength = (UCHAR)strlen("nonexistent"); + lstrcpyA(GetEa.V.EaName, "nonexistent"); + EaLength = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + GetEa.V.EaNameLength + 1; + + 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]); + memset(&Context, 0, sizeof Context); Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE); ASSERT(STATUS_SUCCESS == Result); @@ -176,28 +213,6 @@ static void ea_check_ea(HANDLE Handle) ASSERT(1 == Context.EaCount[1]); ASSERT(0 == Context.EaCount[2]); - memset(&GetEa, 0, sizeof GetEa); - GetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo"); - lstrcpyA(GetEa.V.EaName, "bnameTwo"); - EaLength = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + GetEa.V.EaNameLength + 1; - - 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]); - - 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); - memset(&Context, 0, sizeof Context); Result = NtQueryEaFile(Handle, &Iosb, &Ea, (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + strlen("Aname1") + 1), From 91568edc451bea4dab96a93413141a4b70d7fa2b Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sat, 16 Mar 2019 00:04:00 -0700 Subject: [PATCH 19/35] sys: ea: testing --- src/sys/ea.c | 101 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/src/sys/ea.c b/src/sys/ea.c index 0edfcee5..7f5a6b63 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -90,12 +90,12 @@ static VOID FspFsvolQueryEaGetCopy( 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; - PVOID Src; + PFILE_FULL_EA_INFORMATION Src; STRING GetName, Name; ULONG CopyLength; - IoStatus->Status = STATUS_SUCCESS; IoStatus->Information = 0; + DstBuf = DstBufBgn, PrevDstBuf = 0; for (GetBuf = GetBufBgn; GetBufEnd > GetBuf; GetBuf = FSP_NEXT_EA(GetBuf, GetBufEnd)) { @@ -118,10 +118,10 @@ static VOID FspFsvolQueryEaGetCopy( { IoStatus->Status = STATUS_INVALID_EA_NAME; IoStatus->Information = (ULONG)((PUINT8)GetBuf - (PUINT8)GetBufBgn); - break; + return; } - Src = GetBuf; + Src = 0; for (SrcBuf = SrcBufBgn; SrcBufEnd > SrcBuf; SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd)) { Name.Length = Name.MaximumLength = SrcBuf->EaNameLength; @@ -134,21 +134,29 @@ static VOID FspFsvolQueryEaGetCopy( } } - if (GetBuf != Src) + if (0 != Src) CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + - ((PFILE_FULL_EA_INFORMATION)Src)->EaNameLength + 1 + - ((PFILE_FULL_EA_INFORMATION)Src)->EaValueLength; + Src->EaNameLength + 1 + Src->EaValueLength; else CopyLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + - ((PFILE_GET_EA_INFORMATION)Src)->EaNameLength + 1; + GetBuf->EaNameLength + 1; if ((PUINT8)DstBuf + CopyLength > (PUINT8)DstBufEnd) { IoStatus->Status = STATUS_BUFFER_OVERFLOW; - break; + IoStatus->Information = 0; + return; } - RtlMoveMemory(DstBuf, Src, CopyLength); + 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) { @@ -159,16 +167,14 @@ static VOID FspFsvolQueryEaGetCopy( if (0 != PrevDstBuf) PrevDstBuf->NextEntryOffset = (ULONG)((PUINT8)DstBuf - (PUINT8)PrevDstBuf); PrevDstBuf = DstBuf; - DstBuf = (PVOID)((PUINT8)DstBuf + CopyLength); + IoStatus->Information = (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn + CopyLength); + DstBuf = (PVOID)((PUINT8)DstBuf + FSP_FSCTL_ALIGN_UP(CopyLength, sizeof(ULONG))); if (ReturnSingleEntry) break; - - DstBuf = (PVOID)FSP_FSCTL_ALIGN_UP((UINT_PTR)DstBuf, sizeof(ULONG)); } - IoStatus->Information = NT_SUCCESS(IoStatus->Status) ? - (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn) : 0; + IoStatus->Status = STATUS_SUCCESS; } static VOID FspFsvolQueryEaIndexCopy( @@ -196,25 +202,25 @@ static VOID FspFsvolQueryEaIndexCopy( } for (SrcBuf = SrcBufBgn; EaIndex < *PEaIndex && SrcBufEnd > SrcBuf; - SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd), EaIndex++) - ; + SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd)) + EaIndex++; - IoStatus->Status = STATUS_SUCCESS; IoStatus->Information = 0; - DstBuf = DstBufBgn, PrevDstBuf = 0; - for (; SrcBufEnd > SrcBuf; SrcBuf = FSP_NEXT_EA(SrcBuf, SrcBufEnd), EaIndex++) - { - if ((PUINT8)DstBuf + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > (PUINT8)DstBufEnd) - break; + 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) { - CopyLength = (ULONG)((PUINT8)DstBufEnd - (PUINT8)DstBuf); - IoStatus->Status = STATUS_BUFFER_OVERFLOW; + if (0 != PrevDstBuf) + break; + IoStatus->Status = STATUS_BUFFER_TOO_SMALL; + IoStatus->Information = 0; + return; } RtlMoveMemory(DstBuf, SrcBuf, CopyLength); @@ -228,29 +234,44 @@ static VOID FspFsvolQueryEaIndexCopy( if (0 != PrevDstBuf) PrevDstBuf->NextEntryOffset = (ULONG)((PUINT8)DstBuf - (PUINT8)PrevDstBuf); PrevDstBuf = DstBuf; - DstBuf = (PVOID)((PUINT8)DstBuf + CopyLength); + IoStatus->Information = (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn + CopyLength); + DstBuf = (PVOID)((PUINT8)DstBuf + FSP_FSCTL_ALIGN_UP(CopyLength, sizeof(ULONG))); - if (!NT_SUCCESS(IoStatus->Status) || ReturnSingleEntry) + EaIndex++; + + if (ReturnSingleEntry) break; - - DstBuf = (PVOID)FSP_FSCTL_ALIGN_UP((UINT_PTR)DstBuf, sizeof(ULONG)); } - if (0 != PrevDstBuf) + if (IndexSpecified) { - *PEaIndex = EaIndex; - IoStatus->Information = (ULONG)((PUINT8)DstBuf - (PUINT8)DstBufBgn); + 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 (SrcBufBgn == SrcBuf) - IoStatus->Status = IndexSpecified ? - STATUS_NONEXISTENT_EA_ENTRY : STATUS_NO_EAS_ON_FILE; - else if (SrcBufEnd > SrcBuf) - IoStatus->Status = STATUS_BUFFER_TOO_SMALL; + if (0 != PrevDstBuf) + { + *PEaIndex = EaIndex; + IoStatus->Status = SrcBufEnd > SrcBuf && !ReturnSingleEntry ? + STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; + } else - IoStatus->Status = IndexSpecified && *PEaIndex != EaIndex ? - STATUS_NONEXISTENT_EA_ENTRY : STATUS_NO_MORE_EAS; + { + IoStatus->Status = SrcBufBgn == SrcBuf ? + STATUS_NO_EAS_ON_FILE : STATUS_NO_MORE_EAS; + IoStatus->Information = 0; + } } } @@ -447,6 +468,8 @@ NTSTATUS FspFsvolQueryEaComplete( FspFileNodeRelease(FileNode, Main); + Result = Irp->IoStatus.Status; + FSP_LEAVE_IOC("FileObject=%p", IrpSp->FileObject); } From 9e5d75fadc37e2b8c9915e6e005890c47f5af6c3 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sat, 16 Mar 2019 13:58:09 -0700 Subject: [PATCH 20/35] sys: util: FspEaBufferAndNamesValid --- src/sys/create.c | 3 +-- src/sys/driver.h | 4 ++++ src/sys/ea.c | 21 +-------------------- src/sys/util.c | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/sys/create.c b/src/sys/create.c index 4f8c53d2..99bf2f3e 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -314,8 +314,7 @@ static NTSTATUS FspFsvolCreateNoLock( return STATUS_ACCESS_DENIED; /* is the EA buffer valid? */ - Irp->IoStatus.Information = 0; - Result = IoCheckEaBufferValidity(EaBuffer, EaLength, (PULONG)&Irp->IoStatus.Information); + Result = FspEaBufferAndNamesValid(EaBuffer, EaLength, (PULONG)&Irp->IoStatus.Information); if (!NT_SUCCESS(Result)) return Result; } diff --git a/src/sys/driver.h b/src/sys/driver.h index 03aa976f..f23169ed 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -515,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, diff --git a/src/sys/ea.c b/src/sys/ea.c index 7f5a6b63..92340442 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -506,31 +506,12 @@ static NTSTATUS FspFsvolSetEa( Result = FspBufferUserBuffer(Irp, Length, IoReadAccess); if (!NT_SUCCESS(Result)) return Result; - Buffer = Irp->AssociatedIrp.SystemBuffer; - Irp->IoStatus.Information = 0; - Result = IoCheckEaBufferValidity(Buffer, Length, - (PULONG)&Irp->IoStatus.Information); + Result = FspEaBufferAndNamesValid(Buffer, Length, (PULONG)&Irp->IoStatus.Information); 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)) - { - Result = STATUS_INVALID_EA_NAME; - Irp->IoStatus.Information = (ULONG)((PUINT8)Ea - (PUINT8)Buffer); - return Result; - } - } - FspFileNodeAcquireExclusive(FileNode, Full); Result = FspIopCreateRequestEx(Irp, 0, Length, FspFsvolSetEaRequestFini, 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(); From 41c3465f2ad7c68fb4f07b1734995e93a3cee655 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 17 Mar 2019 12:21:32 -0700 Subject: [PATCH 21/35] tst: winfsp-tests: ea --- tst/winfsp-tests/ea-test.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index f074bc0e..6d1050e1 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -119,6 +119,13 @@ static NTSTATUS ea_check_ea_enumerate( Context->EaCount[2]++; } + if (0 == strcmp(SingleEa->EaName, "NONEXISTENT")) + { + ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("NONEXISTENT")); + ASSERT(SingleEa->EaValueLength == 0); + Context->EaCount[3]++; + } + Context->Count++; return STATUS_SUCCESS; @@ -156,6 +163,7 @@ static void ea_check_ea(HANDLE Handle) 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, @@ -178,6 +186,7 @@ static void ea_check_ea(HANDLE Handle) ASSERT(0 == Context.EaCount[0]); ASSERT(0 == Context.EaCount[1]); ASSERT(0 == Context.EaCount[2]); + ASSERT(1 == Context.EaCount[3]); memset(&Context, 0, sizeof Context); Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE); @@ -188,6 +197,7 @@ static void ea_check_ea(HANDLE Handle) 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); @@ -202,6 +212,7 @@ static void ea_check_ea(HANDLE Handle) 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); @@ -212,6 +223,7 @@ static void ea_check_ea(HANDLE Handle) 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, @@ -230,6 +242,7 @@ static void ea_check_ea(HANDLE Handle) 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, @@ -242,6 +255,7 @@ static void ea_check_ea(HANDLE Handle) 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, @@ -260,6 +274,7 @@ static void ea_check_ea(HANDLE Handle) 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, @@ -278,6 +293,7 @@ static void ea_check_ea(HANDLE Handle) 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); @@ -309,6 +325,7 @@ static void ea_check_ea(HANDLE Handle) 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; @@ -329,6 +346,7 @@ static void ea_check_ea(HANDLE Handle) 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; @@ -349,6 +367,7 @@ static void ea_check_ea(HANDLE Handle) 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; From 92dfb0be961411e6a5e00b878d53e0dcc06b37c3 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 17 Mar 2019 12:39:00 -0700 Subject: [PATCH 22/35] tst: winfsp-tests: ea --- tst/winfsp-tests/ea-test.c | 68 ++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 6d1050e1..cbbab08c 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -83,6 +83,40 @@ static void ea_init_ea( 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; @@ -402,7 +436,7 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) FILE_FULL_EA_INFORMATION V; UINT8 B[512]; } Ea; - ULONG EaLength = 0; + ULONG EaLength; StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); @@ -415,13 +449,23 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0); ASSERT(INVALID_HANDLE_VALUE != DirHandle); - ea_init_ea(&Ea.V, sizeof Ea, &EaLength); - 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, @@ -498,9 +542,7 @@ static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) FILE_FULL_EA_INFORMATION V; UINT8 B[512]; } Ea; - ULONG EaLength = 0; - - ea_init_ea(&Ea.V, sizeof Ea, &EaLength); + ULONG EaLength; StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); @@ -513,6 +555,13 @@ static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) 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); @@ -534,6 +583,13 @@ static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) 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); From 1dfbb0d9bff401d0258287a3a6afbe5e2ad1a8ae Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 17 Mar 2019 13:08:12 -0700 Subject: [PATCH 23/35] tst: winfsp-tests: ea --- tst/winfsp-tests/ea-test.c | 79 +++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index cbbab08c..57db65a5 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -117,6 +117,36 @@ static void ea_init_bad_ea( FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred); } +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; +} + struct ea_check_ea_context { ULONG Count; @@ -177,16 +207,23 @@ static void ea_check_ea(HANDLE Handle) union { FILE_GET_EA_INFORMATION V; - UINT8 B[128]; + 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; - memset(&GetEa, 0, sizeof GetEa); - GetEa.V.EaNameLength = (UCHAR)strlen("bnameTwo"); - lstrcpyA(GetEa.V.EaName, "bnameTwo"); - EaLength = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + GetEa.V.EaNameLength + 1; + 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); @@ -206,10 +243,12 @@ static void ea_check_ea(HANDLE Handle) ASSERT(STATUS_BUFFER_OVERFLOW == Result); ASSERT(0 == Iosb.Information); - memset(&GetEa, 0, sizeof GetEa); - GetEa.V.EaNameLength = (UCHAR)strlen("nonexistent"); - lstrcpyA(GetEa.V.EaName, "nonexistent"); - EaLength = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + GetEa.V.EaNameLength + 1; + 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); @@ -222,6 +261,28 @@ static void ea_check_ea(HANDLE Handle) 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]); + memset(&Context, 0, sizeof Context); Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE); ASSERT(STATUS_SUCCESS == Result); From c6e1b15b37cd6d2aac571896f9175d55f5ce3739 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 17 Mar 2019 13:13:12 -0700 Subject: [PATCH 24/35] tst: winfsp-tests: ea --- tst/winfsp-tests/ea-test.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 57db65a5..3ccdbee2 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -283,6 +283,28 @@ static void ea_check_ea(HANDLE Handle) 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("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, sizeof Ea, FALSE, 0, 0, 0, FALSE); ASSERT(STATUS_SUCCESS == Result); From 7ee289fb13e88354da5a1bbeb2cb7b00ade8831c Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 17 Mar 2019 13:25:29 -0700 Subject: [PATCH 25/35] tst: winfsp-tests: ea --- tst/winfsp-tests/ea-test.c | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 3ccdbee2..5fa4d8d4 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -283,6 +283,35 @@ static void ea_check_ea(HANDLE Handle) 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"); @@ -305,6 +334,21 @@ static void ea_check_ea(HANDLE Handle) 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); From 4d4bf92c32031d8e605315f66d31b464aa0bd155 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sun, 17 Mar 2019 17:17:30 -0700 Subject: [PATCH 26/35] tst: memfs: cosmetic change --- tst/memfs/memfs.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index b3fa297b..d4bec79e 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -282,14 +282,14 @@ 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) struct _MEMFS_FILE_NODE *MainFileNode; #endif -#if defined(MEMFS_EA) - MEMFS_FILE_NODE_EA_MAP *EaMap; -#endif } MEMFS_FILE_NODE; struct MEMFS_FILE_NODE_LESS From ad612c535da047c848ef2b1157ffd1dac1aa86c2 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 18 Mar 2019 13:51:58 -0700 Subject: [PATCH 27/35] tst: memfs-dotnet: ea support --- src/dotnet/FileSystemBase.cs | 16 +----- tst/memfs-dotnet/Program.cs | 103 ++++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 16 deletions(-) diff --git a/src/dotnet/FileSystemBase.cs b/src/dotnet/FileSystemBase.cs index 6fb0e68d..1991d188 100644 --- a/src/dotnet/FileSystemBase.cs +++ b/src/dotnet/FileSystemBase.cs @@ -1092,8 +1092,7 @@ namespace Fsp out FileInfo FileInfo, out String NormalizedName) { - Int32 Result; - Result = Create( + return Create( FileName, CreateOptions, GrantedAccess, @@ -1104,11 +1103,6 @@ namespace Fsp out FileDesc, out FileInfo, out NormalizedName); - if (0 > Result) - return Result; - if (IntPtr.Zero != Ea) - Result = SetEa(FileNode, FileDesc, Ea, EaLength); /* ignore Result */ - return STATUS_SUCCESS; } public virtual Int32 OverwriteEx( Object FileNode, @@ -1120,19 +1114,13 @@ namespace Fsp UInt32 EaLength, out FileInfo FileInfo) { - Int32 Result; - Result = Overwrite( + return Overwrite( FileNode, FileDesc, FileAttributes, ReplaceFileAttributes, AllocationSize, out FileInfo); - if (0 > Result) - return Result; - if (IntPtr.Zero != Ea) - Result = SetEa(FileNode, FileDesc, Ea, EaLength); /* ignore Result */ - return STATUS_SUCCESS; } public virtual Int32 GetEa( Object FileNode, diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index 70bce965..ad224c56 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; } @@ -309,13 +323,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 +368,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 +415,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 +435,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 +460,13 @@ namespace memfs FileNodeMap.Remove(StreamNode); } + 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 +1062,62 @@ 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); + EaValueData Data; + Data.EaValue = EaValue; + Data.NeedEa = NeedEa; + EaMap[EaName] = Data; + return STATUS_SUCCESS; + } private FileNodeMap FileNodeMap; private UInt32 MaxFileNodes; From 5c42377c1b1fa967284b33d736709ddabdd2e334 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 18 Mar 2019 18:43:18 -0700 Subject: [PATCH 28/35] tst: memfs-dotnet: ea support --- tst/winfsp-tests/ea-test.c | 186 +++++++++++++++++++++++++++++++------ 1 file changed, 156 insertions(+), 30 deletions(-) diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 5fa4d8d4..09868e7d 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -49,6 +49,36 @@ NTSYSAPI NTSTATUS NTAPI NtSetEaFile( 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) { @@ -117,36 +147,6 @@ static void ea_init_bad_ea( FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred); } -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; -} - struct ea_check_ea_context { ULONG Count; @@ -161,6 +161,7 @@ static NTSTATUS ea_check_ea_enumerate( 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)); @@ -169,6 +170,7 @@ static NTSTATUS ea_check_ea_enumerate( 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)); @@ -177,6 +179,7 @@ static NTSTATUS ea_check_ea_enumerate( 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)); @@ -185,6 +188,7 @@ static NTSTATUS ea_check_ea_enumerate( if (0 == strcmp(SingleEa->EaName, "NONEXISTENT")) { + ASSERT(0 == SingleEa->Flags); ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("NONEXISTENT")); ASSERT(SingleEa->EaValueLength == 0); Context->EaCount[3]++; @@ -545,6 +549,114 @@ static void ea_check_ea(HANDLE Handle) 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); @@ -694,6 +806,13 @@ static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) 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); + ea_check_ea2(DirHandle); + CloseHandle(DirHandle); DirHandle = CreateFileW(FilePath, @@ -722,6 +841,13 @@ static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) 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); + ea_check_ea2(FileHandle); + CloseHandle(FileHandle); FileHandle = CreateFileW(FilePath, From 02a650f8d0a73f86732ee79d83c44dd92d508996 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Mar 2019 11:15:14 -0700 Subject: [PATCH 29/35] sys: ea: return STATUS_EA_CORRUPT_ERROR when appropriate --- src/sys/driver.h | 2 ++ src/sys/ea.c | 17 +++++++++++++++++ tst/winfsp-tests/ea-test.c | 4 ++++ 3 files changed, 23 insertions(+) diff --git a/src/sys/driver.h b/src/sys/driver.h index f23169ed..391e1be9 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -1288,6 +1288,7 @@ typedef struct FSP_FILE_NODE ULONG StreamInfoChangeNumber; UINT64 Ea; ULONG EaChangeNumber; + ULONG EaChangeCount; BOOLEAN TruncateOnClose; FILE_LOCK FileLock; #if (NTDDI_VERSION < NTDDI_WIN8) @@ -1327,6 +1328,7 @@ typedef struct UINT64 DirInfo; ULONG DirInfoCacheHint; ULONG EaIndex; + ULONG EaChangeCount; /* stream support */ HANDLE MainFileHandle; PFILE_OBJECT MainFileObject; diff --git a/src/sys/ea.c b/src/sys/ea.c index 92340442..0b5462b4 100644 --- a/src/sys/ea.c +++ b/src/sys/ea.c @@ -285,6 +285,7 @@ static VOID FspFsvolQueryEaCopy( 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); @@ -293,6 +294,8 @@ static VOID FspFsvolQueryEaCopy( ULONG EaListLength = IrpSp->Parameters.QueryEa.EaListLength; ULONG EaIndex; + ASSERT(FileNode == FileDesc->FileNode); + if (0 != EaList) { FspFsvolQueryEaGetCopy( @@ -305,6 +308,15 @@ static VOID FspFsvolQueryEaCopy( } 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) @@ -319,7 +331,10 @@ static VOID FspFsvolQueryEaCopy( DstBufBgn, DstSize, IoStatus); if (NT_SUCCESS(IoStatus->Status) || STATUS_BUFFER_OVERFLOW == IoStatus->Status) + { FileDesc->EaIndex = EaIndex; + FileDesc->EaChangeCount = FileNode->EaChangeCount; + } } } @@ -576,6 +591,8 @@ NTSTATUS FspFsvolSetEaComplete( FspFileNodeSetEa(FileNode, 0, 0); } + FileNode->EaChangeCount++; + FspFileNodeNotifyChange(FileNode, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, FALSE); FspIopRequestContext(Request, RequestFileNode) = 0; diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 09868e7d..8abff58f 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -645,6 +645,10 @@ static void ea_check_ea2(HANDLE Handle) } Ea; struct ea_check_ea_context Context; + memset(&Context, 0, sizeof Context); + Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE); + ASSERT(STATUS_EA_CORRUPT_ERROR == Result); + memset(&Context, 0, sizeof Context); Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, TRUE); ASSERT(STATUS_SUCCESS == Result); From a830de9d0407f1d116dc214b098a7bb0278c3d1b Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Mar 2019 13:22:35 -0700 Subject: [PATCH 30/35] sys: create: overwrite EA support --- src/sys/create.c | 10 +++++++++- tst/memfs-dotnet/Program.cs | 3 +++ tst/memfs/memfs.cpp | 15 ++++++++++++--- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/sys/create.c b/src/sys/create.c index 99bf2f3e..1ce68201 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -1171,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/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index ad224c56..aab75778 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -460,6 +460,9 @@ 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); diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index d4bec79e..a0a9df47 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -346,10 +346,10 @@ NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode) return STATUS_SUCCESS; } -static inline -VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) -{ #if defined(MEMFS_EA) +static inline +VOID MemfsFileNodeDeleteAllEa(MEMFS_FILE_NODE *FileNode) +{ if (0 != FileNode->EaMap) { for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end(); @@ -357,6 +357,14 @@ VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) free(p->second); delete FileNode->EaMap; } +} +#endif + +static inline +VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) +{ +#if defined(MEMFS_EA) + MemfsFileNodeDeleteAllEa(FileNode); #endif #if defined(MEMFS_REPARSE_POINTS) free(FileNode->ReparseData); @@ -1244,6 +1252,7 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, #endif #if defined(MEMFS_EA) + MemfsFileNodeDeleteAllEa(FileNode); if (0 != Ea) { Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength); From ff94a63c37fce33cf76e1d0dfb405500c0583d66 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Mar 2019 14:21:25 -0700 Subject: [PATCH 31/35] tst: winfsp-tests: Overwrite: EA support --- src/dotnet/Interop.cs | 10 +++- tst/memfs-dotnet/Program.cs | 13 +++-- tst/memfs/memfs.cpp | 7 ++- tst/winfsp-tests/ea-test.c | 106 ++++++++++++++++++++++++++++++++++-- 4 files changed, 122 insertions(+), 14 deletions(-) diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index 01e1a6af..6678cb35 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -914,9 +914,13 @@ namespace Fsp.Interop EndP) { String EaName = Marshal.PtrToStringAnsi((IntPtr)P->EaName, P->EaNameLength); - Byte[] EaValue = new Byte[P->EaValueLength]; - Marshal.Copy((IntPtr)(((IntPtr)P->EaName).ToInt64() + P->EaNameLength + 1), - EaValue, 0, P->EaValueLength); + 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) diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index aab75778..60a083a4 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -1115,10 +1115,15 @@ namespace memfs { FileNode FileNode = (FileNode)FileNode0; SortedDictionary EaMap = FileNode.GetEaMap(true); - EaValueData Data; - Data.EaValue = EaValue; - Data.NeedEa = NeedEa; - EaMap[EaName] = Data; + if (null != EaValue) + { + EaValueData Data; + Data.EaValue = EaValue; + Data.NeedEa = NeedEa; + EaMap[EaName] = Data; + } + else + EaMap.Remove(EaName); return STATUS_SUCCESS; } diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index a0a9df47..8dc4ba1e 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -348,7 +348,7 @@ NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode) #if defined(MEMFS_EA) static inline -VOID MemfsFileNodeDeleteAllEa(MEMFS_FILE_NODE *FileNode) +VOID MemfsFileNodeDeleteEaMap(MEMFS_FILE_NODE *FileNode) { if (0 != FileNode->EaMap) { @@ -356,6 +356,7 @@ VOID MemfsFileNodeDeleteAllEa(MEMFS_FILE_NODE *FileNode) p != q; ++p) free(p->second); delete FileNode->EaMap; + FileNode->EaMap = 0; } } #endif @@ -364,7 +365,7 @@ static inline VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) { #if defined(MEMFS_EA) - MemfsFileNodeDeleteAllEa(FileNode); + MemfsFileNodeDeleteEaMap(FileNode); #endif #if defined(MEMFS_REPARSE_POINTS) free(FileNode->ReparseData); @@ -1252,7 +1253,7 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, #endif #if defined(MEMFS_EA) - MemfsFileNodeDeleteAllEa(FileNode); + MemfsFileNodeDeleteEaMap(FileNode); if (0 != Ea) { Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength); diff --git a/tst/winfsp-tests/ea-test.c b/tst/winfsp-tests/ea-test.c index 8abff58f..df29dd44 100644 --- a/tst/winfsp-tests/ea-test.c +++ b/tst/winfsp-tests/ea-test.c @@ -645,10 +645,6 @@ static void ea_check_ea2(HANDLE Handle) } Ea; struct ea_check_ea_context Context; - memset(&Context, 0, sizeof Context); - Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, FALSE); - ASSERT(STATUS_EA_CORRUPT_ERROR == Result); - memset(&Context, 0, sizeof Context); Result = NtQueryEaFile(Handle, &Iosb, &Ea, sizeof Ea, FALSE, 0, 0, 0, TRUE); ASSERT(STATUS_SUCCESS == Result); @@ -771,6 +767,103 @@ static void ea_create_test(void) } } +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); @@ -815,6 +908,8 @@ static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) 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); @@ -850,6 +945,8 @@ static void ea_getset_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) 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); @@ -886,5 +983,6 @@ static void ea_getset_test(void) void ea_tests(void) { TEST_OPT(ea_create_test); + TEST_OPT(ea_overwrite_test); TEST_OPT(ea_getset_test); } From 37bcfc888ae6c116360ec8fa7f0e95bca2f8e882 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Mar 2019 15:27:43 -0700 Subject: [PATCH 32/35] tst: memfs-dotnet: testing EA support --- src/dotnet/Interop.cs | 8 ++++---- tst/memfs-dotnet/Program.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dotnet/Interop.cs b/src/dotnet/Interop.cs index 6678cb35..7bc38599 100644 --- a/src/dotnet/Interop.cs +++ b/src/dotnet/Interop.cs @@ -295,12 +295,12 @@ namespace Fsp.Interop fixed (Byte *P = EaName) { - int I = 0; + int I = 0, J = 0; for (; NameLength > I; I++) P[I] = (Byte)Name[I]; - P[I] = 0; - for (; ValueLength > I; I++) - P[I] = Value[I]; + P[I++] = 0; + for (; ValueLength > J; J++) + P[I + J] = Value[J]; } } } diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs index 60a083a4..b3c2270e 100644 --- a/tst/memfs-dotnet/Program.cs +++ b/tst/memfs-dotnet/Program.cs @@ -273,6 +273,7 @@ namespace memfs Host.NamedStreams = true; Host.PostCleanupWhenModifiedOnly = true; Host.PassQueryDirectoryFileName = true; + Host.ExtendedAttributes = true; return STATUS_SUCCESS; } From e6d1de1cad551be9c44e404af5ac3359895241f7 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Mar 2019 15:35:53 -0700 Subject: [PATCH 33/35] appveyor: troubleshoot --- appveyor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 3380730e..a0416342 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')) From 58fa2a06208c9cdae498c1712e87a32ec739ef73 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Mar 2019 16:09:57 -0700 Subject: [PATCH 34/35] appveyor: troubleshoot --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a0416342..ba087bbe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ environment: # TESTING: Perf init: -- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) +#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) install: - git submodule update --init --recursive @@ -45,4 +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')) +#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) From 0664b492c87cdd137dfb226d25d917b64b0ff576 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Mar 2019 16:29:08 -0700 Subject: [PATCH 35/35] inc: fsctl: fix FSP_FSCTL_VOLUME_PARAMS sizes --- inc/winfsp/fsctl.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 77ff3c81..6d7d99be 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -168,7 +168,7 @@ enum /* additional kernel-mode flags */\ UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\ UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\ - UINT32 KmReservedFlags:7;\ + 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\ @@ -178,7 +178,7 @@ enum UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\ UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\ UINT32 EaTimeoutValid:1; /* EaTimeout field is valid */\ - UINT32 KmAdditionalReservedFlags:28;\ + 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 */\ @@ -190,11 +190,15 @@ 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;