diff options
Diffstat (limited to 'shared/FindFilePlus')
-rw-r--r-- | shared/FindFilePlus/FindFilePlus.vcxproj | 245 | ||||
-rw-r--r-- | shared/FindFilePlus/dll_main.cpp | 25 | ||||
-rw-r--r-- | shared/FindFilePlus/find_file_plus.cpp | 305 | ||||
-rw-r--r-- | shared/FindFilePlus/find_file_plus.h | 78 | ||||
-rw-r--r-- | shared/FindFilePlus/load_dll.cpp | 25 | ||||
-rw-r--r-- | shared/FindFilePlus/load_dll.h | 39 |
6 files changed, 717 insertions, 0 deletions
diff --git a/shared/FindFilePlus/FindFilePlus.vcxproj b/shared/FindFilePlus/FindFilePlus.vcxproj new file mode 100644 index 00000000..a49e99ba --- /dev/null +++ b/shared/FindFilePlus/FindFilePlus.vcxproj @@ -0,0 +1,245 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{70394AEF-5897-4911-AFA1-82EAF0581EFA}</ProjectGuid> + <RootNamespace>ShadowDll</RootNamespace> + <Keyword>Win32Proj</Keyword> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <WholeProgramOptimization>true</WholeProgramOptimization> + <PlatformToolset>Windows7.1SDK</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>Windows7.1SDK</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <WholeProgramOptimization>true</WholeProgramOptimization> + <PlatformToolset>Windows7.1SDK</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>Windows7.1SDK</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> + <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> + <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> + <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">OBJ\$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> + <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> + <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">FindFilePlus_$(Platform)</TargetName> + <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">FindFilePlus_$(Platform)</TargetName> + <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">FindFilePlus_$(Platform)</TargetName> + <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">FindFilePlus_$(Platform)</TargetName> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">D:\Data\WinDDK\inc\ddk;D:\Data\WinDDK\inc\api;D:\Data\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include</IncludePath> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">D:\Data\WinDDK\inc\ddk;D:\Data\WinDDK\inc\api;D:\Data\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include</IncludePath> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">D:\Data\WinDDK\inc\ddk;D:\Data\WinDDK\inc\api;D:\Data\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include</IncludePath> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">D:\Data\WinDDK\inc\ddk;D:\Data\WinDDK\inc\api;D:\Data\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <BuildLog> + <Path>$(IntDir)Build.html</Path> + </BuildLog> + <ClCompile> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_X86_;_DEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>true</MinimalRebuild> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <SuppressStartupBanner>true</SuppressStartupBanner> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <DisableSpecificWarnings>4100</DisableSpecificWarnings> + <AdditionalIncludeDirectories>C:\Program Files\C++\Projects\FreeFileSync\shared</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <ProfileGuidedDatabase> + </ProfileGuidedDatabase> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <BuildLog> + <Path>$(IntDir)Build.html</Path> + </BuildLog> + <Midl> + <TargetEnvironment>X64</TargetEnvironment> + </Midl> + <ClCompile> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_AMD64_;_DEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>true</MinimalRebuild> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <SuppressStartupBanner>true</SuppressStartupBanner> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4100</DisableSpecificWarnings> + <AdditionalIncludeDirectories>C:\Program Files\C++\Projects\FreeFileSync\shared</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <ProfileGuidedDatabase> + </ProfileGuidedDatabase> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX64</TargetMachine> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <BuildLog> + <Path>$(IntDir)Build.html</Path> + </BuildLog> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_X86_;NDEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <SuppressStartupBanner>true</SuppressStartupBanner> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4100</DisableSpecificWarnings> + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>C:\Program Files\C++\Projects\FreeFileSync\shared</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <GenerateDebugInformation>false</GenerateDebugInformation> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + <ProfileGuidedDatabase> + </ProfileGuidedDatabase> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <BuildLog> + <Path>$(IntDir)Build.html</Path> + </BuildLog> + <Midl> + <TargetEnvironment>X64</TargetEnvironment> + </Midl> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_AMD64_;NDEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <SuppressStartupBanner>true</SuppressStartupBanner> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4100</DisableSpecificWarnings> + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>C:\Program Files\C++\Projects\FreeFileSync\shared</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <GenerateDebugInformation>false</GenerateDebugInformation> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + <ProfileGuidedDatabase> + </ProfileGuidedDatabase> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX64</TargetMachine> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="dll_main.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + </PrecompiledHeader> + <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + </PrecompiledHeader> + <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + </PrecompiledHeader> + <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + </PrecompiledHeader> + <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged> + </ClCompile> + <ClCompile Include="find_file_plus.cpp" /> + <ClCompile Include="load_dll.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="find_file_plus.h" /> + <ClInclude Include="load_dll.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/shared/FindFilePlus/dll_main.cpp b/shared/FindFilePlus/dll_main.cpp new file mode 100644 index 00000000..3805c99d --- /dev/null +++ b/shared/FindFilePlus/dll_main.cpp @@ -0,0 +1,25 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +//optional: add init/teardown logic here +BOOL APIENTRY DllMain(HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} diff --git a/shared/FindFilePlus/find_file_plus.cpp b/shared/FindFilePlus/find_file_plus.cpp new file mode 100644 index 00000000..8b29efa2 --- /dev/null +++ b/shared/FindFilePlus/find_file_plus.cpp @@ -0,0 +1,305 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#include "find_file_plus.h" +//#include <windows.h> //these two don't play nice with each other + +#include "load_dll.h" +#include <loki/ScopeGuard.h> + +using namespace dll; +using namespace findplus; + + +namespace +{ +//-------------------------------------------------------------------------------------------------------------- +typedef NTSTATUS (NTAPI* NtOpenFileFunc)(PHANDLE fileHandle, + ACCESS_MASK desiredAccess, + POBJECT_ATTRIBUTES objectAttributes, + PIO_STATUS_BLOCK ioStatusBlock, + ULONG shareAccess, + ULONG openOptions); + +typedef NTSTATUS (NTAPI* NtCloseFunc)(HANDLE handle); + +typedef NTSTATUS (NTAPI* NtQueryDirectoryFileFunc)(HANDLE fileHandle, + HANDLE event, + PIO_APC_ROUTINE apcRoutine, + PVOID apcContext, + PIO_STATUS_BLOCK ioStatusBlock, + PVOID fileInformation, + ULONG length, + FILE_INFORMATION_CLASS fileInformationClass, + BOOLEAN ReturnSingleEntry, + PUNICODE_STRING fileMask, + BOOLEAN restartScan); + +typedef ULONG (NTAPI* RtlNtStatusToDosErrorFunc)(NTSTATUS /*__in status*/); + +typedef struct _RTL_RELATIVE_NAME_U +{ + UNICODE_STRING RelativeName; + HANDLE ContainingDirectory; + PVOID /*PRTLP_CURDIR_REF*/ CurDirRef; +} RTL_RELATIVE_NAME_U, *PRTL_RELATIVE_NAME_U; + +typedef BOOLEAN (NTAPI* RtlDosPathNameToNtPathName_UFunc)(PCWSTR, //__in dosFileName, + PUNICODE_STRING, //__out ntFileName, + PCWSTR*, //__out_optFilePart, + PRTL_RELATIVE_NAME_U); //__out_opt relativeName + +typedef BOOLEAN (NTAPI* RtlDosPathNameToRelativeNtPathName_UFunc)(PCWSTR, //__in dosFileName, + PUNICODE_STRING, //__out ntFileName, + PWSTR*, //__out_optFilePart, + PRTL_RELATIVE_NAME_U); //__out_opt relativeName + +typedef VOID (NTAPI* RtlFreeUnicodeStringFunc)(PUNICODE_STRING); //__inout unicodeString + +//-------------------------------------------------------------------------------------------------------------- + +//it seems we cannot use any of the ntoskrnl.lib files in WinDDK as they produce access violations +//fortunately dynamic binding works fine: +NtOpenFileFunc ntOpenFile; +NtCloseFunc ntClose; +NtQueryDirectoryFileFunc ntQueryDirectoryFile; +RtlNtStatusToDosErrorFunc rtlNtStatusToDosError; +RtlFreeUnicodeStringFunc rtlFreeUnicodeString; +RtlDosPathNameToNtPathName_UFunc rtlDosPathNameToNtPathName_U; + + +template <class FunType> inline +void initDllFun(FunType& fun, const char* functionName) //throw FileError +{ + if (!fun) + { + fun = getSystemDllFun<FunType>(L"ntdll.dll", functionName); + if (!fun) + throw FileError(182); //== ERROR_INVALID_ORDINAL, verified at compile time in "load_dll.cpp"; we better not use rtlNtStatusToDosError(STATUS_ORDINAL_NOT_FOUND) here ;) + } +} + + +struct FileError +{ + FileError(ULONG errorCode) : win32Error(errorCode) {} + ULONG win32Error; +}; +} + +class findplus::FileSearcher +{ +public: + FileSearcher(const wchar_t* dirname); //throw FileError + ~FileSearcher(); + + void readdir(FileInformation& output); //throw FileError + +private: + UNICODE_STRING ntPathName; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()! + HANDLE hDir; + + ULONG nextEntryOffset; //!= 0 if entry is waiting in buffer + //::FindNextFileW() uses 0x1000 = 4096 = sizeof(FILE_BOTH_DIR_INFORMATION) + sizeof(TCHAR) * 2000 + //=> let's use the same, even if our header is 16 byte larger; maybe there is some packet size advantage for networks? Note that larger buffers seem to degrade performance. + static const ULONG BUFFER_SIZE = 4096; + LONGLONG buffer[BUFFER_SIZE / sizeof(LONGLONG)]; //buffer needs to be aligned at LONGLONG boundary + + static_assert(BUFFER_SIZE % sizeof(LONGLONG) == 0, "ups, our buffer is trimmed!"); +}; + + +FileSearcher::FileSearcher(const wchar_t* dirname) : + hDir(NULL), + nextEntryOffset(0) +{ + UNICODE_STRING cleanDummy = {}; + ntPathName = cleanDummy; + + Loki::ScopeGuard guardConstructor = Loki::MakeGuard([&]() { this->~FileSearcher(); }); + + //NT/ZwXxx Routines + //http://msdn.microsoft.com/en-us/library/ff567122(v=VS.85).aspx + + //Run-Time Library (RTL) Routines + //http://msdn.microsoft.com/en-us/library/ff563638(v=VS.85).aspx + + //init static dll functions + initDllFun(ntOpenFile, "NtOpenFile"); //throw FileError + initDllFun(ntClose, "NtClose"); + initDllFun(ntQueryDirectoryFile, "NtQueryDirectoryFile"); + initDllFun(rtlNtStatusToDosError, "RtlNtStatusToDosError"); + initDllFun(rtlFreeUnicodeString, "RtlFreeUnicodeString"); + try + { + initDllFun(rtlDosPathNameToNtPathName_U, "RtlDosPathNameToRelativeNtPathName_U"); //use the newer version if available + } + catch (const FileError&) + { + initDllFun(rtlDosPathNameToNtPathName_U, "RtlDosPathNameToNtPathName_U"); //fallback for XP + } + //-------------------------------------------------------------------------------------------------------------- + + //convert dosFileName, e.g. C:\Users or \\?\C:\Users to ntFileName \??\C:\Users + //in contrast to ::FindFirstFile() we don't evaluate the relativeName, however tests indicate ntFileName is *always* filled with an absolute name, even if dosFileName is relative + + //NOTE: RtlDosPathNameToNtPathName_U may be used on all XP/Win7/Win8 for compatibility + // RtlDosPathNameToNtPathName_U: used by Windows XP available with OS version 3.51 (Windows NT) and higher + // RtlDosPathNameToRelativeNtPathName_U: used by Win7/Win8 available with OS version 5.2 (Windows Server 2003) and higher + if (!rtlDosPathNameToNtPathName_U(dirname, //__in dosFileName, + &ntPathName, //__out ntFileName, + NULL, //__out_optFilePart, + NULL)) //__out_opt relativeName - empty if dosFileName is absolute + throw FileError(rtlNtStatusToDosError(STATUS_OBJECT_PATH_NOT_FOUND)); //translates to ERROR_PATH_NOT_FOUND, same behavior like ::FindFirstFileEx() + + OBJECT_ATTRIBUTES objAttr = {}; + InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes, + &ntPathName, //[in] PUNICODE_STRING objectName, + OBJ_CASE_INSENSITIVE, //[in] ULONG attributes, + NULL, //[in] HANDLE rootDirectory, + NULL); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor + + { + IO_STATUS_BLOCK status = {}; + NTSTATUS rv = ntOpenFile(&hDir, //__out PHANDLE FileHandle, + FILE_LIST_DIRECTORY | SYNCHRONIZE, //__in ACCESS_MASK desiredAccess, - 100001 used by ::FindFirstFile() on all XP/Win7/Win8 + &objAttr, //__in POBJECT_ATTRIBUTES objectAttributes, + &status, //__out PIO_STATUS_BLOCK ioStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //__in ULONG shareAccess, - 7 on Win7/Win8, 3 on XP + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT); //__in ULONG openOptions - 4021 used on all XP/Win7/Win8 + if (!NT_SUCCESS(rv)) + throw FileError(rtlNtStatusToDosError(rv)); + } + + guardConstructor.Dismiss(); +} + + +FileSearcher::~FileSearcher() +{ + //cleanup in reverse order + if (hDir) + ntClose(hDir); + + if (ntPathName.Buffer) + rtlFreeUnicodeString(&ntPathName); //cleanup identical to ::FindFirstFile() + //note that most if this function seems inlined by the linker, so that its assembly looks equivalent to "RtlFreeHeap(GetProcessHeap(), 0, ntPathName.Buffer)" +} + + +void FileSearcher::readdir(FileInformation& output) +{ + //although FILE_ID_FULL_DIR_INFORMATION should suffice for our purposes, there are problems on Windows XP for certain directories, e.g. "\\Vboxsvr\build" + //making NtQueryDirectoryFile() return with STATUS_INVALID_PARAMETER while other directories, e.g. "C:\" work fine for some reason + //FILE_ID_BOTH_DIR_INFORMATION on the other hand works on XP/Win7/Win8 + //performance: there is no noticable difference between FILE_ID_BOTH_DIR_INFORMATION and FILE_ID_FULL_DIR_INFORMATION + + /* corresponding first access in ::FindFirstFileW() + + NTSTATUS rv = ntQueryDirectoryFile(hDir, //__in HANDLE fileHandle, + NULL, //__in_opt HANDLE event, + NULL, //__in_opt PIO_APC_ROUTINE apcRoutine, + NULL, //__in_opt PVOID apcContext, + &status, //__out PIO_STATUS_BLOCK ioStatusBlock, + &buffer, //__out_bcount(Length) PVOID fileInformation, + BUFFER_SIZE, //__in ULONG length, ::FindFirstFileW() on all XP/Win7/Win8 uses sizeof(FILE_BOTH_DIR_INFORMATION) + sizeof(TCHAR) * MAX_PATH == 0x268 + FileIdBothDirectoryInformation, //__in FILE_INFORMATION_CLASS fileInformationClass - all XP/Win7/Win8 use "FileBothDirectoryInformation" + true, //__in BOOLEAN returnSingleEntry, + NULL, //__in_opt PUNICODE_STRING mask, + false); //__in BOOLEAN restartScan + */ + + //analog to ::FindNextFileW() with performance optimized access (in contrast to first access in ::FindFirstFileW()) + if (nextEntryOffset == 0) + { + IO_STATUS_BLOCK status = {}; + NTSTATUS rv = ntQueryDirectoryFile(hDir, //__in HANDLE fileHandle, + NULL, //__in_opt HANDLE event, + NULL, //__in_opt PIO_APC_ROUTINE apcRoutine, + NULL, //__in_opt PVOID apcContext, + &status, //__out PIO_STATUS_BLOCK ioStatusBlock, + &buffer, //__out_bcount(Length) PVOID fileInformation, + BUFFER_SIZE, //__in ULONG length, ::FindNextFileW() on all XP/Win7/Win8 uses sizeof(FILE_BOTH_DIR_INFORMATION) + sizeof(TCHAR) * 2000 == 0x1000 + FileIdBothDirectoryInformation, //__in FILE_INFORMATION_CLASS fileInformationClass - all XP/Win7/Win8 use "FileBothDirectoryInformation" + false, //__in BOOLEAN returnSingleEntry, + NULL, //__in_opt PUNICODE_STRING mask, + false); //__in BOOLEAN restartScan + if (!NT_SUCCESS(rv)) + throw FileError(rtlNtStatusToDosError(rv)); //throws STATUS_NO_MORE_FILES when finished + + if (status.Information == 0) //except for the first call to call ::NtQueryDirectoryFile(): + throw FileError(rtlNtStatusToDosError(STATUS_BUFFER_OVERFLOW)); //if buffer size is too small, return value is STATUS_SUCCESS and Information == 0 -> we don't expect this! + } + + const FILE_ID_BOTH_DIR_INFORMATION& dirInfo = *reinterpret_cast<FILE_ID_BOTH_DIR_INFORMATION*>(reinterpret_cast<char*>(buffer) + nextEntryOffset); + + if (dirInfo.NextEntryOffset == 0) + nextEntryOffset = 0; //our offset is relative to the beginning of the buffer + else + nextEntryOffset += dirInfo.NextEntryOffset; + + + auto toFileTime = [](const LARGE_INTEGER & rawTime) -> FILETIME + { + FILETIME tmp = { rawTime.LowPart, rawTime.HighPart }; + return tmp; + }; + + output.creationTime = toFileTime(dirInfo.CreationTime); + output.lastWriteTime = toFileTime(dirInfo.LastWriteTime); + output.fileSize.QuadPart = dirInfo.EndOfFile.QuadPart; + output.fileId.QuadPart = dirInfo.FileId.QuadPart; + output.fileAttributes = dirInfo.FileAttributes; + output.shortNameLength = dirInfo.FileNameLength / sizeof(TCHAR); //FileNameLength is in bytes! + + if (dirInfo.FileNameLength + sizeof(TCHAR) > sizeof(output.shortName)) //this may actually happen if ::NtQueryDirectoryFile() decides to return a + throw FileError(rtlNtStatusToDosError(STATUS_BUFFER_OVERFLOW)); //short name of length MAX_PATH + 1, 0-termination is not required! + + ::memcpy(output.shortName, dirInfo.FileName, dirInfo.FileNameLength); + output.shortName[output.shortNameLength] = 0; //NOTE: FILE_ID_BOTH_DIR_INFORMATION::FileName in general is NOT 0-terminated! It is on XP/Win7, but NOT on Win8! + + static_assert(sizeof(output.creationTime) == sizeof(dirInfo.CreationTime), "dang!"); + static_assert(sizeof(output.lastWriteTime) == sizeof(dirInfo.LastWriteTime), "dang!"); + static_assert(sizeof(output.fileSize) == sizeof(dirInfo.EndOfFile), "dang!"); + static_assert(sizeof(output.fileId) == sizeof(dirInfo.FileId), "dang!"); + static_assert(sizeof(output.fileAttributes) == sizeof(dirInfo.FileAttributes), "dang!"); +} + + +FindHandle findplus::openDir(const wchar_t* dirname) +{ + try + { + return new FileSearcher(dirname); //throw FileError + } + catch (const FileError& err) + { + setWin32Error(err.win32Error); + return NULL; + } +} + + +bool findplus::readDir(FindHandle hnd, FileInformation& output) +{ + try + { + hnd->readdir(output); //throw FileError + return true; + } + catch (const FileError& err) + { + setWin32Error(err.win32Error); + return false; + } +} + + +void findplus::closeDir(FindHandle hnd) +{ + if (hnd) //play a little "nice" + delete static_cast<FileSearcher*>(hnd); +} diff --git a/shared/FindFilePlus/find_file_plus.h b/shared/FindFilePlus/find_file_plus.h new file mode 100644 index 00000000..88cb13af --- /dev/null +++ b/shared/FindFilePlus/find_file_plus.h @@ -0,0 +1,78 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#ifndef FIND_FIRST_FILE_PLUS_HEADER_087483434 +#define FIND_FIRST_FILE_PLUS_HEADER_087483434 + +#ifdef FIND_FILE_PLUS_DLL_EXPORTS +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) +#else +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) +#endif + + +#ifdef FIND_FILE_PLUS_DLL_EXPORTS +#include <Ntifs.h> //driver level headers must be included *before* windows api headers! +#endif +#include <windef.h> // +#undef min +#undef max + +#include "../build_info.h" + +namespace findplus +{ +/*-------------- + |declarations| + --------------*/ + +struct FileInformation +{ + FILETIME creationTime; + FILETIME lastWriteTime; + ULARGE_INTEGER fileSize; + ULARGE_INTEGER fileId; + DWORD fileAttributes; + DWORD shortNameLength; + WCHAR shortName[MAX_PATH + 1]; //shortName is 0-terminated +}; + +class FileSearcher; +typedef FileSearcher* FindHandle; + +DLL_FUNCTION_DECLARATION +FindHandle openDir(const wchar_t* dirname); //returns NULL on error, call ::GetLastError(); returns ERROR_INVALID_ORDINAL if ntdll-libraries could not be loaded +//note do NOT place an asterisk at end, e.g. C:\SomeDir\*, as one would do for ::FindFirstFile() + +DLL_FUNCTION_DECLARATION +bool readDir(FindHandle hnd, FileInformation& output); //returns false on error or if there are no more files; ::GetLastError() returns ERROR_NO_MORE_FILES + +DLL_FUNCTION_DECLARATION +void closeDir(FindHandle hnd); + +/*---------- + |typedefs| + ----------*/ +typedef FindHandle (*OpenDirFunc )(const wchar_t* dirname); +typedef bool (*ReadDirFunc )(FindHandle hnd, FileInformation& dirInfo); +typedef void (*CloseDirFunc)(FindHandle hnd); + +/*-------------- + |symbol names| + --------------*/ +//const pointers ensure internal linkage +const char openDirFuncName [] = "openDir"; +const char readDirFuncName [] = "readDir"; +const char closeDirFuncName[] = "closeDir"; + +/*--------------- + |library names| + ---------------*/ +inline const wchar_t* getDllName() { return util::is64BitBuild ? L"FindFilePlus_x64.dll" : L"FindFilePlus_Win32.dll"; } +} + + +#endif //FIND_FIRST_FILE_PLUS_HEADER_087483434 diff --git a/shared/FindFilePlus/load_dll.cpp b/shared/FindFilePlus/load_dll.cpp new file mode 100644 index 00000000..51ec1f0c --- /dev/null +++ b/shared/FindFilePlus/load_dll.cpp @@ -0,0 +1,25 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> // yes, this sequence is mad, but ddk and api headers contain plenty of redefinitions +#include "load_dll.h" // + +void* /*FARPROC*/ dll::loadSymbol(const wchar_t* libraryName, const char* functionName) +{ + return ::GetProcAddress(::GetModuleHandle(libraryName), functionName); + //cleanup neither required nor allowed (::FreeLibrary()) + +} +//note: void* and FARPROC function pointer have same binary size on Windows + + +void dll::setWin32Error(unsigned long lastError) +{ + ::SetLastError(lastError); +} + +static_assert(ERROR_INVALID_ORDINAL == 182, "dang!");
\ No newline at end of file diff --git a/shared/FindFilePlus/load_dll.h b/shared/FindFilePlus/load_dll.h new file mode 100644 index 00000000..f152dd74 --- /dev/null +++ b/shared/FindFilePlus/load_dll.h @@ -0,0 +1,39 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#ifndef LOAD_DLL_HEADER_0312463214872163832174 +#define LOAD_DLL_HEADER_0312463214872163832174 + +namespace dll +{ +template <typename FunctionType> +FunctionType getSystemDllFun(const wchar_t* libraryName, const char* functionName); //NOTE: uses ::GetModuleHandle => call for system DLLs only! + +void setWin32Error(unsigned long lastError); + + + + + + + + + + + + + +void* /*FARPROC*/ loadSymbol(const wchar_t* libraryName, const char* functionName); + +template <typename FunctionType> inline +FunctionType getSystemDllFun(const wchar_t* libraryName, const char* functionName) +{ + return reinterpret_cast<FunctionType>(loadSymbol(libraryName, functionName)); +} +} + +#endif //LOAD_DLL_HEADER_0312463214872163832174 + |