summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/FindFilePlus/FindFilePlus.vcxproj245
-rw-r--r--zen/FindFilePlus/dll_main.cpp30
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp298
-rw-r--r--zen/FindFilePlus/find_file_plus.h78
-rw-r--r--zen/FindFilePlus/init_dll_binding.h16
-rw-r--r--zen/FindFilePlus/load_dll.cpp23
-rw-r--r--zen/FindFilePlus/load_dll.h46
-rw-r--r--zen/dir_watcher.cpp54
-rw-r--r--zen/dir_watcher.h3
-rw-r--r--zen/file_handling.cpp84
-rw-r--r--zen/file_handling.h7
-rw-r--r--zen/file_id.cpp13
-rw-r--r--zen/file_id.h5
-rw-r--r--zen/file_id_def.h65
-rw-r--r--zen/file_id_internal.h48
-rw-r--r--zen/file_traverser.cpp767
-rw-r--r--zen/file_traverser.h7
-rw-r--r--zen/guid.h2
-rw-r--r--zen/int64.h4
-rw-r--r--zen/long_path_prefix.h1
-rw-r--r--zen/notify_removal.cpp41
-rw-r--r--zen/privilege.cpp80
-rw-r--r--zen/privilege.h47
-rw-r--r--zen/process_status.h40
-rw-r--r--zen/stl_tools.h46
-rw-r--r--zen/symlink_target.h2
-rw-r--r--zen/time.h5
27 files changed, 1462 insertions, 595 deletions
diff --git a/zen/FindFilePlus/FindFilePlus.vcxproj b/zen/FindFilePlus/FindFilePlus.vcxproj
new file mode 100644
index 00000000..2c4256a6
--- /dev/null
+++ b/zen/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>../..</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>../..</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>../..</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>../..</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/zen/FindFilePlus/dll_main.cpp b/zen/FindFilePlus/dll_main.cpp
new file mode 100644
index 00000000..5d64181b
--- /dev/null
+++ b/zen/FindFilePlus/dll_main.cpp
@@ -0,0 +1,30 @@
+// **************************************************************************
+// * 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 <zen/win.h>
+
+#include "init_dll_binding.h"
+
+
+//optional: add init/teardown logic here
+BOOL APIENTRY DllMain(HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ if (!findplus::initDllBinding())
+ return false;
+ case DLL_PROCESS_DETACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return true;
+}
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp
new file mode 100644
index 00000000..becfe553
--- /dev/null
+++ b/zen/FindFilePlus/find_file_plus.cpp
@@ -0,0 +1,298 @@
+// **************************************************************************
+// * 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 "init_dll_binding.h"
+//#include <windows.h> //these two don't play nice with each other
+#include "load_dll.h"
+#include <zen/scope_guard.h>
+
+using namespace dll;
+using namespace findplus;
+
+
+namespace
+{
+struct FileError
+{
+ FileError(ULONG errorCode) : win32Error(errorCode) {}
+ ULONG win32Error;
+};
+
+
+//--------------------------------------------------------------------------------------------------------------
+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,
+ PCWSTR*, //__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:
+const SysDllFun<NtOpenFileFunc> ntOpenFile (L"ntdll.dll", "NtOpenFile");
+const SysDllFun<NtCloseFunc> ntClose (L"ntdll.dll", "NtClose");
+const SysDllFun<NtQueryDirectoryFileFunc> ntQueryDirectoryFile (L"ntdll.dll", "NtQueryDirectoryFile");
+const SysDllFun<RtlNtStatusToDosErrorFunc> rtlNtStatusToDosError (L"ntdll.dll", "RtlNtStatusToDosError");
+const SysDllFun<RtlFreeUnicodeStringFunc> rtlFreeUnicodeString (L"ntdll.dll", "RtlFreeUnicodeString");
+const SysDllFun<RtlDosPathNameToNtPathName_UFunc> rtlDosPathNameToNtPathName_U(SysDllFun<RtlDosPathNameToRelativeNtPathName_UFunc>(L"ntdll.dll", "RtlDosPathNameToRelativeNtPathName_U") ?
+ SysDllFun<RtlDosPathNameToRelativeNtPathName_UFunc>(L"ntdll.dll", "RtlDosPathNameToRelativeNtPathName_U") : //use the newer version if available
+ SysDllFun<RtlDosPathNameToNtPathName_UFunc>(L"ntdll.dll", "RtlDosPathNameToNtPathName_U")); //fallback for XP
+//global constants only -> preserve thread safety!
+}
+
+
+bool findplus::initDllBinding() //evaluate in ::DllMain() when attaching process
+{
+ //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
+
+ //verify dynamic dll binding
+ return ntOpenFile &&
+ ntClose &&
+ ntQueryDirectoryFile &&
+ rtlNtStatusToDosError &&
+ rtlFreeUnicodeString &&
+ rtlDosPathNameToNtPathName_U;
+
+ //this may become handy some time: nt status code STATUS_ORDINAL_NOT_FOUND maps to win32 code ERROR_INVALID_ORDINAL
+}
+
+
+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)
+{
+ ntPathName.Buffer = NULL;
+ ntPathName.Length = 0;
+ ntPathName.MaximumLength = 0;
+
+ zen::ScopeGuard guardConstructor = zen::makeGuard([&]() { this->~FileSearcher(); });
+ //--------------------------------------------------------------------------------------------------------------
+
+ //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();
+}
+
+
+inline
+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 hnd;
+}
diff --git a/zen/FindFilePlus/find_file_plus.h b/zen/FindFilePlus/find_file_plus.h
new file mode 100644
index 00000000..72b76dbb
--- /dev/null
+++ b/zen/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 <zen/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
+}; //no need for #pragma pack -> all members already starting at 4 byte boundary!
+
+class FileSearcher;
+typedef FileSearcher* FindHandle;
+
+DLL_FUNCTION_DECLARATION
+FindHandle openDir(const wchar_t* dirname); //returns NULL on error, call ::GetLastError()
+//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 in this case
+
+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 zen::is64BitBuild ? L"FindFilePlus_x64.dll" : L"FindFilePlus_Win32.dll"; }
+}
+
+
+#endif //FIND_FIRST_FILE_PLUS_HEADER_087483434
diff --git a/zen/FindFilePlus/init_dll_binding.h b/zen/FindFilePlus/init_dll_binding.h
new file mode 100644
index 00000000..51b32c99
--- /dev/null
+++ b/zen/FindFilePlus/init_dll_binding.h
@@ -0,0 +1,16 @@
+// **************************************************************************
+// * 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 INIT_DLL_BINDING_HEADER_ß018356031467832145
+#define INIT_DLL_BINDING_HEADER_ß018356031467832145
+
+namespace findplus
+{
+//load and check dll binding at startup
+bool initDllBinding(); //evaluate in ::DllMain() when attaching process
+}
+
+#endif //INIT_DLL_BINDING_HEADER_ß018356031467832145
diff --git a/zen/FindFilePlus/load_dll.cpp b/zen/FindFilePlus/load_dll.cpp
new file mode 100644
index 00000000..20d9a5fe
--- /dev/null
+++ b/zen/FindFilePlus/load_dll.cpp
@@ -0,0 +1,23 @@
+// **************************************************************************
+// * 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 "load_dll.h"
+#define WIN32_LEAN_AND_MEAN
+#include <zen/win.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);
+}
diff --git a/zen/FindFilePlus/load_dll.h b/zen/FindFilePlus/load_dll.h
new file mode 100644
index 00000000..350de9f8
--- /dev/null
+++ b/zen/FindFilePlus/load_dll.h
@@ -0,0 +1,46 @@
+// **************************************************************************
+// * 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
+{
+void setWin32Error(unsigned long lastError);
+
+//NOTE: uses ::GetModuleHandle => call for system DLLs only!
+template <class Func>
+class SysDllFun
+{
+public:
+ SysDllFun(const wchar_t* systemLibrary, const char* functionName) :
+ fun(reinterpret_cast<Func>(loadSymbol(systemLibrary, functionName))) {}
+
+ operator Func() const { return fun; }
+
+private:
+ Func fun;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void* /*FARPROC*/ loadSymbol(const wchar_t* libraryName, const char* functionName);
+}
+
+#endif //LOAD_DLL_HEADER_0312463214872163832174
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 81e49f89..c980c715 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -134,7 +134,8 @@ public:
ReadChangesAsync(const Zstring& directory, //make sure to not leak in thread-unsafe types!
const std::shared_ptr<SharedData>& shared) :
shared_(shared),
- dirname(directory)
+ dirname(directory),
+ hDir(INVALID_HANDLE_VALUE)
{
if (!endsWith(dirname, FILE_NAME_SEPARATOR))
dirname += FILE_NAME_SEPARATOR;
@@ -143,8 +144,8 @@ public:
//http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
try
{
- Privileges::getInstance().ensureActive(SE_BACKUP_NAME); //throw FileError
- Privileges::getInstance().ensureActive(SE_RESTORE_NAME); //
+ activatePrivilege(SE_BACKUP_NAME); //throw FileError
+ activatePrivilege(SE_RESTORE_NAME); //
}
catch (const FileError&) {}
@@ -155,7 +156,7 @@ public:
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
- if (hDir == INVALID_HANDLE_VALUE )
+ if (hDir == INVALID_HANDLE_VALUE)
{
const std::wstring errorMsg = _("Could not initialize directory monitoring:") + L"\n\"" + dirname + L"\"" + L"\n\n" + zen::getLastErrorFormatted();
if (errorCodeForNotExisting(::GetLastError()))
@@ -253,7 +254,7 @@ public:
std::swap(hDir, other.hDir);
}
- HANDLE getDirHandle() const { return hDir; } //for reading purposes only, don't abuse (e.g. close handle)!
+ HANDLE getDirHandle() const { return hDir; } //for reading/monitoring purposes only, don't abuse (e.g. close handle)!
private:
//shared between main and worker:
@@ -268,13 +269,9 @@ class HandleVolumeRemoval : public NotifyRequestDeviceRemoval
{
public:
HandleVolumeRemoval(HANDLE hDir,
- boost::thread& worker,
- const std::shared_ptr<SharedData>& shared,
- const Zstring& dirname) :
+ boost::thread& worker) :
NotifyRequestDeviceRemoval(hDir), //throw FileError
worker_(worker),
- shared_(shared),
- dirname_(dirname),
removalRequested(false),
operationComplete(false) {}
@@ -291,16 +288,12 @@ private:
worker_.join();
//now hDir should have been released
- //report removal as change to main directory
- shared_->addChange(dirname_);
-
removalRequested = true;
} //don't throw!
+
virtual void onRemovalFinished(HANDLE hnd, bool successful) { operationComplete = true; } //throw()!
boost::thread& worker_;
- std::shared_ptr<SharedData> shared_;
- Zstring dirname_;
bool removalRequested;
bool operationComplete;
};
@@ -312,6 +305,7 @@ struct DirWatcher::Pimpl
boost::thread worker;
std::shared_ptr<SharedData> shared;
+ Zstring dirname;
std::unique_ptr<HandleVolumeRemoval> volRemoval;
};
@@ -320,9 +314,10 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
pimpl_(new Pimpl)
{
pimpl_->shared = std::make_shared<SharedData>();
+ pimpl_->dirname = directory;
ReadChangesAsync reader(directory, pimpl_->shared); //throw FileError
- pimpl_->volRemoval.reset(new HandleVolumeRemoval(reader.getDirHandle(), pimpl_->worker, pimpl_->shared, directory)); //throw FileError
+ pimpl_->volRemoval.reset(new HandleVolumeRemoval(reader.getDirHandle(), pimpl_->worker)); //throw FileError
pimpl_->worker = boost::thread(std::move(reader));
}
@@ -332,22 +327,29 @@ DirWatcher::~DirWatcher()
pimpl_->worker.interrupt();
//pimpl_->worker.join(); -> we don't have time to wait... will take ~50ms anyway
//caveat: exitting the app may simply kill this thread!
+}
+
+
+std::vector<Zstring> DirWatcher::getChanges(const std::function<void()>& processGuiMessages) //throw FileError
+{
+ std::vector<Zstring> output;
- //wait until device removal is confirmed, to (hopefully!) prevent locking hDir again by new watch!
+ //wait until device removal is confirmed, to prevent locking hDir again by some new watch!
if (pimpl_->volRemoval->requestReceived())
{
- const boost::system_time maxwait = boost::get_system_time() + boost::posix_time::seconds(3); //HandleVolumeRemoval::finished() not guaranteed!
+ const boost::system_time maxwait = boost::get_system_time() + boost::posix_time::seconds(15);
+ //HandleVolumeRemoval::finished() not guaranteed! note: Windows gives unresponsive applications ca. 10 seconds until unmounting the usb stick in worst case
while (!pimpl_->volRemoval->finished() && boost::get_system_time() < maxwait)
+ {
+ processGuiMessages(); //DBT_DEVICEREMOVECOMPLETE message is sent here!
boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(50));
- }
-}
-
+ }
-std::vector<Zstring> DirWatcher::getChanges() //throw FileError
-{
- std::vector<Zstring> output;
- pimpl_->shared->getChanges(output); //throw FileError
+ output.push_back(pimpl_->dirname); //report removal as change to main directory
+ }
+ else //the normal case...
+ pimpl_->shared->getChanges(output); //throw FileError
return output;
}
@@ -454,7 +456,7 @@ DirWatcher::~DirWatcher()
}
-std::vector<Zstring> DirWatcher::getChanges() //throw FileError
+std::vector<Zstring> DirWatcher::getChanges(const std::function<void()>&) //throw FileError
{
std::vector<char> buffer(1024 * (sizeof(struct inotify_event) + 16));
diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h
index df3c444c..a9898abb 100644
--- a/zen/dir_watcher.h
+++ b/zen/dir_watcher.h
@@ -9,6 +9,7 @@
#include <vector>
#include <memory>
+#include <functional>
#include "file_error.h"
namespace zen
@@ -35,7 +36,7 @@ public:
~DirWatcher();
//extract accumulated changes since last call
- std::vector<Zstring> getChanges(); //throw FileError
+ std::vector<Zstring> getChanges(const std::function<void()>& processGuiMessages); //throw FileError
private:
DirWatcher(const DirWatcher&);
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index 5d57938a..8523c633 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -15,7 +15,7 @@
#include "assert_static.h"
#include <boost/thread/tss.hpp>
#include <boost/thread/once.hpp>
-#include "file_id_internal.h"
+#include "file_id_def.h"
#ifdef FFS_WIN
#include "privilege.h"
@@ -194,12 +194,15 @@ namespace
#ifdef FFS_WIN
DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
{
- std::vector<wchar_t> buffer(10000);
+ //note: this even works for network shares: \\share\dirname
+
+ const DWORD BUFFER_SIZE = 10000;
+ std::vector<wchar_t> buffer(BUFFER_SIZE);
//full pathName need not yet exist!
if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
&buffer[0], //__out LPTSTR lpszVolumePathName,
- static_cast<DWORD>(buffer.size()))) //__in DWORD cchBufferLength
+ BUFFER_SIZE)) //__in DWORD cchBufferLength
return 0;
Zstring volumePath = &buffer[0];
@@ -608,7 +611,7 @@ struct RemoveCallbackImpl : public CallbackRemoveDir
moveCallback_(moveCallback) {}
virtual void notifyFileDeletion(const Zstring& filename) { moveCallback_.requestUiRefresh(sourceDir_); }
- virtual void notifyDirDeletion(const Zstring& dirname) { moveCallback_.requestUiRefresh(sourceDir_); }
+ virtual void notifyDirDeletion (const Zstring& dirname ) { moveCallback_.requestUiRefresh(sourceDir_); }
private:
RemoveCallbackImpl(const RemoveCallbackImpl&);
@@ -620,7 +623,7 @@ private:
}
-void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback) //throw FileError;
+void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback) //throw FileError
{
//call back once per folder
if (callback)
@@ -683,7 +686,7 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
}
-void zen::moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback) //throw FileError;
+void zen::moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback) //throw FileError
{
#ifdef FFS_WIN
const Zstring& sourceDirFormatted = sourceDir;
@@ -999,13 +1002,13 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
try
{
//enable privilege: required to read/write SACL information
- Privileges::getInstance().ensureActive(SE_SECURITY_NAME); //polling allowed...
+ activatePrivilege(SE_SECURITY_NAME); //polling allowed...
//enable privilege: required to copy owner information
- Privileges::getInstance().ensureActive(SE_RESTORE_NAME);
+ activatePrivilege(SE_RESTORE_NAME);
//the following privilege may be required according to http://msdn.microsoft.com/en-us/library/aa364399(VS.85).aspx (although not needed nor active in my tests)
- Privileges::getInstance().ensureActive(SE_BACKUP_NAME);
+ activatePrivilege(SE_BACKUP_NAME);
}
catch (const FileError& e)
{
@@ -1416,14 +1419,19 @@ public:
throw FileError(errorMsg);
}
- void setSrcAttr(const FileAttrib& attr) { sourceAttr = attr; }
- FileAttrib getSrcAttr() const { assert(sourceAttr.modificationTime != 0); return sourceAttr; }
+ void setNewAttr(const FileAttrib& attr) { newAttrib = attr; }
+
+ FileAttrib getSrcAttr() const
+ {
+ assert(newAttrib.modificationTime != 0);
+ return newAttrib;
+ }
private:
CallbackData(const CallbackData&);
CallbackData& operator=(const CallbackData&);
- FileAttrib sourceAttr;
+ FileAttrib newAttrib;
std::wstring errorMsg; //
bool exceptionInUserCallback; //these two are exclusive!
UInt64 bytesTransferredOnException;
@@ -1456,19 +1464,27 @@ DWORD CALLBACK copyCallbackInternal(
//#################### return source file attributes ################################
if (dwCallbackReason == CALLBACK_STREAM_SWITCH) //called up front for every file (even if 0-sized)
{
- BY_HANDLE_FILE_INFORMATION fileInfo = {};
- if (!::GetFileInformationByHandle(hSourceFile, &fileInfo))
+ BY_HANDLE_FILE_INFORMATION fileInfoSrc = {};
+ if (!::GetFileInformationByHandle(hSourceFile, &fileInfoSrc))
{
cbd.reportError(_("Error reading file attributes:") + L"\n\"" + cbd.sourceFile_ + L"\"" + L"\n\n" + getLastErrorFormatted());
return PROGRESS_CANCEL;
}
- const FileAttrib attr = { UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh),
- toTimeT(fileInfo.ftLastWriteTime)
- };
- //extractFileID(fileInfo));
+ BY_HANDLE_FILE_INFORMATION fileInfoTrg = {};
+ if (!::GetFileInformationByHandle(hDestinationFile, &fileInfoTrg))
+ {
+ cbd.reportError(_("Error reading file attributes:") + L"\n\"" + cbd.targetFile_ + L"\"" + L"\n\n" + getLastErrorFormatted());
+ return PROGRESS_CANCEL;
+ }
+
+ FileAttrib attr;
+ attr.fileSize = UInt64(fileInfoSrc.nFileSizeLow, fileInfoSrc.nFileSizeHigh);
+ attr.modificationTime = toTimeT(fileInfoSrc.ftLastWriteTime);
+ attr.sourceFileId = extractFileID(fileInfoSrc);
+ attr.targetFileId = extractFileID(fileInfoTrg);
- cbd.setSrcAttr(attr);
+ cbd.setNewAttr(attr);
//#################### copy file creation time ################################
FILETIME creationTime = {};
@@ -1527,7 +1543,7 @@ DWORD CALLBACK copyCallbackInternal(
void rawCopyWinApi_sub(const Zstring& sourceFile,
const Zstring& targetFile,
CallbackCopyFile* callback,
- FileAttrib* sourceAttr) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
+ FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
{
zen::ScopeGuard guardTarget = zen::makeGuard([&]() { removeFile(targetFile); }); //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we ;)
@@ -1601,8 +1617,8 @@ void rawCopyWinApi_sub(const Zstring& sourceFile,
throw FileError(errorMessage);
}
- if (sourceAttr)
- *sourceAttr = cbd.getSrcAttr();
+ if (newAttrib)
+ *newAttrib = cbd.getSrcAttr();
{
const Int64 modTime = getFileTime(sourceFile, SYMLINK_FOLLOW); //throw FileError
@@ -1972,7 +1988,7 @@ void rawCopyWinApi(const Zstring& sourceFile,
void rawCopyStream(const Zstring& sourceFile,
const Zstring& targetFile,
CallbackCopyFile* callback,
- FileAttrib* sourceAttr) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
{
zen::ScopeGuard guardTarget = zen::makeGuard([&]() { removeFile(targetFile); }); //transactional behavior: place guard before lifetime of FileOutput
try
@@ -2012,23 +2028,29 @@ void rawCopyStream(const Zstring& sourceFile,
//adapt file modification time:
{
- struct stat srcInfo = {};
+ struct ::stat srcInfo = {};
if (::stat(sourceFile.c_str(), &srcInfo) != 0) //read file attributes from source directory
throw FileError(_("Error reading file attributes:") + L"\n\"" + sourceFile + L"\"" + L"\n\n" + getLastErrorFormatted());
- if (sourceAttr)
- {
- sourceAttr->fileSize = UInt64(srcInfo.st_size);
- sourceAttr->modificationTime = srcInfo.st_mtime;
- }
-
- struct utimbuf newTimes = {};
+ struct ::utimbuf newTimes = {};
newTimes.actime = srcInfo.st_atime;
newTimes.modtime = srcInfo.st_mtime;
//set new "last write time"
if (::utime(targetFile.c_str(), &newTimes) != 0)
throw FileError(_("Error changing modification time:") + L"\n\"" + targetFile + L"\"" + L"\n\n" + getLastErrorFormatted());
+
+ if (newAttrib)
+ {
+ struct ::stat trgInfo = {};
+ if (::stat(targetFile.c_str(), &trgInfo) != 0) //read file attributes from source directory
+ throw FileError(_("Error reading file attributes:") + L"\n\"" + targetFile + L"\"" + L"\n\n" + getLastErrorFormatted());
+
+ newAttrib->fileSize = UInt64(srcInfo.st_size);
+ newAttrib->modificationTime = srcInfo.st_mtime;
+ newAttrib->sourceFileId = extractFileID(srcInfo);
+ newAttrib->targetFileId = extractFileID(trgInfo);
+ }
}
guardTarget.dismiss(); //target has been created successfully!
diff --git a/zen/file_handling.h b/zen/file_handling.h
index 02f3e532..8ffe38d0 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -10,6 +10,7 @@
#include "zstring.h"
#include "file_error.h"
#include "int64.h"
+#include "file_id_def.h"
namespace zen
{
@@ -68,7 +69,9 @@ void createDirectory(const Zstring& directory); //throw FileError; -> function o
struct FileAttrib
{
UInt64 fileSize;
- Int64 modificationTime; //size_t UTC compatible
+ Int64 modificationTime; //time_t UTC compatible
+ FileId sourceFileId;
+ FileId targetFileId;
};
void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissing, ErrorFileLocked (Windows-only)
@@ -76,7 +79,7 @@ void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissi
bool copyFilePermissions,
bool transactionalCopy,
CallbackCopyFile* callback, //may be NULL
- FileAttrib* sourceAttr = NULL); //return current attributes at the time of copy
+ FileAttrib* newAttrib = NULL); //return current attributes at the time of copy
//Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop.
// => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending:
const Zstring TEMP_FILE_ENDING = Zstr(".ffs_tmp");
diff --git a/zen/file_id.cpp b/zen/file_id.cpp
index acbdcd7b..7527062b 100644
--- a/zen/file_id.cpp
+++ b/zen/file_id.cpp
@@ -5,7 +5,6 @@
// **************************************************************************
#include "file_id.h"
-#include "file_id_internal.h"
#ifdef FFS_WIN
#include "win.h" //includes "windows.h"
@@ -17,7 +16,7 @@
#endif
-std::string zen::getFileID(const Zstring& filename)
+zen::FileId zen::getFileID(const Zstring& filename)
{
#ifdef FFS_WIN
//WARNING: CreateFile() is SLOW, while GetFileInformationByHandle() is cheap!
@@ -43,11 +42,11 @@ std::string zen::getFileID(const Zstring& filename)
#elif defined FFS_LINUX
struct ::stat fileInfo = {};
- if (::lstat(filename.c_str(), &fileInfo) == 0) //lstat() does not follow symlinks
+ if (::stat(filename.c_str(), &fileInfo) == 0) //stat() follows symlinks
return extractFileID(fileInfo);
#endif
- return std::string();
+ return zen::FileId();
}
@@ -56,10 +55,10 @@ bool zen::samePhysicalFile(const Zstring& file1, const Zstring& file2)
if (EqualFilename()(file1, file2)) //quick check
return true;
- const std::string id1 = getFileID(file1);
- const std::string id2 = getFileID(file2);
+ const auto id1 = getFileID(file1);
+ const auto id2 = getFileID(file2);
- if (id1.empty() || id2.empty())
+ if (id1 == zen::FileId() || id2 == zen::FileId())
return false;
return id1 == id2;
diff --git a/zen/file_id.h b/zen/file_id.h
index f015569d..3fd4c6bb 100644
--- a/zen/file_id.h
+++ b/zen/file_id.h
@@ -7,6 +7,7 @@
#ifndef FILEID_H_INCLUDED
#define FILEID_H_INCLUDED
+#include "file_id_def.h"
#include "zstring.h"
#include <string>
@@ -15,8 +16,8 @@
namespace zen
{
//get unique file id (symbolic link handling: opens the link!!!)
-//returns empty string on error!
-std::string getFileID(const Zstring& filename);
+//returns initial FileId() on error!
+FileId getFileID(const Zstring& filename);
//test whether two distinct paths point to the same file or directory:
// true: paths point to same files/dirs
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
new file mode 100644
index 00000000..b2029879
--- /dev/null
+++ b/zen/file_id_def.h
@@ -0,0 +1,65 @@
+// **************************************************************************
+// * 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 FILE_ID_INTERNAL_HEADER_013287632486321493
+#define FILE_ID_INTERNAL_HEADER_013287632486321493
+
+#include <utility>
+#include "assert_static.h"
+
+#ifdef FFS_WIN
+#include "win.h" //includes "windows.h"
+
+#elif defined FFS_LINUX
+#include <sys/stat.h>
+#endif
+
+
+namespace zen
+{
+#ifdef FFS_WIN
+typedef std::pair<decltype(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), decltype(ULARGE_INTEGER().QuadPart)> FileId; //(volume serial number, file ID)
+
+inline
+FileId extractFileID(const BY_HANDLE_FILE_INFORMATION& fileInfo)
+{
+ ULARGE_INTEGER uint = {};
+ uint.HighPart = fileInfo.nFileIndexHigh;
+ uint.LowPart = fileInfo.nFileIndexLow;
+ return std::make_pair(fileInfo.dwVolumeSerialNumber, uint.QuadPart);
+}
+
+inline
+FileId extractFileID(DWORD dwVolumeSerialNumber, ULARGE_INTEGER fileId)
+{
+ return std::make_pair(dwVolumeSerialNumber, fileId.QuadPart);
+}
+
+namespace impl
+{
+inline
+void validate(const FileId& id, const BY_HANDLE_FILE_INFORMATION& fileInfo)
+{
+ assert_static(sizeof(id.second) == sizeof(fileInfo.nFileIndexHigh) + sizeof(fileInfo.nFileIndexLow));
+ assert_static(sizeof(id.first ) == sizeof(DWORD));
+ assert_static(sizeof(id.second) == sizeof(ULARGE_INTEGER));
+}
+}
+
+#elif defined FFS_LINUX
+namespace impl { typedef struct ::stat StatDummy; } //sigh...
+
+typedef std::pair<decltype(impl::StatDummy::st_dev), decltype(impl::StatDummy::st_ino)> FileId; //(device id, inode)
+
+inline
+FileId extractFileID(const struct stat& fileInfo)
+{
+ return std::make_pair(fileInfo.st_dev, fileInfo.st_ino);
+}
+#endif
+}
+
+#endif //FILE_ID_INTERNAL_HEADER_013287632486321493
diff --git a/zen/file_id_internal.h b/zen/file_id_internal.h
deleted file mode 100644
index 492d8432..00000000
--- a/zen/file_id_internal.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// **************************************************************************
-// * 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 FILE_ID_INTERNAL_HEADER_013287632486321493
-#define FILE_ID_INTERNAL_HEADER_013287632486321493
-
-#include <string>
-
-#ifdef FFS_WIN
-#include "win.h" //includes "windows.h"
-
-#elif defined FFS_LINUX
-#include <sys/stat.h>
-#endif
-
-namespace
-{
-template <class T> inline
-std::string numberToBytes(T number)
-{
- const char* rawBegin = reinterpret_cast<const char*>(&number);
- return std::string(rawBegin, rawBegin + sizeof(number));
-}
-
-#ifdef FFS_WIN
-inline
-std::string extractFileID(const BY_HANDLE_FILE_INFORMATION& fileInfo)
-{
- return numberToBytes(fileInfo.dwVolumeSerialNumber) +
- numberToBytes(fileInfo.nFileIndexHigh) +
- numberToBytes(fileInfo.nFileIndexLow);
-}
-#elif defined FFS_LINUX
-inline
-std::string extractFileID(const struct stat& fileInfo)
-{
- return numberToBytes(fileInfo.st_dev) +
- numberToBytes(fileInfo.st_ino);
-}
-#endif
-
-}
-
-
-#endif //FILE_ID_INTERNAL_HEADER_013287632486321493
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 7a2df695..81e70383 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -14,8 +14,8 @@
#include "long_path_prefix.h"
#include "dst_hack.h"
#include "file_update_handle.h"
-//#include "dll_loader.h"
-//#include "FindFilePlus/find_file_plus.h"
+#include "dll.h"
+#include "FindFilePlus/find_file_plus.h"
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -30,33 +30,34 @@ namespace
{
//implement "retry" in a generic way:
-//returns "true" if "cmd" was invoked successfully, "false" if error occured and was ignored
-template <class Command> inline //function object with bool operator()(std::wstring& errorMsg), returns "true" on success, "false" on error and writes "errorMsg" in this case
-bool tryReportingError(Command cmd, zen::TraverseCallback& callback)
+template <class Command> inline //function object expecting to throw FileError if operation fails
+void tryReportingError(Command cmd, zen::TraverseCallback& callback)
{
for (;;)
- {
- std::wstring errorMsg;
- if (cmd(errorMsg))
- return true;
-
- switch (callback.onError(errorMsg))
+ try
{
- case TraverseCallback::TRAV_ERROR_RETRY:
- break;
- case TraverseCallback::TRAV_ERROR_IGNORE:
- return false;
- default:
- assert(false);
- break;
+ cmd();
+ return;
+ }
+ catch (const FileError& e)
+ {
+ switch (callback.onError(e.toString()))
+ {
+ case TraverseCallback::TRAV_ERROR_RETRY:
+ break;
+ case TraverseCallback::TRAV_ERROR_IGNORE:
+ return;
+ default:
+ assert(false);
+ break;
+ }
}
- }
}
#ifdef FFS_WIN
inline
-bool setWin32FileInformationFromSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output)
+bool extractFileInfoFromSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output)
{
//open handle to target of symbolic link
HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(linkName).c_str(),
@@ -75,288 +76,308 @@ bool setWin32FileInformationFromSymlink(const Zstring& linkName, zen::TraverseCa
return false;
//write output
- output.lastWriteTimeRaw = toTimeT(fileInfoByHandle.ftLastWriteTime);
output.fileSize = zen::UInt64(fileInfoByHandle.nFileSizeLow, fileInfoByHandle.nFileSizeHigh);
+ output.lastWriteTimeRaw = toTimeT(fileInfoByHandle.ftLastWriteTime);
+ //output.id = extractFileID(fileInfoByHandle); -> id from dereferenced symlink is problematic, since renaming will consider the link, not the target!
return true;
}
+
+DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
+{
+ //note: this even works for network shares: \\share\dirname
+
+ const DWORD BUFFER_SIZE = 10000;
+ std::vector<wchar_t> buffer(BUFFER_SIZE);
+
+ //full pathName need not yet exist!
+ if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
+ &buffer[0], //__out LPTSTR lpszVolumePathName,
+ BUFFER_SIZE)) //__in DWORD cchBufferLength
+ return 0;
+
+ Zstring volumePath = &buffer[0];
+ if (!endsWith(volumePath, FILE_NAME_SEPARATOR))
+ volumePath += FILE_NAME_SEPARATOR;
+
+ DWORD volumeSerial = 0;
+ if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName,
+ NULL, //__out LPTSTR lpVolumeNameBuffer,
+ 0, //__in DWORD nVolumeNameSize,
+ &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber,
+ NULL, //__out_opt LPDWORD lpMaximumComponentLength,
+ NULL, //__out_opt LPDWORD lpFileSystemFlags,
+ NULL, //__out LPTSTR lpFileSystemNameBuffer,
+ 0)) //__in DWORD nFileSystemNameSize
+ return 0;
+
+ return volumeSerial;
+}
+
+
+const DllFun<findplus::OpenDirFunc> openDir (findplus::getDllName(), findplus::openDirFuncName ); //
+const DllFun<findplus::ReadDirFunc> readDir (findplus::getDllName(), findplus::readDirFuncName ); //load at startup: avoid pre C++11 static initialization MT issues
+const DllFun<findplus::CloseDirFunc> closeDir(findplus::getDllName(), findplus::closeDirFuncName); //
+
+
/*
-warn_static("finish")
- DllFun<findplus::OpenDirFunc> openDir (findplus::getDllName(), findplus::openDirFuncName);
- DllFun<findplus::ReadDirFunc> readDir (findplus::getDllName(), findplus::readDirFuncName);
- DllFun<findplus::CloseDirFunc> closeDir(findplus::getDllName(), findplus::closeDirFuncName);
- -> thread safety!
-*/
-#endif
+Common C-style interface for Win32 FindFirstFile(), FindNextFile() and FileFilePlus openDir(), closeDir():
+struct X //see "policy based design"
+{
+typedef ... Handle;
+typedef ... FindData;
+static Handle create(const Zstring& directoryPf, FindData& fileInfo); //throw FileError
+static void destroy(Handle hnd); //throw()
+static bool next(Handle hnd, const Zstring& directory, WIN32_FIND_DATA& fileInfo) //throw FileError
+
+//helper routines
+static void extractFileInfo (const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output);
+static Int64 getModTime (const FindData& fileInfo);
+static const FILETIME& getModTimeRaw (const FindData& fileInfo); //yet another concession to DST hack
+static const FILETIME& getCreateTimeRaw(const FindData& fileInfo); //
+static const wchar_t* getShortName (const FindData& fileInfo);
+static bool isDirectory (const FindData& fileInfo);
+static bool isSymlink (const FindData& fileInfo);
}
+Note: Win32 FindFirstFile(), FindNextFile() is a weaker abstraction than FileFilePlus openDir(), readDir(), closeDir() and Unix opendir(), closedir(), stat(),
+ so unfortunately we have to use former as a greatest common divisor
+*/
-class DirTraverser
+
+struct Win32Traverser
{
-public:
- DirTraverser(const Zstring& baseDirectory, bool followSymlinks, zen::TraverseCallback& sink, zen::DstHackCallback* dstCallback) :
-#ifdef FFS_WIN
- isFatFileSystem(dst::isFatDrive(baseDirectory)),
-#endif
- followSymlinks_(followSymlinks)
+ typedef HANDLE Handle;
+ typedef WIN32_FIND_DATA FindData;
+
+ static Handle create(const Zstring& directory, FindData& fileInfo) //throw FileError
{
-#ifdef FFS_WIN
- //format base directory name
- const Zstring& directoryFormatted = baseDirectory;
+ const Zstring& directoryPf = endsWith(directory, FILE_NAME_SEPARATOR) ?
+ directory :
+ directory + FILE_NAME_SEPARATOR;
-#elif defined FFS_LINUX
- const Zstring directoryFormatted = //remove trailing slash
- baseDirectory.size() > 1 && endsWith(baseDirectory, FILE_NAME_SEPARATOR) ? //exception: allow '/'
- beforeLast(baseDirectory, FILE_NAME_SEPARATOR) :
- baseDirectory;
-#endif
+ HANDLE output = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &fileInfo);
+ //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
+ if (output == INVALID_HANDLE_VALUE)
+ throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
+ //::GetLastError() == ERROR_FILE_NOT_FOUND -> actually NOT okay, even for an empty directory this should not occur (., ..)
+ return output;
+ }
- //traverse directories
- traverse(directoryFormatted, sink, 0);
+ static void destroy(Handle hnd) { ::FindClose(hnd); } //throw()
- //apply daylight saving time hack AFTER file traversing, to give separate feedback to user
-#ifdef FFS_WIN
- if (isFatFileSystem && dstCallback)
- applyDstHack(*dstCallback);
-#endif
+ static bool next(Handle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ {
+ if (!::FindNextFile(hnd, &fileInfo))
+ {
+ if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
+ return false;
+ //else we have a problem... report it:
+ throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
+ }
+ return true;
}
-private:
- DirTraverser(const DirTraverser&);
- DirTraverser& operator=(const DirTraverser&);
+ //helper routines
+ template <class FindData>
+ static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
+ {
+ output.lastWriteTimeRaw = getModTime(fileInfo);
+ output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
+ }
- void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level)
+ template <class FindData>
+ static Int64 getModTime(const FindData& fileInfo) { return toTimeT(fileInfo.ftLastWriteTime); }
+
+ template <class FindData>
+ static const FILETIME& getModTimeRaw(const FindData& fileInfo) { return fileInfo.ftLastWriteTime; }
+
+ template <class FindData>
+ static const FILETIME& getCreateTimeRaw(const FindData& fileInfo) { return fileInfo.ftCreationTime; }
+
+ template <class FindData>
+ static const wchar_t* getShortName(const FindData& fileInfo) { return fileInfo.cFileName; }
+
+ template <class FindData>
+ static bool isDirectory(const FindData& fileInfo) { return (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; }
+
+ template <class FindData>
+ static bool isSymlink(const FindData& fileInfo) { return (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; }
+};
+
+
+struct FilePlusTraverser
+{
+ typedef findplus::FindHandle Handle;
+ typedef findplus::FileInformation FindData;
+
+ static Handle create(const Zstring& directory, FindData& fileInfo) //throw FileError
{
- tryReportingError([&](std::wstring& errorMsg) -> bool
+ Handle output = ::openDir(applyLongPathPrefix(directory).c_str());
+ if (output == NULL)
+ throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
+
+ bool rv = next(output, directory, fileInfo); //throw FileError
+ if (!rv) //we expect at least two successful reads, even for an empty directory: ., ..
+ throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted(ERROR_NO_MORE_FILES));
+
+ return output;
+ }
+
+ static void destroy(Handle hnd) { ::closeDir(hnd); } //throw()
+
+ static bool next(Handle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ {
+ if (!::readDir(hnd, fileInfo))
{
- if (level == 100) //notify endless recursion
- {
- errorMsg = _("Endless loop when traversing directory:") + L"\n\"" + directory + L"\"";
+ if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
return false;
- }
- return true;
- }, sink);
+ //else we have a problem... report it:
+ throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
+ }
+ return true;
+ }
-#ifdef FFS_WIN
- /*
- //ensure directoryPf ends with backslash
- const Zstring& directoryPf = directory.EndsWith(FILE_NAME_SEPARATOR) ?
- directory :
- directory + FILE_NAME_SEPARATOR;
-
- FindHandle searchHandle = NULL;
- tryReportingError([&](std::wstring& errorMsg) -> bool
- {
- searchHandle = this->openDir(applyLongPathPrefix(directoryPf).c_str());
- //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
+ //helper routines
+ template <class FindData>
+ static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
+ {
+ output.fileSize = UInt64(fileInfo.fileSize.QuadPart);
+ output.lastWriteTimeRaw = getModTime(fileInfo);
+ if (volumeSerial)
+ output.id = extractFileID(*volumeSerial, fileInfo.fileId);
+ }
- if (searchHandle == NULL)
- {
- //const DWORD lastError = ::GetLastError();
- //if (lastError == ERROR_FILE_NOT_FOUND) -> actually NOT okay, even for an empty directory this should not occur (., ..)
- //return true; //fine: empty directory
+ template <class FindData>
+ static Int64 getModTime(const FindData& fileInfo) { return toTimeT(fileInfo.lastWriteTime); }
- //else: we have a problem... report it:
- errorMsg = _("Error traversing directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted();
- return false;
- }
- return true;
- }, sink);
+ template <class FindData>
+ static const FILETIME& getModTimeRaw(const FindData& fileInfo) { return fileInfo.lastWriteTime; }
- if (searchHandle == NULL)
- return; //empty dir or ignore error
- ZEN_ON_BLOCK_EXIT(this->closeDir(searchHandle));
+ template <class FindData>
+ static const FILETIME& getCreateTimeRaw(const FindData& fileInfo) { return fileInfo.creationTime; }
- FileInformation fileInfo = {};
- for (;;)
- {
- bool moreData = false;
- tryReportingError([&](std::wstring& errorMsg) -> bool
- {
- if (!this->readDir(searchHandle, fileInfo))
- {
- if (::GetLastError() == ERROR_NO_MORE_FILES) //this is fine
- return true;
-
- //else we have a problem... report it:
- errorMsg = _("Error traversing directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted();
- return false;
- }
-
- moreData = true;
- return true;
- }, sink);
- if (!moreData) //no more items or ignore error
- return;
-
-
- //don't return "." and ".."
- const Zchar* const shortName = fileInfo.shortName;
- if (shortName[0] == L'.' &&
- (shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0')))
- continue;
+ template <class FindData>
+ static const wchar_t* getShortName(const FindData& fileInfo) { return fileInfo.shortName; }
- const Zstring& fullName = directoryPf + shortName;
+ template <class FindData>
+ static bool isDirectory(const FindData& fileInfo) { return (fileInfo.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; }
- const bool isSymbolicLink = (fileInfo.fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ template <class FindData>
+ static bool isSymlink(const FindData& fileInfo) { return (fileInfo.fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; }
+};
- if (isSymbolicLink && !followSymlinks_) //evaluate symlink directly
- {
- TraverseCallback::SymlinkInfo details;
- try
- {
- details.targetPath = getSymlinkRawTargetString(fullName); //throw FileError
- }
- catch (FileError& e)
- {
- (void)e;
- #ifndef NDEBUG //show broken symlink / access errors in debug build!
- sink.onError(e.msg());
- #endif
- }
-
- details.lastWriteTimeRaw = toTimeT(fileInfo.lastWriteTime);
- details.dirLink = (fileInfo.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; //directory symlinks have this flag on Windows
- sink.onSymlink(shortName, fullName, details);
- }
- else if (fileInfo.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!)
- {
- const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName);
- if (rv)
- traverse<followSymlinks_>(fullName, *rv, level + 1);
- }
- else //a file or symlink that is followed...
- {
- TraverseCallback::FileInfo details;
-
- if (isSymbolicLink) //dereference symlinks!
- {
- if (!setWin32FileInformationFromSymlink(fullName, details))
- {
- //broken symlink...
- details.lastWriteTimeRaw = 0; //we are not interested in the modification time of the link
- details.fileSize = 0U;
- }
- }
- else
- {
- //####################################### DST hack ###########################################
- if (isFatFileSystem)
- {
- const dst::RawTime rawTime(fileInfo.creationTime, fileInfo.lastWriteTime);
-
- if (dst::fatHasUtcEncoded(rawTime)) //throw (std::runtime_error)
- fileInfo.lastWriteTime = dst::fatDecodeUtcTime(rawTime); //return real UTC time; throw (std::runtime_error)
- else
- markForDstHack.push_back(std::make_pair(fullName, fileInfo.lastWriteTime));
- }
- //####################################### DST hack ###########################################
-
- details.lastWriteTimeRaw = toTimeT(fileInfo.lastWriteTime);
- details.fileSize = fileInfo.fileSize.QuadPart;
- }
-
- sink.onFile(shortName, fullName, details);
- }
- }
- */
+class DirTraverser
+{
+public:
+ DirTraverser(const Zstring& baseDirectory, bool followSymlinks, zen::TraverseCallback& sink, zen::DstHackCallback* dstCallback) :
+ isFatFileSystem(dst::isFatDrive(baseDirectory)),
+ followSymlinks_(followSymlinks),
+ volumeSerial(retrieveVolumeSerial(baseDirectory)) //return 0 on error
+ {
+ try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail)
+ {
+ activatePrivilege(SE_BACKUP_NAME); //throw FileError
+ }
+ catch (...) {} //don't cause issues in user mode
+ if (::openDir && ::readDir && ::closeDir)
+ traverse<FilePlusTraverser>(baseDirectory, sink, 0);
+ else //fallback
+ traverse<Win32Traverser>(baseDirectory, sink, 0);
- //ensure directoryPf ends with backslash
- const Zstring& directoryPf = endsWith(directory, FILE_NAME_SEPARATOR) ?
- directory :
- directory + FILE_NAME_SEPARATOR;
- WIN32_FIND_DATA fileInfo = {};
+ //apply daylight saving time hack AFTER file traversing, to give separate feedback to user
+ if (dstCallback && isFatFileSystem)
+ applyDstHack(*dstCallback);
+ }
- HANDLE searchHandle = INVALID_HANDLE_VALUE;
- tryReportingError([&](std::wstring& errorMsg) -> bool
+private:
+ DirTraverser(const DirTraverser&);
+ DirTraverser& operator=(const DirTraverser&);
+
+ template <class Trav>
+ void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level)
+ {
+ tryReportingError([&]
{
- searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &fileInfo);
- //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
+ if (level == 100) //notify endless recursion
+ throw FileError(_("Endless loop when traversing directory:") + L"\n\"" + directory + L"\"");
+ }, sink);
- if (searchHandle == INVALID_HANDLE_VALUE)
- {
- //const DWORD lastError = ::GetLastError();
- //if (lastError == ERROR_FILE_NOT_FOUND) -> actually NOT okay, even for an empty directory this should not occur (., ..)
- //return true; //fine: empty directory
+ typename Trav::FindData fileInfo = {};
- //else: we have a problem... report it:
- errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted();
- return false;
- }
- return true;
+ typename Trav::Handle searchHandle = 0;
+
+ tryReportingError([&]
+ {
+ typedef Trav Trav; //f u VS!
+ searchHandle = Trav::create(directory, fileInfo); //throw FileError
}, sink);
- if (searchHandle == INVALID_HANDLE_VALUE)
- return; //empty dir or ignore error
- ZEN_ON_BLOCK_EXIT(::FindClose(searchHandle));
+ if (searchHandle == 0)
+ return; //ignored error
+ ZEN_ON_BLOCK_EXIT(typedef Trav Trav; Trav::destroy(searchHandle));
do
{
//don't return "." and ".."
- const Zchar* const shortName = fileInfo.cFileName;
+ const Zchar* const shortName = Trav::getShortName(fileInfo);
if (shortName[0] == L'.' &&
(shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0')))
continue;
- const Zstring& fullName = directoryPf + shortName;
-
- const bool isSymbolicLink = (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ const Zstring& fullName = endsWith(directory, FILE_NAME_SEPARATOR) ?
+ directory + shortName :
+ directory + FILE_NAME_SEPARATOR + shortName;
- if (isSymbolicLink && !followSymlinks_) //evaluate symlink directly
+ if (Trav::isSymlink(fileInfo) && !followSymlinks_) //evaluate symlink directly
{
TraverseCallback::SymlinkInfo details;
try
{
details.targetPath = getSymlinkRawTargetString(fullName); //throw FileError
}
- catch (FileError& e)
- {
- (void)e;
-#ifndef NDEBUG //show broken symlink / access errors in debug build!
- sink.onError(e.toString());
+#ifdef NDEBUG //Release
+ catch (FileError&) {}
+#else
+ catch (FileError& e) { sink.onError(e.toString()); } //show broken symlink / access errors in debug build!
#endif
- }
- details.lastWriteTimeRaw = toTimeT(fileInfo.ftLastWriteTime);
- details.dirLink = (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; //directory symlinks have this flag on Windows
+ details.lastWriteTimeRaw = Trav::getModTime (fileInfo);
+ details.dirLink = Trav::isDirectory(fileInfo); //directory symlinks have this flag on Windows
sink.onSymlink(shortName, fullName, details);
}
- else if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!)
+ else if (Trav::isDirectory(fileInfo)) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!)
{
const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName);
if (rv)
- traverse(fullName, *rv, level + 1);
+ traverse<Trav>(fullName, *rv, level + 1);
}
else //a file or symlink that is followed...
{
TraverseCallback::FileInfo details;
- if (isSymbolicLink) //dereference symlinks!
+ if (Trav::isSymlink(fileInfo)) //dereference symlinks!
{
- if (!setWin32FileInformationFromSymlink(fullName, details))
- {
- //broken symlink...
- details.lastWriteTimeRaw = 0; //we are not interested in the modification time of the link
- details.fileSize = 0U;
- }
+ extractFileInfoFromSymlink(fullName, details); //try to...
+ //keep details initial if symlink is broken
}
else
{
+ Trav::extractFileInfo(fileInfo, volumeSerial != 0 ? &volumeSerial : nullptr, details); //make optional character of volumeSerial explicit in the interface
+
//####################################### DST hack ###########################################
if (isFatFileSystem)
{
- const dst::RawTime rawTime(fileInfo.ftCreationTime, fileInfo.ftLastWriteTime);
+ const dst::RawTime rawTime(Trav::getCreateTimeRaw(fileInfo), Trav::getModTimeRaw(fileInfo));
if (dst::fatHasUtcEncoded(rawTime)) //throw (std::runtime_error)
- fileInfo.ftLastWriteTime = dst::fatDecodeUtcTime(rawTime); //return real UTC time; throw (std::runtime_error)
+ details.lastWriteTimeRaw = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error)
else
- markForDstHack.push_back(std::make_pair(fullName, fileInfo.ftLastWriteTime));
+ markForDstHack.push_back(std::make_pair(fullName, Trav::getModTimeRaw(fileInfo)));
}
//####################################### DST hack ###########################################
- details.lastWriteTimeRaw = toTimeT(fileInfo.ftLastWriteTime);
- details.fileSize = zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
}
sink.onFile(shortName, fullName, details);
@@ -366,141 +387,18 @@ private:
{
bool moreData = false;
- tryReportingError([&](std::wstring& errorMsg) -> bool
+ typedef Trav Trav1; //f u VS!
+ tryReportingError([&]
{
- if (!::FindNextFile(searchHandle, // handle to search
- &fileInfo)) // pointer to structure for data on found file
- {
- if (::GetLastError() == ERROR_NO_MORE_FILES) //this is fine
- return true;
-
- //else we have a problem... report it:
- errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted();
- return false;
- }
-
- moreData = true;
- return true;
+ typedef Trav1 Trav; //f u VS!
+ moreData = Trav::next(searchHandle, directory, fileInfo); //throw FileError
}, sink);
return moreData;
}());
-
-#elif defined FFS_LINUX
- DIR* dirObj = NULL;
- if (!tryReportingError([&](std::wstring& errorMsg) -> bool
- {
- dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/"
- if (dirObj == NULL)
- {
- errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted();
- return false;
- }
- return true;
- }, sink))
- return;
- ZEN_ON_BLOCK_EXIT(::closedir(dirObj)); //never close NULL handles! -> crash
-
- while (true)
- {
- struct dirent* dirEntry = NULL;
- tryReportingError([&](std::wstring& errorMsg) -> bool
- {
- errno = 0; //set errno to 0 as unfortunately this isn't done when readdir() returns NULL because it can't find any files
- dirEntry = ::readdir(dirObj);
- if (dirEntry == NULL)
- {
- if (errno == 0)
- return true; //everything okay, not more items
-
- //else: we have a problem... report it:
- errorMsg = _("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted();
- return false;
- }
- return true;
- }, sink);
- if (dirEntry == NULL) //no more items or ignore error
- return;
-
-
- //don't return "." and ".."
- const char* const shortName = dirEntry->d_name;
- if (shortName[0] == '.' &&
- (shortName[1] == '\0' || (shortName[1] == '.' && shortName[2] == '\0')))
- continue;
-
- const Zstring& fullName = endsWith(directory, FILE_NAME_SEPARATOR) ? //e.g. "/"
- directory + shortName :
- directory + FILE_NAME_SEPARATOR + shortName;
-
- struct stat fileInfo = {};
-
- if (!tryReportingError([&](std::wstring& errorMsg) -> bool
- {
- if (::lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks
- {
- errorMsg = _("Error reading file attributes:") + L"\n\"" + fullName + L"\"" + L"\n\n" + zen::getLastErrorFormatted();
- return false;
- }
- return true;
- }, sink))
- continue; //ignore error: skip file
-
- const bool isSymbolicLink = S_ISLNK(fileInfo.st_mode);
-
- if (isSymbolicLink)
- {
- if (followSymlinks_) //on Linux Symlinks need to be followed to evaluate whether they point to a file or directory
- {
- if (::stat(fullName.c_str(), &fileInfo) != 0) //stat() resolves symlinks
- {
- sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); //report broken symlink as file!
- continue;
- }
- }
- else //evaluate symlink directly
- {
- TraverseCallback::SymlinkInfo details;
- try
- {
- details.targetPath = getSymlinkRawTargetString(fullName); //throw FileError
- }
- catch (FileError& e)
- {
-#ifndef NDEBUG //show broken symlink / access errors in debug build!
- sink.onError(e.toString());
-#endif
- }
-
- details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
- details.dirLink = ::stat(fullName.c_str(), &fileInfo) == 0 && S_ISDIR(fileInfo.st_mode); //S_ISDIR and S_ISLNK are mutually exclusive on Linux => need to follow link
- sink.onSymlink(shortName, fullName, details);
- continue;
- }
- }
-
- //fileInfo contains dereferenced data in any case from here on
-
- if (S_ISDIR(fileInfo.st_mode)) //a directory... cannot be a symlink on Linux in this case
- {
- const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName);
- if (rv)
- traverse(fullName, *rv, level + 1);
- }
- else //a file... (or symlink; pathological!)
- {
- TraverseCallback::FileInfo details;
- details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
- details.fileSize = zen::UInt64(fileInfo.st_size);
-
- sink.onFile(shortName, fullName, details);
- }
- }
-#endif
}
-#ifdef FFS_WIN
//####################################### DST hack ###########################################
void applyDstHack(zen::DstHackCallback& dstCallback)
{
@@ -517,7 +415,7 @@ private:
const dst::RawTime encodedTime = dst::fatEncodeUtcTime(i->second); //throw (std::runtime_error)
{
//may need to remove the readonly-attribute (e.g. FAT usb drives)
- FileUpdateHandle updateHandle(i->first, [ = ]()
+ FileUpdateHandle updateHandle(i->first, [=]()
{
return ::CreateFile(zen::applyLongPathPrefix(i->first).c_str(),
GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp
@@ -571,20 +469,167 @@ private:
typedef std::vector<std::pair<Zstring, FILETIME> > FilenameTimeList;
FilenameTimeList markForDstHack;
//####################################### DST hack ###########################################
-#endif
+
const bool followSymlinks_;
+ const DWORD volumeSerial; //may be 0!
};
-void zen::traverseFolder(const Zstring& directory, bool followSymlinks, TraverseCallback& sink, DstHackCallback* dstCallback)
+#elif defined FFS_LINUX
+class DirTraverser
{
-#ifdef FFS_WIN
- try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail)
+public:
+ DirTraverser(const Zstring& baseDirectory, bool followSymlinks, zen::TraverseCallback& sink, zen::DstHackCallback* dstCallback) :
+ followSymlinks_(followSymlinks)
{
- zen::Privileges::getInstance().ensureActive(SE_BACKUP_NAME); //throw FileError
+ const Zstring directoryFormatted = //remove trailing slash
+ baseDirectory.size() > 1 && endsWith(baseDirectory, FILE_NAME_SEPARATOR) ? //exception: allow '/'
+ beforeLast(baseDirectory, FILE_NAME_SEPARATOR) :
+ baseDirectory;
+
+ /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede
+ that field within the dirent structure, portable applications that use readdir_r() should allocate
+ the buffer whose address is passed in entry as follows:
+ len = offsetof(struct dirent, d_name) + pathconf(dirpath, _PC_NAME_MAX) + 1
+ entryp = malloc(len); */
+ const long maxPath = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return -1
+ buffer.resize(offsetof(struct ::dirent, d_name) + maxPath + 1);
+
+ traverse(directoryFormatted, sink, 0);
}
- catch (...) {} //don't cause issues in user mode
+
+private:
+ DirTraverser(const DirTraverser&);
+ DirTraverser& operator=(const DirTraverser&);
+
+ void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level)
+ {
+ tryReportingError([&]
+ {
+ if (level == 100) //notify endless recursion
+ throw FileError(_("Endless loop when traversing directory:") + L"\n\"" + directory + L"\"");
+ }, sink);
+
+
+ DIR* dirObj = NULL;
+ tryReportingError([&]
+ {
+ dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/"
+ if (dirObj == NULL)
+ throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
+ }, sink);
+
+ if (dirObj == NULL)
+ return; //ignored error
+ ZEN_ON_BLOCK_EXIT(::closedir(dirObj)); //never close NULL handles! -> crash
+
+ while (true)
+ {
+ struct ::dirent* dirEntry = NULL;
+ tryReportingError([&]
+ {
+ if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
+ throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
+ }, sink);
+ if (dirEntry == NULL) //no more items or ignore error
+ return;
+
+
+ //don't return "." and ".."
+ const char* const shortName = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"!
+ if (shortName[0] == '.' &&
+ (shortName[1] == '\0' || (shortName[1] == '.' && shortName[2] == '\0')))
+ continue;
+
+ const Zstring& fullName = endsWith(directory, FILE_NAME_SEPARATOR) ? //e.g. "/"
+ directory + shortName :
+ directory + FILE_NAME_SEPARATOR + shortName;
+
+ struct ::stat fileInfo = {};
+ bool haveData = false;
+ tryReportingError([&]
+ {
+ if (::lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks
+ throw FileError(_("Error reading file attributes:") + L"\n\"" + fullName + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
+ haveData = true;
+ }, sink);
+ if (!haveData)
+ continue; //ignore error: skip file
+
+ if (S_ISLNK(fileInfo.st_mode))
+ {
+ if (followSymlinks_) //on Linux Symlinks need to be followed to evaluate whether they point to a file or directory
+ {
+ if (::stat(fullName.c_str(), &fileInfo) != 0) //stat() resolves symlinks
+ {
+ sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); //report broken symlink as file!
+ continue;
+ }
+
+ fileInfo.st_dev = 0; //id from dereferenced symlink is problematic, since renaming will consider the link, not the target!
+ fileInfo.st_ino = 0; //
+ }
+ else //evaluate symlink directly
+ {
+ TraverseCallback::SymlinkInfo details;
+ try
+ {
+ details.targetPath = getSymlinkRawTargetString(fullName); //throw FileError
+ }
+ catch (FileError& e)
+ {
+#ifndef NDEBUG //show broken symlink / access errors in debug build!
+ sink.onError(e.toString());
#endif
+ }
+
+ details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
+ details.dirLink = ::stat(fullName.c_str(), &fileInfo) == 0 && S_ISDIR(fileInfo.st_mode);
+ //S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link
+ sink.onSymlink(shortName, fullName, details);
+ continue;
+ }
+ }
+
+ //fileInfo contains dereferenced data in any case from here on
+
+ if (S_ISDIR(fileInfo.st_mode)) //a directory... cannot be a symlink on Linux in this case
+ {
+ const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName);
+ if (rv)
+ traverse(fullName, *rv, level + 1);
+ }
+ else //a file... (or symlink; pathological!)
+ {
+ TraverseCallback::FileInfo details;
+ details.fileSize = zen::UInt64(fileInfo.st_size);
+ details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time (time_t format); unit: 1 second
+ details.id = extractFileID(fileInfo);
+
+ sink.onFile(shortName, fullName, details);
+ }
+ }
+ }
+ std::vector<char> buffer;
+ const bool followSymlinks_;
+};
+#endif
+}
+
+
+void zen::traverseFolder(const Zstring& directory, bool followSymlinks, TraverseCallback& sink, DstHackCallback* dstCallback)
+{
DirTraverser(directory, followSymlinks, sink, dstCallback);
}
+
+
+bool zen::supportForFileId() //Linux: always; Windows: if FindFilePlus_Win32.dll was loaded correctly
+{
+#ifdef FFS_WIN
+ return ::openDir && ::readDir && ::closeDir;
+
+#elif defined FFS_LINUX
+ return true;
+#endif
+}
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index 3f4f47d5..075c32e5 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -10,6 +10,8 @@
#include <memory>
#include "zstring.h"
#include "int64.h"
+#include "file_id_def.h"
+
//advanced file traverser returning metadata and hierarchical information on files and directories
@@ -24,6 +26,7 @@ public:
{
UInt64 fileSize; //unit: bytes!
Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC
+ FileId id; //optional: may be initial!
};
struct SymlinkInfo
@@ -67,6 +70,10 @@ void traverseFolder(const Zstring& directory, //throw();
//followSymlinks:
//"true": Symlinks dereferenced and reported via onFile() and onDir() => onSymlink not used!
//"false": Symlinks directly reported via onSymlink(), directory symlinks are not followed
+
+
+//determine whether FileId can be expected to be retrieved
+bool supportForFileId(); //Linux: always; Windows: if FindFilePlus_Win32.dll was loaded correctly
}
#endif // FILETRAVERSER_H_INCLUDED
diff --git a/zen/guid.h b/zen/guid.h
index 218c80a5..f9f497b2 100644
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -10,7 +10,7 @@
#include <string>
#include <boost/uuid/uuid.hpp>
-#ifdef __MINGW32__ //boost should start and clean up!
+#ifdef __MINGW32__ //boost should start cleaning this mess up
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wuninitialized"
diff --git a/zen/int64.h b/zen/int64.h
index e8a9cbe0..91e24437 100644
--- a/zen/int64.h
+++ b/zen/int64.h
@@ -56,7 +56,7 @@ public:
value(rhs) {} //-> std::int64_t equals long int on x64 Linux! Still we want implicit behavior for all other systems!
//unsafe explicit but checked conversion from arbitrary integer type
- template <class T> explicit Int64(T rhs) : value(rhs) { checkRange<std::int64_t>(rhs); }
+ template <class T> explicit Int64(T rhs) : value(static_cast<std::int64_t>(rhs)) { checkRange<std::int64_t>(rhs); }
Int64& operator=(const Int64& rhs) { value = rhs.value; return *this; }
@@ -135,7 +135,7 @@ public:
value(rhs) {} //-> std::uint64_t equals unsigned long int on x64 Linux! Still we want implicit behavior for all other systems!
//unsafe explicit but checked conversion from arbitrary integer type
- template <class T> explicit UInt64(T rhs) : value(rhs) { checkRange<std::uint64_t>(rhs); }
+ template <class T> explicit UInt64(T rhs) : value(static_cast<std::uint64_t>(rhs)) { checkRange<std::uint64_t>(rhs); }
UInt64& operator=(const UInt64& rhs) { value = rhs.value; return *this; }
diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h
index 1d9213d1..d03409e1 100644
--- a/zen/long_path_prefix.h
+++ b/zen/long_path_prefix.h
@@ -7,6 +7,7 @@
#ifndef LONGPATHPREFIX_H_INCLUDED
#define LONGPATHPREFIX_H_INCLUDED
+#include "win.h"
#include "zstring.h"
namespace zen
diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp
index 8d8eeb90..2b6b9003 100644
--- a/zen/notify_removal.cpp
+++ b/zen/notify_removal.cpp
@@ -74,11 +74,10 @@ private:
const wchar_t MessageProvider::WINDOW_NAME[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name
-LRESULT CALLBACK topWndProc(
- HWND hwnd, //handle to window
- UINT uMsg, //message identifier
- WPARAM wParam, //first message parameter
- LPARAM lParam) //second message parameter
+LRESULT CALLBACK topWndProc(HWND hwnd, //handle to window
+ UINT uMsg, //message identifier
+ WPARAM wParam, //first message parameter
+ LPARAM lParam) //second message parameter
{
if (messageProviderConstructed) //attention: this callback is triggered in the middle of singleton construction! It is a bad idea to to call back at this time!
try
@@ -110,18 +109,17 @@ MessageProvider::MessageProvider() :
zen::ScopeGuard guardClass = zen::makeGuard([&]() { ::UnregisterClass(WINDOW_NAME, process); });
//create dummy-window
- windowHandle = ::CreateWindow(
- WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word!
- NULL, //LPCTSTR lpWindowName,
- 0, //DWORD dwStyle,
- 0, //int x,
- 0, //int y,
- 0, //int nWidth,
- 0, //int nHeight,
- 0, //note: we need a toplevel window to receive device arrival events, not a message-window (HWND_MESSAGE)!
- NULL, //HMENU hMenu,
- process, //HINSTANCE hInstance,
- NULL); //LPVOID lpParam
+ windowHandle = ::CreateWindow(WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word!
+ NULL, //LPCTSTR lpWindowName,
+ 0, //DWORD dwStyle,
+ 0, //int x,
+ 0, //int y,
+ 0, //int nWidth,
+ 0, //int nHeight,
+ 0, //note: we need a toplevel window to receive device arrival events, not a message-window (HWND_MESSAGE)!
+ NULL, //HMENU hMenu,
+ process, //HINSTANCE hInstance,
+ NULL); //LPVOID lpParam
if (windowHandle == NULL)
throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (CreateWindow)");
@@ -160,17 +158,16 @@ public:
filter.dbch_devicetype = DBT_DEVTYP_HANDLE;
filter.dbch_handle = hDir;
- hNotification = ::RegisterDeviceNotification(
- MessageProvider::instance().getWnd(), //__in HANDLE hRecipient,
- &filter, //__in LPVOID NotificationFilter,
- DEVICE_NOTIFY_WINDOW_HANDLE); //__in DWORD Flags
+ hNotification = ::RegisterDeviceNotification(MessageProvider::instance().getWnd(), //__in HANDLE hRecipient,
+ &filter, //__in LPVOID NotificationFilter,
+ DEVICE_NOTIFY_WINDOW_HANDLE); //__in DWORD Flags
if (hNotification == NULL)
{
const DWORD lastError = ::GetLastError();
if (lastError != ERROR_CALL_NOT_IMPLEMENTED && //fail on SAMBA share: this shouldn't be a showstopper!
lastError != ERROR_SERVICE_SPECIFIC_ERROR && //neither should be fail for "Pogoplug" mapped network drives
lastError != ERROR_INVALID_DATA) //this seems to happen for a NetDrive-mapped FTP server
- throw zen::FileError(std::wstring(L"Could not register device removal notifications:") + L"\n\n" + getLastErrorFormatted(lastError));
+ throw zen::FileError(L"Could not register device removal notifications:" L"\n\n" + getLastErrorFormatted(lastError));
}
}
diff --git a/zen/privilege.cpp b/zen/privilege.cpp
index 809202b7..6dd0b2d7 100644
--- a/zen/privilege.cpp
+++ b/zen/privilege.cpp
@@ -1,17 +1,15 @@
#include "privilege.h"
+#include <map>
+#include "thread.h" //includes <boost/thread.hpp>
+#include "zstring.h"
#include "scope_guard.h"
using namespace zen;
-Privileges& Privileges::getInstance()
+namespace
{
- static Privileges instance;
- return instance;
-}
-
-
-bool Privileges::privilegeIsActive(LPCTSTR privilege) //throw FileError
+bool privilegeIsActive(LPCTSTR privilege) //throw FileError
{
HANDLE hToken = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
@@ -42,7 +40,7 @@ bool Privileges::privilegeIsActive(LPCTSTR privilege) //throw FileError
}
-void Privileges::setPrivilege(LPCTSTR privilege, bool enable) //throw FileError
+void setPrivilege(LPCTSTR privilege, bool enable) //throw FileError
{
HANDLE hToken = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
@@ -62,15 +60,67 @@ void Privileges::setPrivilege(LPCTSTR privilege, bool enable) //throw FileError
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
- if (!::AdjustTokenPrivileges(
- hToken, //__in HANDLE TokenHandle,
- false, //__in BOOL DisableAllPrivileges,
- &tp, //__in_opt PTOKEN_PRIVILEGES NewState,
- 0, //__in DWORD BufferLength,
- NULL, //__out_opt PTOKEN_PRIVILEGES PreviousState,
- NULL)) //__out_opt PDWORD ReturnLength
+ if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle,
+ false, //__in BOOL DisableAllPrivileges,
+ &tp, //__in_opt PTOKEN_PRIVILEGES NewState,
+ 0, //__in DWORD BufferLength,
+ NULL, //__out_opt PTOKEN_PRIVILEGES PreviousState,
+ NULL)) //__out_opt PDWORD ReturnLength
throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted());
if (::GetLastError() == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success!
throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted());
}
+
+
+class Privileges
+{
+public:
+ static Privileges& getInstance()
+ {
+ static Privileges inst;
+ return inst;
+ }
+
+ void ensureActive(LPCTSTR privilege) //throw FileError
+ {
+ if (activePrivileges.find(privilege) != activePrivileges.end())
+ return; //privilege already active
+
+ if (privilegeIsActive(privilege)) //privilege was already active before starting this tool
+ activePrivileges.insert(std::make_pair(privilege, false));
+ else
+ {
+ setPrivilege(privilege, true);
+ activePrivileges.insert(std::make_pair(privilege, true));
+ }
+ }
+
+private:
+ Privileges() {}
+ Privileges(Privileges&);
+ void operator=(Privileges&);
+
+ ~Privileges() //clean up: deactivate all privileges that have been activated by this application
+ {
+ for (auto iter = activePrivileges.begin(); iter != activePrivileges.end(); ++iter)
+ if (iter->second)
+ try
+ {
+ setPrivilege(iter->first.c_str(), false);
+ }
+ catch (...) {}
+ }
+
+ std::map<Zstring, bool> activePrivileges; //bool: enabled by this application
+};
+
+boost::mutex lockPrivileges;
+}
+
+
+void zen::activatePrivilege(LPCTSTR privilege) //throw FileError
+{
+ boost::lock_guard<boost::mutex> dummy(lockPrivileges);
+ Privileges::getInstance().ensureActive(privilege);
+}
diff --git a/zen/privilege.h b/zen/privilege.h
index 4545aac7..88fc8992 100644
--- a/zen/privilege.h
+++ b/zen/privilege.h
@@ -7,57 +7,12 @@
#ifndef PRIVILEGE_H_INCLUDED
#define PRIVILEGE_H_INCLUDED
-#include <map>
-#include "zstring.h"
#include "file_error.h"
#include "win.h" //includes "windows.h"
namespace zen
{
-#ifdef FFS_WIN
-class Privileges
-{
-public:
- static Privileges& getInstance();
-
- void ensureActive(LPCTSTR privilege) //throw FileError
- {
- if (activePrivileges.find(privilege) != activePrivileges.end())
- return; //privilege already active
-
- if (privilegeIsActive(privilege)) //privilege was already active before starting this tool
- activePrivileges.insert(std::make_pair(privilege, false));
- else
- {
- setPrivilege(privilege, true);
- activePrivileges.insert(std::make_pair(privilege, true));
- }
- }
-
-private:
- Privileges() {}
- Privileges(Privileges&);
- void operator=(Privileges&);
-
- ~Privileges() //clean up: deactivate all privileges that have been activated by this application
- {
- for (PrivBuffType::const_iterator i = activePrivileges.begin(); i != activePrivileges.end(); ++i)
- try
- {
- if (i->second)
- Privileges::setPrivilege(i->first.c_str(), false);
- }
- catch (...) {}
- }
-
- static bool privilegeIsActive(LPCTSTR privilege); //throw FileError
- static void setPrivilege(LPCTSTR privilege, bool enable); //throw FileError
-
- typedef std::map<Zstring, bool> PrivBuffType; //bool: enabled by this application
-
- PrivBuffType activePrivileges;
-};
-#endif
+void activatePrivilege(LPCTSTR privilege); //throw FileError; thread-safe!!!
}
diff --git a/zen/process_status.h b/zen/process_status.h
index cc5825aa..15266b28 100644
--- a/zen/process_status.h
+++ b/zen/process_status.h
@@ -3,29 +3,55 @@
#ifdef FFS_WIN
#include "win.h" //includes "windows.h"
+
+#elif defined FFS_LINUX
#endif
namespace zen
{
-struct DisableStandby //signal a "busy" state to the operating system
+struct PreventStandby //signal a "busy" state to the operating system
{
#ifdef FFS_WIN
- DisableStandby() { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); }
- ~DisableStandby() { ::SetThreadExecutionState(ES_CONTINUOUS); }
+ PreventStandby() { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); }
+ ~PreventStandby() { ::SetThreadExecutionState(ES_CONTINUOUS); }
#endif
};
+struct ScheduleForBackgroundProcessing //lower CPU and file I/O priorities
+{
+#ifdef FFS_WIN
#ifndef PROCESS_MODE_BACKGROUND_BEGIN
#define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 // Windows Server 2003 and Windows XP/2000: This value is not supported!
#define PROCESS_MODE_BACKGROUND_END 0x00200000 //
#endif
-struct ScheduleForBackgroundProcessing //lower CPU and file I/O priorities
-{
-#ifdef FFS_WIN
- ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); }
+ ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); } //this call lowers CPU priority, too!!
~ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END); }
+
+#elif defined FFS_LINUX
+ /*
+ CPU prio:
+ int getpriority(PRIO_PROCESS, 0); - errno caveat!
+ int ::setpriority(PRIO_PROCESS, 0, int prio); //a zero value for "who" denotes the calling process
+
+ priority can be decreased, but NOT increased :(
+
+ file I/O prio:
+ ScheduleForBackgroundProcessing() : oldIoPrio(::ioprio_get(IOPRIO_WHO_PROCESS, ::getpid()))
+ {
+ if (oldIoPrio != -1)
+ ::ioprio_set(IOPRIO_WHO_PROCESS, ::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
+ }
+ ~ScheduleForBackgroundProcessing()
+ {
+ if (oldIoPrio != -1)
+ ::ioprio_set(IOPRIO_WHO_PROCESS, ::getpid(), oldIoPrio);
+ }
+
+ private:
+ const int oldIoPrio;
+ */
#endif
};
}
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 1707a1eb..96101821 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -28,11 +28,11 @@ void map_remove_if(M& map, Predicate p);
template <class ForwardIterator, class T, typename Compare>
ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp);
-template<class BidirectionalIterator, class T>
+template <class BidirectionalIterator, class T>
BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value);
//replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name)
-template<class BidirectionalIterator1, class BidirectionalIterator2>
+template <class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1,
BidirectionalIterator2 first2, BidirectionalIterator2 last2);
@@ -96,45 +96,47 @@ ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const
}
-template<class BidirectionalIterator, class T> inline
+template <class BidirectionalIterator, class T> inline
BidirectionalIterator find_last(const BidirectionalIterator first, BidirectionalIterator last, const T& value)
{
+ //reverse iteration: 1. check 2. decrement 3. evaluate
const BidirectionalIterator iterNotFound = last;
- do
+ for (;;) //VS 2010 doesn't like "while (true)"
{
if (last == first)
return iterNotFound;
--last;
+
+ if (*last == value)
+ return last;
}
- while (!(*last == value)); //loop over "last": 1. check 2. decrement 3. evaluate
- return last;
}
-template<class BidirectionalIterator1, class BidirectionalIterator2> inline
+template <class BidirectionalIterator1, class BidirectionalIterator2> inline
BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
const BidirectionalIterator2 first2, BidirectionalIterator2 last2)
{
- if (first1 == last1 ||
- first2 == last2)
- return last1;
-
- int diff = static_cast<int>(std::distance(first1, last1)) -
- static_cast<int>(std::distance(first2, last2));
-
const BidirectionalIterator1 iterNotFound = last1;
- --last2;
- while (diff-- >= 0) //loop over "last1": 1. check 2. decrement 3. evaluate
+ //reverse iteration: 1. check 2. decrement 3. evaluate
+ for (;;)
{
- --last1;
+ BidirectionalIterator1 it1 = last1;
+ BidirectionalIterator2 it2 = last2;
- BidirectionalIterator1 iter1 = last1;
- for (BidirectionalIterator2 iter2 = last2; *iter1 == *iter2; --iter1, --iter2)
- if (iter2 == first2)
- return iter1;
+ for (;;)
+ {
+ if (it2 == first2) return it1;
+ if (it1 == first1) return iterNotFound;
+
+ --it1;
+ --it2;
+
+ if (*it1 != *it2) break;
+ }
+ --last1;
}
- return iterNotFound;
}
}
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 370a0c56..dfbbba6d 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -66,7 +66,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError
try //reading certain symlinks/junctions requires admin rights! This shall not cause an error in user mode!
{
- Privileges::getInstance().ensureActive(SE_BACKUP_NAME); //throw FileError
+ activatePrivilege(SE_BACKUP_NAME); //throw FileError
}
catch (...) {}
diff --git a/zen/time.h b/zen/time.h
index f08f6ef8..6124d4d1 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -175,7 +175,10 @@ String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormat
{
typedef typename GetCharType<String>::Result CharType;
- const struct std::tm& ctc = toClibTimeComponents(comp);
+ struct std::tm ctc = toClibTimeComponents(comp);
+ std::mktime (&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday
+ //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent
+
CharType buffer[256];
const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc);
return String(buffer, charsWritten);
bgstack15