diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:24:59 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:24:59 +0200 |
commit | a1c91f4695e208d5a8f80dc37b1818169b7829ff (patch) | |
tree | 52f5134376d17c99b6c9e53133a2eb5cf171377c /zen | |
parent | 5.16 (diff) | |
download | FreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.tar.gz FreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.tar.bz2 FreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.zip |
5.17
Diffstat (limited to 'zen')
48 files changed, 887 insertions, 875 deletions
diff --git a/zen/FindFilePlus/FindFilePlus.vcxproj b/zen/FindFilePlus/FindFilePlus.vcxproj index b94174ac..d7dfe219 100644 --- a/zen/FindFilePlus/FindFilePlus.vcxproj +++ b/zen/FindFilePlus/FindFilePlus.vcxproj @@ -66,10 +66,8 @@ <_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> diff --git a/zen/IFileOperation/FileOperation_Vista.vcxproj b/zen/IFileOperation/FileOperation_Vista.vcxproj index 4694a3a5..e5165514 100644 --- a/zen/IFileOperation/FileOperation_Vista.vcxproj +++ b/zen/IFileOperation/FileOperation_Vista.vcxproj @@ -67,10 +67,8 @@ <_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> @@ -88,7 +86,7 @@ </BuildLog> <ClCompile> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> @@ -100,6 +98,7 @@ <DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings> <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> <SmallerTypeCheck>true</SmallerTypeCheck> + <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -124,7 +123,7 @@ </Midl> <ClCompile> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> @@ -136,6 +135,7 @@ <DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings> <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> <SmallerTypeCheck>true</SmallerTypeCheck> + <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -158,7 +158,7 @@ <ClCompile> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> @@ -169,6 +169,7 @@ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings> <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> + <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> @@ -196,7 +197,7 @@ <ClCompile> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;FFS_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> @@ -207,6 +208,7 @@ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <DisableSpecificWarnings>4100;4996;4512</DisableSpecificWarnings> <AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories> + <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles> </ClCompile> <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp index 8816b502..218c6f99 100644 --- a/zen/IFileOperation/file_op.cpp +++ b/zen/IFileOperation/file_op.cpp @@ -31,13 +31,7 @@ using namespace zen; namespace { -struct Win32Error -{ - Win32Error(DWORD errorCode) : errorCode_(errorCode) {} - DWORD errorCode_; -}; - -std::vector<std::wstring> getLockingProcesses(const wchar_t* filename); //throw Win32Error +std::vector<std::wstring> getLockingProcesses(const wchar_t* filename); //throw SysError class RecyclerProgressCallback : public IFileOperationProgressSink @@ -167,13 +161,13 @@ private: }; -void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError +void moveToRecycleBin(const wchar_t* fileNames[], //throw SysError size_t fileCount, fileop::RecyclerCallback callback, void* sink) { ComPtr<IFileOperation> fileOp; - ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw ComError + ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw SysError nullptr, CLSCTX_ALL, IID_PPV_ARGS(fileOp.init()))); @@ -196,7 +190,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError ComPtr<RecyclerProgressCallback> opProgress; *opProgress.init() = new (std::nothrow) RecyclerProgressCallback(callback, sink); if (!opProgress) - throw ComError(L"Error creating RecyclerProgressCallback.", E_OUTOFMEMORY); + throw SysError(formatComError(L"Error creating RecyclerProgressCallback.", E_OUTOFMEMORY)); DWORD callbackID = 0; ZEN_COM_CHECK(fileOp->Advise(opProgress.get(), &callbackID)); @@ -214,10 +208,10 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError { continueExecution = callback(fileNames[i], sink); //should not throw! } - catch (...) { throw ComError(L"Unexpected exception in callback.", E_UNEXPECTED); } + catch (...) { throw SysError(formatComError(L"Unexpected exception in callback.", E_UNEXPECTED)); } if (!continueExecution) - throw ComError(L"Operation cancelled.", HRESULT_FROM_WIN32(ERROR_CANCELLED)); + throw SysError(formatComError(L"Operation cancelled.", HRESULT_FROM_WIN32(ERROR_CANCELLED))); } //create file/folder item object @@ -235,7 +229,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError if (!somethingExists(fileNames[i])) continue; } - throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + fileNames[i] + L"\'.", hr); + throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + fileNames[i] + L"\'.", hr)); } ZEN_COM_CHECK(fileOp->DeleteItem(psiFile.get(), nullptr)); @@ -251,24 +245,22 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError { ZEN_COM_CHECK(fileOp->PerformOperations()); } - catch (const ComError&) + catch (const SysError&) { //first let's check if we have more detailed error information available if (const std::pair<std::wstring, HRESULT>* lastError = opProgress->getLastError()) { - try //create an even better error message if we detect a locking issue: + std::vector<std::wstring> processes; //create an even better error message if we detect a locking issue: + try { processes = getLockingProcesses(lastError->first.c_str()); /*throw SysError*/ } + catch (const SysError&) {} + + if (!processes.empty()) { - std::vector<std::wstring> processes = getLockingProcesses(lastError->first.c_str()); //throw Win32Error - if (!processes.empty()) - { - std::wstring msg = L"The file \'" + lastError->first + L"\' is locked by another process:"; - std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { msg += L'\n'; msg += proc; }); - throw ComError(msg); //message is already descriptive enough, no need to add the HRESULT code - } + std::wstring errorMsg = L"The file \'" + lastError->first + L"\' is locked by another process:"; + std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { errorMsg += L'\n'; errorMsg += proc; }); + throw SysError(errorMsg); //message is descriptive enough, no need to evaluate HRESULT! } - catch (const Win32Error&) {} - - throw ComError(std::wstring(L"Error during \"PerformOperations\" for file:\n") + L"\'" + lastError->first + L"\'.", lastError->second); + throw SysError(formatComError(std::wstring(L"Error during \"PerformOperations\" for file:\n") + L"\'" + lastError->first + L"\'.", lastError->second)); } throw; } @@ -278,15 +270,15 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted)); if (pfAnyOperationsAborted == TRUE) - throw ComError(L"Operation did not complete successfully."); + throw SysError(L"Operation did not complete successfully."); } -void copyFile(const wchar_t* sourceFile, //throw ComError +void copyFile(const wchar_t* sourceFile, //throw SysError const wchar_t* targetFile) { ComPtr<IFileOperation> fileOp; - ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw ComError + ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw SysError nullptr, CLSCTX_ALL, IID_PPV_ARGS(fileOp.init()))); @@ -295,7 +287,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError // from being shown to the user during the // operation. This includes error, confirmation // and progress dialogs. - ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw ComError + ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw SysError FOF_SILENT | FOFX_EARLYFAILURE | FOF_NOERRORUI)); @@ -306,12 +298,12 @@ void copyFile(const wchar_t* sourceFile, //throw ComError nullptr, IID_PPV_ARGS(psiSourceFile.init())); if (FAILED(hr)) - throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + sourceFile + L"\'.", hr); + throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + sourceFile + L"\'.", hr)); } const size_t pos = std::wstring(targetFile).find_last_of(L'\\'); if (pos == std::wstring::npos) - throw ComError(L"Target filename does not contain a path separator."); + throw SysError(L"Target filename does not contain a path separator."); const std::wstring targetFolder(targetFile, pos); const std::wstring targetFileNameShort = targetFile + pos + 1; @@ -323,7 +315,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError nullptr, IID_PPV_ARGS(psiTargetFolder.init())); if (FAILED(hr)) - throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for folder:\n") + L"\'" + targetFolder + L"\'.", hr); + throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for folder:\n") + L"\'" + targetFolder + L"\'.", hr)); } //schedule file copy operation @@ -337,14 +329,14 @@ void copyFile(const wchar_t* sourceFile, //throw ComError ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted)); if (pfAnyOperationsAborted == TRUE) - throw ComError(L"Operation did not complete successfully."); + throw SysError(L"Operation did not complete successfully."); } -void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw ComError +void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw SysError { ComPtr<IShellFolder> desktopFolder; - ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw ComError + ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError PIDLIST_RELATIVE pidlFolder = nullptr; ZEN_COM_CHECK(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, @@ -358,53 +350,64 @@ void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw ComError ComPtr<IPersist> persistFolder; ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl, nullptr, // [in] IBindCtx *pbc, - IID_PPV_ARGS(persistFolder.init()))); //throw ComError + IID_PPV_ARGS(persistFolder.init()))); //throw SysError - ZEN_COM_CHECK(persistFolder->GetClassID(&pathCLSID)); //throw ComError + ZEN_COM_CHECK(persistFolder->GetClassID(&pathCLSID)); //throw SysError } -std::vector<std::wstring> getLockingProcesses(const wchar_t* filename) //throw Win32Error +std::vector<std::wstring> getLockingProcesses(const wchar_t* filename) //throw SysError { - wchar_t sessionKey[CCH_RM_SESSION_KEY + 1] = {}; //fixes two bugs: http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx DWORD sessionHandle = 0; - DWORD rv1 = ::RmStartSession(&sessionHandle, //__out DWORD *pSessionHandle, - 0, //__reserved DWORD dwSessionFlags, - sessionKey); //__out WCHAR strSessionKey[ ] - if (rv1 != ERROR_SUCCESS) - throw Win32Error(rv1); + { + wchar_t sessionKey[CCH_RM_SESSION_KEY + 1] = {}; //fixes two bugs: http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx + DWORD rv1 = ::RmStartSession(&sessionHandle, //__out DWORD *pSessionHandle, + 0, //__reserved DWORD dwSessionFlags, + sessionKey); //__out WCHAR strSessionKey[ ] + if (rv1 != ERROR_SUCCESS) + throw SysError(formatSystemError(L"RmStartSession", rv1)); + } ZEN_ON_SCOPE_EXIT(::RmEndSession(sessionHandle)); - DWORD rv2 = ::RmRegisterResources(sessionHandle, //__in DWORD dwSessionHandle, - 1, //__in UINT nFiles, - &filename, //__in_opt LPCWSTR rgsFilenames[ ], - 0, //__in UINT nApplications, - nullptr, //__in_opt RM_UNIQUE_PROCESS rgApplications[ ], - 0, //__in UINT nServices, - nullptr); //__in_opt LPCWSTR rgsServiceNames[ ] - if (rv2 != ERROR_SUCCESS) - throw Win32Error(rv2); - - UINT procInfoSize = 0; - UINT procInfoSizeNeeded = 0; - DWORD rebootReasons = 0; - ::RmGetList(sessionHandle, &procInfoSizeNeeded, &procInfoSize, nullptr, &rebootReasons); //get procInfoSizeNeeded - //fails with "access denied" for C:\pagefile.sys! - - if (procInfoSizeNeeded == 0) - return std::vector<std::wstring>(); - - procInfoSize = procInfoSizeNeeded; - std::vector<RM_PROCESS_INFO> procInfo(procInfoSize); - - DWORD rv3 = ::RmGetList(sessionHandle, //__in DWORD dwSessionHandle, - &procInfoSizeNeeded, //__out UINT *pnProcInfoNeeded, - &procInfoSize, //__inout UINT *pnProcInfo, - &procInfo[0], //__inout_opt RM_PROCESS_INFO rgAffectedApps[ ], - &rebootReasons); //__out LPDWORD lpdwRebootReasons - if (rv3 != ERROR_SUCCESS) - throw Win32Error(rv3); - procInfo.resize(procInfoSize); + { + DWORD rv2 = ::RmRegisterResources(sessionHandle, //__in DWORD dwSessionHandle, + 1, //__in UINT nFiles, + &filename, //__in_opt LPCWSTR rgsFilenames[ ], + 0, //__in UINT nApplications, + nullptr, //__in_opt RM_UNIQUE_PROCESS rgApplications[ ], + 0, //__in UINT nServices, + nullptr); //__in_opt LPCWSTR rgsServiceNames[ ] + if (rv2 != ERROR_SUCCESS) + throw SysError(formatSystemError(L"RmRegisterResources", rv2)); + } + + std::vector<RM_PROCESS_INFO> procInfo; + { + UINT procInfoSize = 0; + UINT procInfoSizeNeeded = 0; + DWORD rebootReasons = 0; + DWORD rv3 = ::RmGetList(sessionHandle, &procInfoSizeNeeded, &procInfoSize, nullptr, &rebootReasons); //get procInfoSizeNeeded + if (rv3 == ERROR_SUCCESS) + return std::vector<std::wstring>(); + if (rv3 != ERROR_MORE_DATA) + throw SysError(formatSystemError(L"RmGetList", rv3)); + //C:\pagefile.sys fails with ERROR_SHARING_VIOLATION! + + if (procInfoSizeNeeded == 0) + return std::vector<std::wstring>(); + + procInfoSize = procInfoSizeNeeded; + procInfo.resize(procInfoSizeNeeded); + + rv3 = ::RmGetList(sessionHandle, //__in DWORD dwSessionHandle, + &procInfoSizeNeeded, //__out UINT *pnProcInfoNeeded, + &procInfoSize, //__inout UINT *pnProcInfo, + &procInfo[0], //__inout_opt RM_PROCESS_INFO rgAffectedApps[ ], + &rebootReasons); //__out LPDWORD lpdwRebootReasons + if (rv3 != ERROR_SUCCESS) + throw SysError(formatSystemError(L"RmGetList", rv3)); + procInfo.resize(procInfoSize); + } std::vector<std::wstring> output; for (auto iter = procInfo.begin(); iter != procInfo.end(); ++iter) @@ -437,7 +440,7 @@ std::vector<std::wstring> getLockingProcesses(const wchar_t* filename) //throw W &buffer[0], //__out LPTSTR lpExeName, &bufferSize)) //__inout PDWORD lpdwSize if (bufferSize < buffer.size()) - processName += std::wstring(L" - ") + L"\'" + &buffer[0] + L"\'"; + processName += std::wstring(L", ") + L"\'" + &buffer[0] + L"\'"; } } output.push_back(processName); @@ -457,10 +460,10 @@ bool fileop::moveToRecycleBin(const wchar_t* fileNames[], { try { - ::moveToRecycleBin(fileNames, fileCount, callback, sink); //throw ComError + ::moveToRecycleBin(fileNames, fileCount, callback, sink); //throw SysError return true; } - catch (const ComError& e) + catch (const SysError& e) { lastErrorMessage.reset(new std::wstring(e.toString())); return false; @@ -473,10 +476,10 @@ bool fileop::copyFile(const wchar_t* sourceFile, { try { - ::copyFile(sourceFile, targetFile); //throw ComError + ::copyFile(sourceFile, targetFile); //throw SysError return true; } - catch (const ComError& e) + catch (const SysError& e) { lastErrorMessage.reset(new std::wstring(e.toString())); return false; @@ -489,11 +492,11 @@ bool fileop::checkRecycler(const wchar_t* dirname, bool& isRecycler) try { CLSID clsid = {}; - getFolderClsid(dirname, clsid); //throw ComError + getFolderClsid(dirname, clsid); //throw SysError isRecycler = ::IsEqualCLSID(clsid, CLSID_RecycleBin) == TRUE; //silence perf warning return true; } - catch (const ComError& e) + catch (const SysError& e) { lastErrorMessage.reset(new std::wstring(e.toString())); return false; @@ -511,7 +514,7 @@ bool fileop::getLockingProcesses(const wchar_t* filename, const wchar_t*& procLi { try { - std::vector<std::wstring> processes = ::getLockingProcesses(filename); //throw Win32Error + std::vector<std::wstring> processes = ::getLockingProcesses(filename); //throw SysError std::wstring buffer; std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { buffer += proc; buffer += L'\n'; }); @@ -524,9 +527,9 @@ bool fileop::getLockingProcesses(const wchar_t* filename, const wchar_t*& procLi return true; } - catch (const Win32Error& e) + catch (const SysError& e) { - lastErrorMessage.reset(new std::wstring(formatWin32Msg(e.errorCode_))); + lastErrorMessage.reset(new std::wstring(e.toString())); return false; } } diff --git a/zen/assert_static.h b/zen/assert_static.h index c5b7f8c1..469e0299 100644 --- a/zen/assert_static.h +++ b/zen/assert_static.h @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** diff --git a/zen/base64.h b/zen/base64.h index 5f18ea36..ae896e70 100644 --- a/zen/base64.h +++ b/zen/base64.h @@ -1,8 +1,9 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * 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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** + #ifndef BASE64_HEADER_08473021856321840873021487213453214 #define BASE64_HEADER_08473021856321840873021487213453214 diff --git a/zen/basic_math.h b/zen/basic_math.h index 89d9d6c0..f01421c7 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -1,6 +1,6 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * 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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** diff --git a/zen/com_error.h b/zen/com_error.h index 0e0448a7..5eb7611d 100644 --- a/zen/com_error.h +++ b/zen/com_error.h @@ -4,31 +4,19 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef COM_ERROR_HEADER -#define COM_ERROR_HEADER +#ifndef COM_ERROR_HEADER_88425703425254 +#define COM_ERROR_HEADER_88425703425254 -#include <string> #include <cstdio> -#include "win.h" //includes "windows.h" +#include "sys_error.h" namespace zen { -std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr); -std::wstring formatWin32Msg(DWORD dwMessageId); //return empty string on error - -class ComError -{ -public: - explicit ComError(const std::wstring& msg, HRESULT hr = S_OK) : msg_(hr == S_OK ? msg : generateErrorMsg(msg, hr)) {} - const std::wstring& toString() const { return msg_; } - -private: - std::wstring msg_; -}; +std::wstring formatComError(const std::wstring& msg, HRESULT hr); //Convenience Macros checking for COM errors: -#define ZEN_COM_CHECK(func) ZEN_COM_CHECK_IMPL(func, #func) //throw ComError +#define ZEN_COM_CHECK(func) ZEN_COM_CHECK_IMPL(func, #func) //throw SysError /* Example: ZEN_COM_CHECK(backupComp->InitializeForBackup()); @@ -36,17 +24,17 @@ Equivalent to: { HRESULT hrInternal = backupComp->InitializeForBackup(); if (FAILED(hrInternal)) - throw ComError(L"Error calling \"backupComp->InitializeForBackup()\".", hrInternal); + throw SysError(formatComError(L"Error calling \"backupComp->InitializeForBackup()\".", hrInternal)); } */ -#define ZEN_COM_ASSERT(obj) ZEN_COM_ASSERT_IMPL(obj, #obj) //throw ComError +#define ZEN_COM_ASSERT(obj) ZEN_COM_ASSERT_IMPL(obj, #obj) //throw SysError /* Example: ZEN_COM_ASSERT(obj); Equivalent to: if (!obj) - throw ComError(L"Assertion failed: \"obj\".", E_FAIL); + throw SysError(formatComError(L"Assertion failed: \"obj\".", E_FAIL)); */ @@ -54,32 +42,35 @@ Equivalent to: - - - - - - - //################# implementation ##################### -std::wstring formatWin32Msg(DWORD dwMessageId) //return empty string on error +namespace impl +{ +inline +std::wstring formatWin32Message(DWORD dwMessageId) //return empty string on error { - std::wstring output; LPWSTR buffer = nullptr; if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_IGNORE_INSERTS | //important: without this flag ::FormatMessage() will fail if message contains placeholders FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, dwMessageId, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0) - { if (buffer) //just to be sure { - output = buffer; - ::LocalFree(buffer); + ZEN_ON_SCOPE_EXIT(::LocalFree(buffer)); + return buffer; } - } - return output; + return std::wstring(); +} + + +inline +std::wstring numberToHexString(long number) +{ + wchar_t buffer[100] = {}; + const int charsWritten = ::swprintf(buffer, 100, L"0x%08x", static_cast<int>(number)); + return charsWritten > 0 ? std::wstring(buffer, charsWritten) : std::wstring(); } + namespace { std::wstring formatFacility(HRESULT hr) @@ -199,28 +190,24 @@ std::wstring formatFacility(HRESULT hr) } } } - -inline -std::wstring numberToHexString(long number) -{ - wchar_t result[100]; - ::swprintf(result, 100, L"0x%08x", static_cast<int>(number)); - return std::wstring(result); } +std::wstring formatComError(const std::wstring& msg, long long hr); //not implemented! intentional overload ambiguity to catch usage errors with HRESULT! + + inline -std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr) +std::wstring formatComError(const std::wstring& msg, HRESULT hr) { - std::wstring output(input); + std::wstring output(msg); output += L"\n"; //don't use _com_error(hr).ErrorMessage(): internally this is nothing more than a call to ::FormatMessage() - std::wstring win32Msg = formatWin32Msg(hr); + std::wstring win32Msg = impl::formatWin32Message(hr); if (!win32Msg.empty()) //empty string on error - output += win32Msg + L"\n" + L"HRESULT: " + numberToHexString(hr); + output += win32Msg + L"\n" + L"HRESULT: " + impl::numberToHexString(hr); else - output += L"HRESULT: " + numberToHexString(hr) + L", " + L"Facility: " + formatFacility(hr); + output += L"HRESULT: " + impl::numberToHexString(hr) + L", " + L"Facility: " + impl::formatFacility(hr); //don't bluntly interpret as Win32 error code HRESULT_CODE(hr), too often misleading! //http://blogs.msdn.com/b/oldnewthing/archive/2006/11/03/942851.aspx @@ -232,9 +219,9 @@ std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr) { \ HRESULT hrInternal = func; \ if (FAILED(hrInternal)) \ - throw zen::ComError(std::wstring(L"Error calling \"") + L ## txt + L"\".", hrInternal); \ + throw zen::SysError(formatComError(std::wstring(L"Error calling \"") + L ## txt + L"\".", hrInternal)); \ } -#define ZEN_COM_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::ComError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".", E_FAIL); +#define ZEN_COM_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(formatComError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".", E_FAIL)); } -#endif //COM_ERROR_HEADER +#endif //COM_ERROR_HEADER_88425703425254 diff --git a/zen/com_ptr.h b/zen/com_ptr.h index 9944ea56..6fb9f2cd 100644 --- a/zen/com_ptr.h +++ b/zen/com_ptr.h @@ -36,7 +36,7 @@ class ComPtr public: ComPtr() : ptr(nullptr) {} // ComPtr(const ComPtr& other) : ptr(other.ptr) { if (ptr) ptr->AddRef(); } //noexcept in C++11 - ComPtr( ComPtr&& other) : ptr(other.release()) {} // + ComPtr( ComPtr&& tmp ) : ptr(tmp.release()) {} // ~ComPtr() { if (ptr) ptr->Release(); } //has exception spec of compiler-generated destructor by default ComPtr& operator=(const ComPtr& other) { ComPtr(other).swap(*this); return *this; } //noexcept in C++11 diff --git a/zen/debug_log.h b/zen/debug_log.h index 6ee44ff5..ad27b66e 100644 --- a/zen/debug_log.h +++ b/zen/debug_log.h @@ -23,9 +23,9 @@ namespace zen { -#ifdef FFS_WIN +#ifdef ZEN_WIN const char ZEN_FILE_NAME_SEPARATOR = '\\'; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC const char ZEN_FILE_NAME_SEPARATOR = '/'; #endif diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 6402dce7..a800fb80 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -10,24 +10,24 @@ #include "thread.h" //includes <boost/thread.hpp> #include "scope_guard.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "notify_removal.h" #include "win.h" //includes "windows.h" #include "long_path_prefix.h" -#elif defined FFS_LINUX +#elif defined ZEN_LINUX #include <sys/inotify.h> #include <fcntl.h> #include "file_traverser.h" -#elif defined FFS_MAC +#elif defined ZEN_MAC //#include <CoreFoundation/FSEvents.h> #endif using namespace zen; -#ifdef FFS_WIN +#ifdef ZEN_WIN namespace { class SharedData @@ -101,14 +101,14 @@ public: boost::lock_guard<boost::mutex> dummy(lockAccess); //first check whether errors occurred in thread - if (!errorMsg.first.empty()) + if (errorInfo) { - const std::wstring msg = errorMsg.first.c_str(); - const DWORD lastError = errorMsg.second; + const std::wstring msg = copyStringTo<std::wstring>(errorInfo->msg); + const std::wstring descr = copyStringTo<std::wstring>(errorInfo->descr); - if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(msg); - throw FileError(msg); + if (errorCodeForNotExisting(errorInfo->errorCode)) + throw ErrorNotExisting(msg, descr); + throw FileError(msg, descr); } output.swap(changedFiles); @@ -117,10 +117,12 @@ public: //context of worker thread - void reportError(const std::wstring& msg, DWORD errorCode) //throw() + void reportError(const std::wstring& msg, const std::wstring& description, DWORD errorCode) //throw() { boost::lock_guard<boost::mutex> dummy(lockAccess); - errorMsg = std::make_pair(copyStringTo<BasicWString>(msg), errorCode); + + ErrorInfo newInfo = { copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode }; + errorInfo = make_unique<ErrorInfo>(newInfo); } private: @@ -128,7 +130,14 @@ private: boost::mutex lockAccess; std::vector<DirWatcher::Entry> changedFiles; - std::pair<BasicWString, DWORD> errorMsg; //non-empty if errors occurred in thread + + struct ErrorInfo + { + BasicWString msg; + BasicWString descr; + DWORD errorCode; + }; + std::unique_ptr<ErrorInfo> errorInfo; //non-empty if errors occurred in thread }; @@ -151,11 +160,13 @@ public: nullptr); if (hDir == INVALID_HANDLE_VALUE) { - const DWORD lastError = ::GetLastError(); - const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError); + const DWORD lastError = ::GetLastError(); //copy before making other system calls! + const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)); + const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); + if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(errorMsg); - throw FileError(errorMsg); + throw ErrorNotExisting(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); } //end of constructor, no need to start managing "hDir" @@ -184,7 +195,10 @@ public: false, //__in BOOL bInitialState, nullptr); //__in_opt LPCTSTR lpName if (overlapped.hEvent == nullptr) - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)) + L" (CreateEvent)" L"\n\n" + getLastErrorFormatted(), ::GetLastError()); + { + const DWORD lastError = ::GetLastError(); //copy before making other system calls! + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"CreateEvent", lastError), lastError); + } ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent)); @@ -202,7 +216,10 @@ public: &bytesReturned, // __out_opt LPDWORD lpBytesReturned, &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped, nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)) + L" (ReadDirectoryChangesW)" L"\n\n" + getLastErrorFormatted(), ::GetLastError()); + { + const DWORD lastError = ::GetLastError(); //copy before making other system calls! + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"ReadDirectoryChangesW", lastError), lastError); + } //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"! zen::ScopeGuard guardAio = zen::makeGuard([&] @@ -223,8 +240,9 @@ public: &bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred, false)) //__in BOOL bWait { - if (::GetLastError() != ERROR_IO_INCOMPLETE) - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)) + L" (GetOverlappedResult)" L"\n\n" + getLastErrorFormatted(), ::GetLastError()); + const DWORD lastError = ::GetLastError(); //copy before making other system calls! + if (lastError != ERROR_IO_INCOMPLETE) + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"GetOverlappedResult", lastError), lastError); //execute asynchronous procedure calls (APC) queued on this thread ::SleepEx(50, // __in DWORD dwMilliseconds, @@ -362,7 +380,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() } -#elif defined FFS_LINUX +#elif defined ZEN_LINUX struct DirWatcher::Pimpl { Zstring dirname; @@ -376,21 +394,19 @@ namespace class DirsOnlyTraverser : public zen::TraverseCallback { public: - DirsOnlyTraverser(std::vector<Zstring>& dirs, - const std::shared_ptr<TraverseCallback>& otherMe) : otherMe_(otherMe), dirs_(dirs) {} + DirsOnlyTraverser(std::vector<Zstring>& dirs) : dirs_(dirs) {} virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) {} virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } - virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) + virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) { dirs_.push_back(fullName); - return otherMe_; + return this; } virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); } virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); } private: - const std::shared_ptr<TraverseCallback>& otherMe_; //lifetime management, two options: 1. use std::weak_ptr 2. ref to shared_ptr std::vector<Zstring>& dirs_; }; } @@ -408,16 +424,14 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError std::vector<Zstring> fullDirList; fullDirList.push_back(dirname); - std::shared_ptr<TraverseCallback> traverser; - traverser = std::make_shared<DirsOnlyTraverser>(fullDirList, traverser); //throw FileError - - zen::traverseFolder(dirname, *traverser); //don't traverse into symlinks (analog to windows build) + DirsOnlyTraverser traverser(fullDirList); //throw FileError + zen::traverseFolder(dirname, traverser); //don't traverse into symlinks (analog to windows build) //init pimpl_->dirname = directory; pimpl_->notifDescr = ::inotify_init(); if (pimpl_->notifDescr == -1) - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"inotify_init", getLastError())); zen::ScopeGuard guardDescr = zen::makeGuard([&] { ::close(pimpl_->notifDescr); }); @@ -429,7 +443,7 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1; } if (!initSuccess) - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"fcntl", getLastError())); //add watches std::for_each(fullDirList.begin(), fullDirList.end(), @@ -448,10 +462,13 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError IN_MOVE_SELF); if (wd == -1) { - const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)) + L"\n\n" + getLastErrorFormatted(); - if (errorCodeForNotExisting(errno)) - throw ErrorNotExisting(errorMsg); - throw FileError(errorMsg); + const ErrorCode lastError = getLastError(); //copy before making other system calls! + const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)); + const std::wstring errorDescr = formatSystemError(L"inotify_add_watch", lastError); + + if (errorCodeForNotExisting(lastError)) + throw ErrorNotExisting(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); } pimpl_->watchDescrs.insert(std::make_pair(wd, appendSeparator(subdir))); @@ -484,7 +501,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found return std::vector<Entry>(); - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(pimpl_->dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(pimpl_->dirname)), formatSystemError(L"read", getLastError())); } std::vector<Entry> output; @@ -522,18 +539,17 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() return output; } -#elif defined FFS_MAC +#elif defined ZEN_MAC warn_static("finish") struct DirWatcher::Pimpl { - }; DirWatcher::DirWatcher(const Zstring& directory) //throw FileError { - + throw FileError(L"Dir Watcher is not yet implemented!"); } @@ -8,12 +8,12 @@ #define DLLLOADER_H_INCLUDED #include <memory> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <string> #include "scope_guard.h" #include "win.h" //includes "windows.h" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <dlfcn.h> #endif @@ -40,11 +40,11 @@ class DllFun public: DllFun() : fun(nullptr) {} -#ifdef FFS_WIN +#ifdef ZEN_WIN DllFun(const wchar_t* libraryName, const char* functionName) : hLibRef(::LoadLibrary(libraryName), ::FreeLibrary), fun(hLibRef ? reinterpret_cast<Func>(::GetProcAddress(static_cast<HMODULE>(hLibRef.get()), functionName)) : nullptr) {} -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC DllFun(const char* libraryName, const char* functionName) : hLibRef(::dlopen(libraryName, RTLD_LAZY), ::dlclose), fun(hLibRef ? reinterpret_cast<Func>(::dlsym(hLibRef.get(), functionName)) : nullptr) {} @@ -57,7 +57,7 @@ private: }; -#ifdef FFS_WIN +#ifdef ZEN_WIN //if the dll is already part of the process space, e.g. "kernel32.dll" or "shell32.dll", we can use a faster variant: //NOTE: since the lifetime of the referenced library is *not* controlled, this is safe to use only for permanently loaded libraries like these! template <class Func> @@ -100,7 +100,7 @@ std::string getResourceStream(const std::wstring& libraryName, size_t resourceId //--------------- implementation--------------------------------------------------- -#ifdef FFS_WIN +#ifdef ZEN_WIN inline std::string getResourceStream(const wchar_t* libraryName, size_t resourceId) { diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp index a70ef13b..887b9b63 100644 --- a/zen/dst_hack.cpp +++ b/zen/dst_hack.cpp @@ -166,10 +166,10 @@ FILETIME utcToLocal(const FILETIME& utcTime) //throw std::runtime_error &utcTime, //__in const FILETIME *lpFileTime, &localTime)) //__out LPFILETIME lpLocalFileTime { - const std::wstring errorMessage = _("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" + - L"High: " + numberTo<std::wstring>(utcTime.dwHighDateTime) + L" " + - L"Low: " + numberTo<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); - throw std::runtime_error(utfCvrtTo<std::string>(errorMessage)); + const std::wstring errorMsg = _("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" + + L"High: " + numberTo<std::wstring>(utcTime.dwHighDateTime) + L" " + + L"Low: " + numberTo<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"FileTimeToLocalFileTime", getLastError()); + throw std::runtime_error(utfCvrtTo<std::string>(errorMsg)); } return localTime; } @@ -184,10 +184,10 @@ FILETIME localToUtc(const FILETIME& localTime) //throw std::runtime_error &localTime, //__in const FILETIME *lpLocalFileTime, &utcTime)) //__out LPFILETIME lpFileTime { - const std::wstring errorMessage = _("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" + - L"High: " + numberTo<std::wstring>(localTime.dwHighDateTime) + L" " + - L"Low: " + numberTo<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); - throw std::runtime_error(utfCvrtTo<std::string>(errorMessage)); + const std::wstring errorMsg = _("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" + + L"High: " + numberTo<std::wstring>(localTime.dwHighDateTime) + L" " + + L"Low: " + numberTo<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"LocalFileTimeToFileTime", getLastError()); + throw std::runtime_error(utfCvrtTo<std::string>(errorMsg)); } return utcTime; } @@ -286,9 +286,8 @@ std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift() if (std::bitset < UTC_LOCAL_OFFSET_BITS - 1 > (absValue).to_ulong() != static_cast<unsigned long>(absValue) || //time shifts that big shouldn't be possible! timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity! { - const std::wstring errorMessage = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + - L"(" + numberTo<std::wstring>(timeShiftSec) + L") " + L"\n\n" + getLastErrorFormatted(); - throw std::runtime_error(utfCvrtTo<std::string>(errorMessage)); + const std::wstring errorMsg = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + L"(" + numberTo<std::wstring>(timeShiftSec) + L")"; + throw std::runtime_error(utfCvrtTo<std::string>(errorMsg)); } std::bitset<UTC_LOCAL_OFFSET_BITS> output(absValue); diff --git a/zen/error_log.h b/zen/error_log.h index 490bb1f4..05049d27 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -105,8 +105,8 @@ String formatMessageImpl(const LogEntry& entry) //internal linkage String formattedText = L"[" + formatTime<String>(FORMAT_TIME, localTime(entry.time)) + L"] " + copyStringTo<String>(getTypeName()) + L": "; const size_t prefixLen = formattedText.size(); - for (auto iter = entry.message.begin(); iter != entry.message.end(); ) - if (*iter == L'\n') + for (auto it = entry.message.begin(); it != entry.message.end(); ) + if (*it == L'\n') { formattedText += L'\n'; @@ -116,12 +116,12 @@ String formatMessageImpl(const LogEntry& entry) //internal linkage do //skip duplicate newlines { - ++iter; + ++it; } - while (iter != entry.message.end() && *iter == L'\n'); + while (it != entry.message.end() && *it == L'\n'); } else - formattedText += *iter++; + formattedText += *it++; return formattedText; } diff --git a/zen/file_error.h b/zen/file_error.h index f107a736..71619834 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -4,29 +4,31 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEERROR_H_INCLUDED -#define FILEERROR_H_INCLUDED +#ifndef FILEERROR_H_INCLUDED_839567308565656789 +#define FILEERROR_H_INCLUDED_839567308565656789 #include <string> #include "zstring.h" #include "utf.h" -#include "last_error.h" //we'll need this later anyway! +#include "sys_error.h" //we'll need this later anyway! namespace zen { -class FileError //Exception base class used to notify file/directory copy/delete errors +//A high-level exception class giving detailed context information for end users +class FileError { public: - explicit FileError(const std::wstring& message) : errorMessage(message) {} + explicit FileError(const std::wstring& msg) : msg_(msg) {} + FileError(const std::wstring& msg, const std::wstring& details) : msg_(msg + L"\n\n" + details) {} virtual ~FileError() {} - const std::wstring& toString() const { return errorMessage; } + const std::wstring& toString() const { return msg_; } private: - std::wstring errorMessage; + std::wstring msg_; }; -#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& message) : FileError(message) {} }; +#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} }; DEFINE_NEW_FILE_ERROR(ErrorNotExisting); DEFINE_NEW_FILE_ERROR(ErrorTargetExisting); @@ -35,7 +37,6 @@ DEFINE_NEW_FILE_ERROR(ErrorFileLocked); DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); - //----------- facilitate usage of std::wstring for error messages -------------------- //allow implicit UTF8 conversion: since std::wstring models a GUI string, convenience is more important than performance @@ -56,4 +57,4 @@ std::wstring fmtFileName(const Zstring& filename) } } -#endif // FILEERROR_H_INCLUDED +#endif //FILEERROR_H_INCLUDED_839567308565656789 diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index 8dc3e72d..398e88e8 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -16,7 +16,7 @@ #include "file_id_def.h" #include <boost/thread/tss.hpp> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <Aclapi.h> #include "privilege.h" #include "dll.h" @@ -26,19 +26,19 @@ #include "win_ver.h" #include "IFileOperation/file_op.h" -#elif defined FFS_LINUX +#elif defined ZEN_LINUX #include <sys/vfs.h> //statfs #include <fcntl.h> //AT_SYMLINK_NOFOLLOW, UTIME_OMIT #ifdef HAVE_SELINUX #include <selinux/selinux.h> #endif -#elif defined FFS_MAC +#elif defined ZEN_MAC #include <sys/mount.h> //statfs //#include <utime.h> #endif -#if defined FFS_LINUX || defined FFS_MAC +#if defined ZEN_LINUX || defined ZEN_MAC #include <sys/stat.h> #include <sys/time.h> //lutimes #endif @@ -49,11 +49,11 @@ using namespace zen; bool zen::fileExists(const Zstring& filename) { //symbolic links (broken or not) are also treated as existing files! -#ifdef FFS_WIN +#ifdef ZEN_WIN const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str()); return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; return ::lstat(filename.c_str(), &fileInfo) == 0 && (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)); //in Linux a symbolic link is neither file nor directory @@ -64,11 +64,11 @@ bool zen::fileExists(const Zstring& filename) bool zen::dirExists(const Zstring& dirname) { //symbolic links (broken or not) are also treated as existing directories! -#ifdef FFS_WIN +#ifdef ZEN_WIN const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str()); return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat dirInfo = {}; return ::lstat(dirname.c_str(), &dirInfo) == 0 && (S_ISLNK(dirInfo.st_mode) || S_ISDIR(dirInfo.st_mode)); //in Linux a symbolic link is neither file nor directory @@ -78,7 +78,7 @@ bool zen::dirExists(const Zstring& dirname) bool zen::symlinkExists(const Zstring& linkname) { -#ifdef FFS_WIN +#ifdef ZEN_WIN WIN32_FIND_DATA fileInfo = {}; { const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &fileInfo); @@ -88,7 +88,7 @@ bool zen::symlinkExists(const Zstring& linkname) } return isSymlink(fileInfo); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; return ::lstat(linkname.c_str(), &fileInfo) == 0 && S_ISLNK(fileInfo.st_mode); //symbolic link @@ -98,11 +98,11 @@ bool zen::symlinkExists(const Zstring& linkname) bool zen::somethingExists(const Zstring& objname) { -#ifdef FFS_WIN +#ifdef ZEN_WIN const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str()); return attr != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; return ::lstat(objname.c_str(), &fileInfo) == 0; #endif @@ -112,13 +112,13 @@ bool zen::somethingExists(const Zstring& objname) SymLinkType zen::getSymlinkType(const Zstring& linkname) //throw() { assert(symlinkExists(linkname)); -#ifdef FFS_WIN +#ifdef ZEN_WIN const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(linkname).c_str()); if (attr == INVALID_FILE_ATTRIBUTES) return SYMLINK_TYPE_UNKNOWN; return (attr & FILE_ATTRIBUTE_DIRECTORY) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC //S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link struct ::stat fileInfo = {}; if (::stat(linkname.c_str(), &fileInfo) != 0) @@ -130,14 +130,39 @@ SymLinkType zen::getSymlinkType(const Zstring& linkname) //throw() namespace { +#ifdef ZEN_WIN +//(try to) enhance error messages by showing which processes lock the file +Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred +{ + if (vistaOrLater()) + { + using namespace fileop; + const DllFun<FunType_getLockingProcesses> getLockingProcesses(getDllName(), funName_getLockingProcesses); + const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString); + + if (getLockingProcesses && freeString) + { + const wchar_t* procList = nullptr; + if (getLockingProcesses(filename.c_str(), procList)) + { + ZEN_ON_SCOPE_EXIT(freeString(procList)); + return procList; + } + } + } + return Zstring(); +} +#endif + + void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl) //throw FileError { -#ifdef FFS_WIN +#ifdef ZEN_WIN WIN32_FIND_DATA fileInfo = {}; { const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); if (searchHandle == INVALID_HANDLE_VALUE) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"FindFirstFile", getLastError())); ::FindClose(searchHandle); } // WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; @@ -173,25 +198,25 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory nullptr); if (hFile == INVALID_HANDLE_VALUE) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"CreateFile", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"GetFileInformationByHandle", getLastError())); attr.fileSize = UInt64(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); attr.modificationTime = toTimeT(fileInfoHnd.ftLastWriteTime); } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; const int rv = procSl == SYMLINK_FOLLOW ? :: stat(filename.c_str(), &fileInfo) : ::lstat(filename.c_str(), &fileInfo); if (rv != 0) //follow symbolic links - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"stat", getLastError())); attr.fileSize = UInt64(fileInfo.st_size); attr.modificationTime = fileInfo.st_mtime; @@ -218,119 +243,34 @@ Int64 zen::getFileTime(const Zstring& filename, ProcSymlink procSl) //throw File UInt64 zen::getFreeDiskSpace(const Zstring& path) //throw FileError { -#ifdef FFS_WIN +#ifdef ZEN_WIN ULARGE_INTEGER bytesFree = {}; if (!::GetDiskFreeSpaceEx(appendSeparator(path).c_str(), //__in_opt LPCTSTR lpDirectoryName, -> "UNC name [...] must include a trailing backslash, for example, "\\MyServer\MyShare\" &bytesFree, //__out_opt PULARGE_INTEGER lpFreeBytesAvailable, nullptr, //__out_opt PULARGE_INTEGER lpTotalNumberOfBytes, nullptr)) //__out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), formatSystemError(L"GetDiskFreeSpaceEx", getLastError())); return UInt64(bytesFree.LowPart, bytesFree.HighPart); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct statfs info = {}; if (::statfs(path.c_str(), &info) != 0) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), formatSystemError(L"statfs", getLastError())); return UInt64(info.f_bsize) * info.f_bavail; #endif } -namespace -{ -#ifdef FFS_WIN -//(try to) enhance error messages by showing which processes lock the file -Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred -{ - if (vistaOrLater()) - { - using namespace fileop; - const DllFun<FunType_getLockingProcesses> getLockingProcesses(getDllName(), funName_getLockingProcesses); - const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString); - - if (getLockingProcesses && freeString) - { - const wchar_t* procList = nullptr; - if (getLockingProcesses(filename.c_str(), procList)) - { - ZEN_ON_SCOPE_EXIT(freeString(procList)); - return procList; - } - } - } - return Zstring(); -} - - -DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error! -{ - //note: this even works for network shares: \\share\dirname - - const DWORD bufferSize = 10000; - std::vector<wchar_t> buffer(bufferSize); - - //full pathName need not yet exist! - if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - return 0; - - DWORD volumeSerial = 0; - if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName, - nullptr, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize - return 0; - - return volumeSerial; -} - -#elif defined FFS_LINUX || defined FFS_MAC -dev_t retrieveVolumeSerial(const Zstring& pathName) //return 0 on error! -{ - Zstring volumePathName = pathName; - - //remove trailing slash - if (volumePathName.size() > 1 && endsWith(volumePathName, FILE_NAME_SEPARATOR)) //exception: allow '/' - volumePathName = beforeLast(volumePathName, FILE_NAME_SEPARATOR); - - struct stat fileInfo = {}; - while (::lstat(volumePathName.c_str(), &fileInfo) != 0) //go up in folder hierarchy until existing folder is found - { - volumePathName = beforeLast(volumePathName, FILE_NAME_SEPARATOR); //returns empty string if ch not found - if (volumePathName.empty()) - return 0; //this includes path "/" also! - } - - return fileInfo.st_dev; -} -#endif -} - - -zen::ResponseSame zen::onSameVolume(const Zstring& folderLeft, const Zstring& folderRight) //throw() -{ - const auto serialLeft = retrieveVolumeSerial(folderLeft); //returns 0 on error! - const auto serialRight = retrieveVolumeSerial(folderRight); //returns 0 on error! - if (serialLeft == 0 || serialRight == 0) - return IS_SAME_CANT_SAY; - - return serialLeft == serialRight ? IS_SAME_YES : IS_SAME_NO; -} - - bool zen::removeFile(const Zstring& filename) //throw FileError { -#ifdef FFS_WIN +#ifdef ZEN_WIN + const wchar_t functionName[] = L"DeleteFile"; const Zstring& filenameFmt = applyLongPathPrefix(filename); if (!::DeleteFile(filenameFmt.c_str())) -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"unlink"; if (::unlink(filename.c_str()) != 0) #endif { @@ -338,7 +278,7 @@ bool zen::removeFile(const Zstring& filename) //throw FileError if (errorCodeForNotExisting(lastError)) //no error situation if file is not existing! manual deletion relies on it! return false; -#ifdef FFS_WIN +#ifdef ZEN_WIN if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only { ::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes @@ -353,18 +293,19 @@ bool zen::removeFile(const Zstring& filename) //throw FileError return false; //neither file nor any other object (e.g. broken symlink) with that name existing //begin of "regular" error reporting - const std::wstring shortMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filename)); + const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filename)); + std::wstring errorDescr = formatSystemError(functionName, lastError); -#ifdef FFS_WIN +#ifdef ZEN_WIN if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! lastError == ERROR_LOCK_VIOLATION) { const Zstring procList = getLockingProcessNames(filename); //throw() if (!procList.empty()) - throw FileError(shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList); + errorDescr = _("The file is locked by another process:") + L"\n" + procList; } #endif - throw FileError(shortMsg + L"\n\n" + getLastErrorFormatted(lastError)); + throw FileError(errorMsg, errorDescr); } return true; } @@ -382,7 +323,7 @@ namespace //wrapper for file system rename function: void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting { -#ifdef FFS_WIN +#ifdef ZEN_WIN const Zstring oldNameFmt = applyLongPathPrefix(oldName); const Zstring newNameFmt = applyLongPathPrefix(newName); @@ -392,16 +333,6 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File { DWORD lastError = ::GetLastError(); - const std::wstring shortMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); - - if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! - lastError == ERROR_LOCK_VIOLATION) - { - const Zstring procList = getLockingProcessNames(oldName); //throw() - if (!procList.empty()) - throw FileError(shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList); - } - if (lastError == ERROR_ACCESS_DENIED) //MoveFileEx may fail to rename a read-only file on a SAMBA-share -> (try to) handle this { const DWORD oldAttr = ::GetFileAttributes(oldNameFmt.c_str()); @@ -421,7 +352,6 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File else { lastError = ::GetLastError(); //use error code from second call to ::MoveFileEx() - //cleanup: (try to) restore file attributes: assume oldName is still existing ::SetFileAttributes(oldNameFmt.c_str(), oldAttr); } @@ -429,37 +359,45 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File } } - std::wstring errorMessage = shortMsg + L"\n\n" + getLastErrorFormatted(lastError); + const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); + std::wstring errorDescr = formatSystemError(L"MoveFileEx", lastError); - if (lastError == ERROR_NOT_SAME_DEVICE) - throw ErrorDifferentVolume(errorMessage); + if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! + lastError == ERROR_LOCK_VIOLATION) + { + const Zstring procList = getLockingProcessNames(oldName); //throw() + if (!procList.empty()) + errorDescr = _("The file is locked by another process:") + L"\n" + procList; + } + if (lastError == ERROR_NOT_SAME_DEVICE) + throw ErrorDifferentVolume(errorMsg, errorDescr); else if (lastError == ERROR_ALREADY_EXISTS || //-> used on Win7 x64 lastError == ERROR_FILE_EXISTS) //-> used by XP??? - throw ErrorTargetExisting(errorMessage); + throw ErrorTargetExisting(errorMsg, errorDescr); else - throw FileError(errorMessage); + throw FileError(errorMsg, errorDescr); } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC if (::rename(oldName.c_str(), newName.c_str()) != 0) { - const int lastError = errno; - std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)) + - L"\n\n" + getLastErrorFormatted(lastError); + const int lastError = errno; //copy before making other system calls! + std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); + const std::wstring errorDescr = formatSystemError(L"rename", lastError); if (lastError == EXDEV) - throw ErrorDifferentVolume(errorMessage); + throw ErrorDifferentVolume(errorMsg, errorDescr); else if (lastError == EEXIST) - throw ErrorTargetExisting(errorMessage); + throw ErrorTargetExisting(errorMsg, errorDescr); else - throw FileError(errorMessage); + throw FileError(errorMsg, errorDescr); } #endif } -#ifdef FFS_WIN +#ifdef ZEN_WIN /*small wrapper around ::GetShortPathName() ::GetLongPathName() */ @@ -474,10 +412,10 @@ Zstring getFilenameFmt(const Zstring& filename, Function fun) //throw(); returns std::vector<wchar_t> buffer(bufferSize); - const DWORD rv = fun(filenameFmt.c_str(), //__in LPCTSTR lpszShortPath, - &buffer[0], //__out LPTSTR lpszLongPath, - bufferSize); //__in DWORD cchBuffer - if (rv == 0 || rv >= bufferSize) + const DWORD charsWritten = fun(filenameFmt.c_str(), //__in LPCTSTR lpszShortPath, + &buffer[0], //__out LPTSTR lpszLongPath, + bufferSize); //__in DWORD cchBuffer + if (charsWritten == 0 || charsWritten >= bufferSize) return Zstring(); return &buffer[0]; @@ -572,7 +510,7 @@ void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw Fil } catch (const ErrorTargetExisting&) { -#ifdef FFS_WIN +#ifdef ZEN_WIN //try to handle issues with already existing short 8.3 file names on Windows if (have8dot3NameClash(newName)) { @@ -615,7 +553,7 @@ public: } return LINK_SKIP; } - virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) + virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) { dirs_.push_back(fullName); return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively! @@ -636,7 +574,7 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback) { assert(somethingExists(directory)); //[!] -#ifdef FFS_WIN +#ifdef ZEN_WIN const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix //(try to) normalize file attributes: actually NEEDED for symbolic links also! @@ -647,12 +585,14 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback) if (symlinkExists(directory)) //remove symlink directly { if (callback) callback->onBeforeDirDeletion(directory); //once per symlink -#ifdef FFS_WIN +#ifdef ZEN_WIN + const wchar_t functionName[] = L"RemoveDirectory"; if (!::RemoveDirectory(directoryFmt.c_str())) -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"unlink"; if (::unlink(directory.c_str()) != 0) #endif - throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), formatSystemError(functionName, getLastError())); } else { @@ -681,12 +621,14 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback) //parent directory is deleted last if (callback) callback->onBeforeDirDeletion(directory); //and once per folder -#ifdef FFS_WIN +#ifdef ZEN_WIN + const wchar_t functionName[] = L"RemoveDirectory"; if (!::RemoveDirectory(directoryFmt.c_str())) -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"rmdir"; if (::rmdir(directory.c_str()) != 0) #endif - throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), formatSystemError(functionName, getLastError())); //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have //successfully been *marked* for deletion, but some application still has a handle open! //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 @@ -708,7 +650,7 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink procSl) //throw FileError { -#ifdef FFS_WIN +#ifdef ZEN_WIN FILETIME creationTime = {}; FILETIME lastWriteTime = tofiletime(modTime); @@ -751,18 +693,18 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), attribs); ); - auto removeReadonly = [&]() -> bool //may need to remove the readonly-attribute (e.g. on FAT usb drives) + auto removeReadonly = [&]() -> bool //throw FileError; may need to remove the readonly-attribute (e.g. on FAT usb drives) { if (attribs == INVALID_FILE_ATTRIBUTES) { const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str()); if (tmpAttr == INVALID_FILE_ATTRIBUTES) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"GetFileAttributes", getLastError())); if (tmpAttr & FILE_ATTRIBUTE_READONLY) { if (!::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_NORMAL)) - throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"SetFileAttributes", getLastError())); attribs = tmpAttr; //reapplied on scope exit return true; @@ -799,19 +741,20 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink if (hFile == INVALID_HANDLE_VALUE) { if (::GetLastError() == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons) - if (removeReadonly()) + if (removeReadonly()) //throw FileError continue; //2. be a *little* fancy hFile = openFile(false); if (hFile == INVALID_HANDLE_VALUE) { - if (::GetLastError() == ERROR_ACCESS_DENIED) - if (removeReadonly()) + const ErrorCode lastError = getLastError(); //copy before making other system calls! + if (lastError == ERROR_ACCESS_DENIED) + if (removeReadonly()) //throw FileError continue; //3. after these herculean stunts we give up... - throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"CreateFile", lastError)); } } break; @@ -826,15 +769,15 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink nullptr, //__in_opt const FILETIME *lpLastAccessTime, &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime { - auto lastErr = ::GetLastError(); + ErrorCode lastError = getLastError(); //copy before making other system calls! //function may fail if file is read-only: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 - if (lastErr == ERROR_ACCESS_DENIED) + if (lastError == ERROR_ACCESS_DENIED) { //dynamically load windows API function: available with Windows Vista and later typedef BOOL (WINAPI* SetFileInformationByHandleFunc)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); - const SysDllFun<SetFileInformationByHandleFunc> setFileInformationByHandle(L"kernel32.dll", "SetFileInformationByHandle"); + if (setFileInformationByHandle) //if not: let the original error propagate! { auto setFileInfo = [&](FILE_BASIC_INFO basicInfo) //throw FileError; no const& since SetFileInformationByHandle() requires non-const parameter! @@ -843,7 +786,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, &basicInfo, //__in LPVOID lpFileInformation, sizeof(basicInfo))) //__in DWORD dwBufferSize - throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"SetFileInformationByHandle", getLastError())); }; auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER @@ -876,15 +819,16 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink } catch (FileError&) {} - lastErr = ERROR_SUCCESS; + lastError = ERROR_SUCCESS; } } } - std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastErr); + std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)); + const std::wstring errorDescr = formatSystemError(L"SetFileTime", lastError); //add more meaningful message: FAT accepts only a subset of the NTFS date range - if (lastErr == ERROR_INVALID_PARAMETER && + if (lastError == ERROR_INVALID_PARAMETER && dst::isFatDrive(filename)) { //we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!! @@ -929,8 +873,8 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink (!isNullTime(creationTime) ? L"\n\tcreate (UTC): \t" + fmtDate(creationTime) : L""); } - if (lastErr != ERROR_SUCCESS) - throw FileError(errorMsg); + if (lastError != ERROR_SUCCESS) + throw FileError(errorMsg, errorDescr); } } #ifndef NDEBUG //dst hack: verify data written @@ -959,7 +903,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink } #endif -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC //sigh, we can't use utimensat on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit??? // struct ::timespec newTimes[2] = {}; @@ -967,7 +911,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink // newTimes[1].tv_sec = to<time_t>(modTime); //modification time (seconds) // // if (::utimensat(AT_FDCWD, filename.c_str(), newTimes, procSl == SYMLINK_DIRECT ? AT_SYMLINK_NOFOLLOW : 0) != 0) - // throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + // throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"utimensat", getLastError())); //=> fallback to "retarded-idiot version"! -- DarkByte @@ -979,20 +923,20 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink :: utimes(filename.c_str(), newTimes) : //utimensat() not yet implemented on OS X ::lutimes(filename.c_str(), newTimes); if (rv != 0) - throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"utimes", getLastError())); #endif } bool zen::supportsPermissions(const Zstring& dirname) //throw FileError { -#ifdef FFS_WIN +#ifdef ZEN_WIN const DWORD bufferSize = MAX_PATH + 1; std::vector<wchar_t> buffer(bufferSize); if (!::GetVolumePathName(dirname.c_str(), //__in LPCTSTR lpszFileName, &buffer[0], //__out LPTSTR lpszVolumePathName, bufferSize)) //__in DWORD cchBufferLength - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"GetVolumePathName", getLastError())); DWORD fsFlags = 0; if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName, @@ -1003,11 +947,11 @@ bool zen::supportsPermissions(const Zstring& dirname) //throw FileError &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, nullptr, //__out LPTSTR lpFileSystemNameBuffer, 0)) //__in DWORD nFileSystemNameSize - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"GetVolumeInformation", getLastError())); return (fsFlags & FILE_PERSISTENT_ACLS) != 0; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC return true; #endif } @@ -1029,7 +973,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem return; - throw FileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtFileName(source)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtFileName(source)), formatSystemError(L"getfilecon", getLastError())); } ZEN_ON_SCOPE_EXIT(::freecon(contextSource)); @@ -1057,7 +1001,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli ::setfilecon(target.c_str(), contextSource) : ::lsetfilecon(target.c_str(), contextSource); if (rv3 < 0) - throw FileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtFileName(target)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtFileName(target)), formatSystemError(L"setfilecon", getLastError())); } #endif //HAVE_SELINUX @@ -1065,7 +1009,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli //copy permissions for files, directories or symbolic links: requires admin rights void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError { -#ifdef FFS_WIN +#ifdef ZEN_WIN //in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags //CAVEAT: if a file system does not support ACLs, GetFileSecurity() will return successfully with a *valid* security descriptor containing *no* ACL entries! @@ -1089,7 +1033,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym } catch (const FileError& e)//add some more context description (e.g. user is not an admin) { - throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)) + L"\n\n" + e.toString()); + throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), e.toString()); } @@ -1108,7 +1052,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym if (bytesNeeded > buffer.size()) buffer.resize(bytesNeeded); else - throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), formatSystemError(L"GetFileSecurity", getLastError())); } SECURITY_DESCRIPTOR& secDescr = reinterpret_cast<SECURITY_DESCRIPTOR&>(buffer[0]); @@ -1132,7 +1076,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInformation, &secDescr)) //__in PSECURITY_DESCRIPTOR pSecurityDescriptor - throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetResolved)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetResolved)), formatSystemError(L"SetFileSecurity", getLastError())); /* PSECURITY_DESCRIPTOR buffer = nullptr; @@ -1214,7 +1158,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym throw FileError */ -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #ifdef HAVE_SELINUX //copy SELinux security context copySecurityContext(source, target, procSl); //throw FileError @@ -1224,20 +1168,24 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym if (procSl == SYMLINK_FOLLOW) { if (::stat(source.c_str(), &fileInfo) != 0) - throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), formatSystemError(L"stat", getLastError())); + + if (::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"chown", getLastError())); - if (::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights! - ::chmod(target.c_str(), fileInfo.st_mode) != 0) - throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)) + L"\n\n" + getLastErrorFormatted()); + if (::chmod(target.c_str(), fileInfo.st_mode) != 0) + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"chmod", getLastError())); } else { if (::lstat(source.c_str(), &fileInfo) != 0) - throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), formatSystemError(L"lstat", getLastError())); - if (::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights! - (!symlinkExists(target) && ::chmod(target.c_str(), fileInfo.st_mode) != 0)) //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod() - throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)) + L"\n\n" + getLastErrorFormatted()); + if (::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"lchown", getLastError())); + + if (!symlinkExists(target) && ::chmod(target.c_str(), fileInfo.st_mode) != 0) //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod() + throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), formatSystemError(L"chmod", getLastError())); } #endif } @@ -1315,7 +1263,7 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT const Zstring& templateDir, bool copyFilePermissions) { -#ifdef FFS_WIN +#ifdef ZEN_WIN //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! Zstring dirTmp = removeLongPathPrefix(endsWith(directory, FILE_NAME_SEPARATOR) ? beforeLast(directory, FILE_NAME_SEPARATOR) : @@ -1327,10 +1275,12 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT const ErrorCode lastError = dirExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; - const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp)) + L"\n\n" + getLastErrorFormatted(lastError); + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp)); + const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); + if (lastError == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(msg); - throw FileError(msg); //[!] this is NOT a ErrorTargetPathMissing case! + throw ErrorTargetExisting(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); //[!] this is NOT a ErrorTargetPathMissing case! } //don't use ::CreateDirectoryEx: @@ -1357,32 +1307,35 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT if (lastError != ERROR_SUCCESS) { - const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError); + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); + const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); + if (lastError == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(msg); + throw ErrorTargetExisting(errorMsg, errorDescr); else if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(msg); - throw FileError(msg); + throw ErrorTargetPathMissing(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); } } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC if (::mkdir(directory.c_str(), 0755) != 0) //mode: drwxr-xr-x { - const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(); const ErrorCode lastError = getLastError(); + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); + const std::wstring errorDescr = formatSystemError(L"mkdir", lastError); if (lastError == EEXIST) - throw ErrorTargetExisting(msg); + throw ErrorTargetExisting(errorMsg, errorDescr); else if (lastError == ENOENT) - throw ErrorTargetPathMissing(msg); - throw FileError(msg); + throw ErrorTargetPathMissing(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); } #endif if (!templateDir.empty()) { -#ifdef FFS_WIN +#ifdef ZEN_WIN //try to copy file attributes (dereference symlinks and junctions) const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), 0, @@ -1451,7 +1404,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool { const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks -#ifdef FFS_WIN +#ifdef ZEN_WIN const bool isDirLink = [&]() -> bool { const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(sourceLink).c_str()); @@ -1463,22 +1416,24 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); if (!createSymbolicLink) - throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\"")); + throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\"")); + const wchar_t functionName[] = L"CreateSymbolicLinkW"; if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required... linkPath.c_str(), //__in LPTSTR lpTargetFileName, (isDirLink ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"symlink"; if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) #endif - throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), formatSystemError(functionName, getLastError())); //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist - zen::ScopeGuard guardNewDir = zen::makeGuard([&] + zen::ScopeGuard guardNewLink = zen::makeGuard([&] { try { -#ifdef FFS_WIN +#ifdef ZEN_WIN if (isDirLink) removeDirectory(targetLink); else @@ -1497,13 +1452,13 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool if (copyFilePermissions) copyObjectPermissions(sourceLink, targetLink, SYMLINK_DIRECT); //throw FileError - guardNewDir.dismiss(); //target has been created successfully! + guardNewLink.dismiss(); //target has been created successfully! } namespace { -#ifdef FFS_WIN +#ifdef ZEN_WIN /* CopyFileEx() BackupRead() FileRead() -------------------------------------------- @@ -1626,24 +1581,27 @@ void copyFileWindowsSparse(const Zstring& sourceFile, { const DWORD lastError = ::GetLastError(); - const std::wstring shortMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)); + const std::wstring errorMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)); + std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); //if file is locked throw "ErrorFileLocked" instead! if (lastError == ERROR_SHARING_VIOLATION || lastError == ERROR_LOCK_VIOLATION) { const Zstring procList = getLockingProcessNames(sourceFile); //throw() - throw ErrorFileLocked(shortMsg + L"\n\n" + (!procList.empty() ? _("The file is locked by another process:") + L"\n" + procList : getLastErrorFormatted(lastError))); + if (!procList.empty()) + errorDescr = _("The file is locked by another process:") + L"\n" + procList; + throw ErrorFileLocked(errorMsg, errorDescr); } - throw FileError(shortMsg + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)"); + throw FileError(errorMsg, errorDescr); } ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileSource)); //---------------------------------------------------------------------- BY_HANDLE_FILE_INFORMATION fileInfoSource = {}; if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource)) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), formatSystemError(L"GetFileInformationByHandle", getLastError())); //---------------------------------------------------------------------- const DWORD validAttribs = FILE_ATTRIBUTE_NORMAL | //"This attribute is valid only if used alone." @@ -1665,17 +1623,18 @@ void copyFileWindowsSparse(const Zstring& sourceFile, nullptr); if (hFileTarget == INVALID_HANDLE_VALUE) { - const DWORD lastError = ::GetLastError(); - const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)"; + const ErrorCode lastError = getLastError(); //copy before making other system calls! + const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)); + const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); if (lastError == ERROR_FILE_EXISTS || //confirmed to be used lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 - throw ErrorTargetExisting(errorMessage); + throw ErrorTargetExisting(errorMsg, errorDescr); if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMessage); + throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMessage); + throw FileError(errorMsg, errorDescr); } ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: guard just after opening target and before managing hFileOut ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget)); @@ -1683,7 +1642,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile, //---------------------------------------------------------------------- BY_HANDLE_FILE_INFORMATION fileInfoTarget = {}; if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget)) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"GetFileInformationByHandle", getLastError())); //return up-to-date file attributes if (newAttrib) @@ -1732,8 +1691,8 @@ void copyFileWindowsSparse(const Zstring& sourceFile, 0, //OutBufferSize &bytesReturned, //number of bytes returned nullptr)) //OVERLAPPED structure - throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)) + - L"\n\n" + zen::getLastErrorFormatted() + L" (NTFS sparse)"); + throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)), + formatSystemError(L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError())); } //---------------------------------------------------------------------- @@ -1760,10 +1719,10 @@ void copyFileWindowsSparse(const Zstring& sourceFile, false, //__in BOOL bAbort, false, //__in BOOL bProcessSecurity, &contextRead)) //__out LPVOID *lpContext - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted()); //better use fine-granular error messages "reading/writing"! + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), formatSystemError(L"BackupRead", getLastError())); //better use fine-granular error messages "reading/writing"! if (bytesRead > BUFFER_SIZE) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + L"(buffer overflow)"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"buffer overflow"); //user should never see this if (bytesRead < BUFFER_SIZE) eof = true; @@ -1776,10 +1735,10 @@ void copyFileWindowsSparse(const Zstring& sourceFile, false, //__in BOOL bAbort, false, //__in BOOL bProcessSecurity, &contextWrite)) //__out LPVOID *lpContext - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"BackupWrite", getLastError())); if (bytesWritten != bytesRead) - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + L"(incomplete write)"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"incomplete write"); //user should never see this //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)! @@ -1797,14 +1756,14 @@ void copyFileWindowsSparse(const Zstring& sourceFile, //::BackupRead() silently fails reading encrypted files -> double check! if (!someBytesWritten && UInt64(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh) != 0U) //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)! - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + L"(unknown error)"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()" //time needs to be set at the end: BackupWrite() changes modification time if (!::SetFileTime(hFileTarget, &fileInfoSource.ftCreationTime, nullptr, &fileInfoSource.ftLastWriteTime)) - throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"SetFileTime", getLastError())); guardTarget.dismiss(); @@ -1854,7 +1813,7 @@ public: void reportUserException(CallbackCopyFile& userCallback) { exceptionInUserCallback = &userCallback; } - void reportError(const std::wstring& message) { errorMsg = message; } + void reportError(const std::wstring& msg, const std::wstring& description) { errorMsg = std::make_pair(msg, description); } //call context: copyFileWindowsDefault() void evaluateErrors() //throw X @@ -1865,14 +1824,14 @@ public: if (exceptionInUserCallback) exceptionInUserCallback->updateCopyStatus(0); //rethrow (hopefully!) - if (!errorMsg.empty()) - throw FileError(errorMsg); + if (!errorMsg.first.empty()) + throw FileError(errorMsg.first, errorMsg.second); } private: - bool shouldCopyAsSparse; // - std::wstring errorMsg; //these are exclusive! - CallbackCopyFile* exceptionInUserCallback; // + bool shouldCopyAsSparse; // + std::pair<std::wstring, std::wstring> errorMsg; //these are exclusive! + CallbackCopyFile* exceptionInUserCallback; // }; @@ -1935,14 +1894,14 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, BY_HANDLE_FILE_INFORMATION fileInfoSrc = {}; if (!::GetFileInformationByHandle(hSourceFile, &fileInfoSrc)) { - cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.sourceFile_)) + L"\n\n" + getLastErrorFormatted()); + cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.sourceFile_)), formatSystemError(L"GetFileInformationByHandle", getLastError())); return PROGRESS_CANCEL; } BY_HANDLE_FILE_INFORMATION fileInfoTrg = {}; if (!::GetFileInformationByHandle(hDestinationFile, &fileInfoTrg)) { - cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.targetFile_)) + L"\n\n" + getLastErrorFormatted()); + cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.targetFile_)), formatSystemError(L"GetFileInformationByHandle", getLastError())); return PROGRESS_CANCEL; } @@ -2055,16 +2014,17 @@ void copyFileWindowsDefault(const Zstring& sourceFile, throw ErrorShouldCopyAsSparse(L"sparse dummy value2"); //assemble error message... - std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)) + - L"\n\n" + getLastErrorFormatted(lastError); + const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)); + std::wstring errorDescr = formatSystemError(L"CopyFileEx", lastError); //if file is locked throw "ErrorFileLocked" instead! if (lastError == ERROR_SHARING_VIOLATION || lastError == ERROR_LOCK_VIOLATION) { const Zstring procList = getLockingProcessNames(sourceFile); //throw() -> enhance error message! - throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + - (!procList.empty() ? _("The file is locked by another process:") + L"\n" + procList : getLastErrorFormatted(lastError))); + if (!procList.empty()) + errorDescr = _("The file is locked by another process:") + L"\n" + procList; + throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), errorDescr); } //if target is existing this functions is expected to throw ErrorTargetExisting!!! @@ -2072,13 +2032,13 @@ void copyFileWindowsDefault(const Zstring& sourceFile, lastError == ERROR_ALREADY_EXISTS) //not sure if used -> better be safe than sorry!!! { guardTarget.dismiss(); //don't delete file that existed previously! - throw ErrorTargetExisting(errorMessage); + throw ErrorTargetExisting(errorMsg, errorDescr); } if (lastError == ERROR_PATH_NOT_FOUND) { guardTarget.dismiss(); //not relevant - throw ErrorTargetPathMissing(errorMessage); + throw ErrorTargetPathMissing(errorMsg, errorDescr); } try //add more meaningful message @@ -2087,13 +2047,13 @@ void copyFileWindowsDefault(const Zstring& sourceFile, if (lastError == ERROR_INVALID_PARAMETER && dst::isFatDrive(targetFile) && getFilesize(sourceFile) >= 4U * UInt64(1024U * 1024 * 1024)) //throw FileError - errorMessage += L"\nFAT volume cannot store files larger than 4 gigabyte!"; + errorDescr += L"\nFAT volume cannot store files larger than 4 gigabyte!"; //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filename is of a restricted type. } catch (...) {} - throw FileError(errorMessage); + throw FileError(errorMsg, errorDescr); } if (newAttrib) @@ -2152,7 +2112,7 @@ void copyFileWindows(const Zstring& sourceFile, const Zstring& targetFile, Callb } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC void copyFileLinuxMac(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, @@ -2163,7 +2123,7 @@ void copyFileLinuxMac(const Zstring& sourceFile, struct ::stat sourceInfo = {}; if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), formatSystemError(L"fstat", getLastError())); zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: place guard before lifetime of FileOutput try @@ -2190,7 +2150,7 @@ void copyFileLinuxMac(const Zstring& sourceFile, //read and return file statistics struct ::stat targetInfo = {}; if (::fstat(fileOut.getDescriptor(), &targetInfo) != 0) - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), formatSystemError(L"fstat", getLastError())); if (newAttrib) { @@ -2249,10 +2209,10 @@ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::Backu inline void copyFileSelectOs(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, FileAttrib* sourceAttr) { -#ifdef FFS_WIN +#ifdef ZEN_WIN copyFileWindows(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC copyFileLinuxMac(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting #endif } diff --git a/zen/file_handling.h b/zen/file_handling.h index c18a68ac..f677358b 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -4,13 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILE_HANDLING_H_INCLUDED -#define FILE_HANDLING_H_INCLUDED +#ifndef FILE_HANDLING_H_8017341345614857 +#define FILE_HANDLING_H_8017341345614857 #include "zstring.h" #include "file_error.h" -#include "int64.h" #include "file_id_def.h" +#include "int64.h" namespace zen { @@ -18,20 +18,10 @@ struct CallbackRemoveDir; struct CallbackCopyFile; -bool fileExists (const Zstring& filename); //throw() check whether file or symlink exists -bool dirExists (const Zstring& dirname); //throw() check whether directory or symlink exists -bool symlinkExists (const Zstring& linkname); //throw() check whether a symbolic link exists -bool somethingExists(const Zstring& objname); //throw() check whether any object with this name exists - -//check whether two folders are located on the same (logical) volume -//left and right directories NEED NOT yet exist! volume prefix is sufficient! path may end with PATH_SEPARATOR -enum ResponseSame -{ - IS_SAME_YES, - IS_SAME_NO, - IS_SAME_CANT_SAY -}; -ResponseSame onSameVolume(const Zstring& folderLeft, const Zstring& folderRight); //throw() +bool fileExists (const Zstring& filename); //noexcept; check whether file *or* (file) symlink exists +bool dirExists (const Zstring& dirname); //noexcept; check whether directory *or* (dir) symlink exists +bool symlinkExists (const Zstring& linkname); //noexcept; check whether a symbolic link exists +bool somethingExists(const Zstring& objname); //noexcept; check whether any object with this name exists enum SymLinkType { @@ -39,7 +29,7 @@ enum SymLinkType SYMLINK_TYPE_FILE, //Windows: may be broken SYMLINK_TYPE_UNKNOWN, //Windows: unable to determine type; Linux: broken Symlink }; -SymLinkType getSymlinkType(const Zstring& linkname); //throw() +SymLinkType getSymlinkType(const Zstring& linkname); //noexcept enum ProcSymlink { @@ -66,7 +56,7 @@ bool supportsPermissions(const Zstring& dirname); //throw FileError, derefernces //if parent directory not existing: create recursively: void makeDirectory(const Zstring& directory, bool failIfExists = false); //throw FileError, ErrorTargetExisting -//fail if already existing or parent not existing: +//fail if already existing or parent directory not existing: //directory should not end with path separator //templateDir may be empty void makeDirectoryPlain(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing @@ -116,4 +106,4 @@ struct CallbackCopyFile }; } -#endif //FILE_HANDLING_H_INCLUDED +#endif //FILE_HANDLING_H_8017341345614857 diff --git a/zen/file_id.cpp b/zen/file_id.cpp index 8c66d1c9..310390da 100644 --- a/zen/file_id.cpp +++ b/zen/file_id.cpp @@ -6,19 +6,19 @@ #include "file_id.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" //includes "windows.h" #include "long_path_prefix.h" #include "scope_guard.h" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <sys/stat.h> #endif zen::FileId zen::getFileID(const Zstring& filename) { -#ifdef FFS_WIN +#ifdef ZEN_WIN //WARNING: CreateFile() is SLOW, while GetFileInformationByHandle() is cheap! http://msdn.microsoft.com/en-us/library/aa363788(VS.85).aspx //privilege SE_BACKUP_NAME doesn't seem to be required here at all @@ -38,7 +38,7 @@ zen::FileId zen::getFileID(const Zstring& filename) return extractFileID(fileInfo); } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; if (::lstat(filename.c_str(), &fileInfo) == 0) return extractFileID(fileInfo); diff --git a/zen/file_id_def.h b/zen/file_id_def.h index 54012b4f..0608aacd 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -10,17 +10,17 @@ #include <utility> #include "assert_static.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" //includes "windows.h" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <sys/stat.h> #endif namespace zen { -#ifdef FFS_WIN +#ifdef ZEN_WIN typedef DWORD DeviceId; typedef ULONGLONG FileIndex; @@ -49,7 +49,7 @@ assert_static(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFi assert_static(sizeof(FileId().second) == sizeof(ULARGE_INTEGER)); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC namespace impl { typedef struct ::stat StatDummy; } //sigh... typedef decltype(impl::StatDummy::st_dev) DeviceId; diff --git a/zen/file_io.cpp b/zen/file_io.cpp index cda48e36..e0f8c12e 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -6,13 +6,13 @@ #include "file_io.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "long_path_prefix.h" #include "IFileOperation/file_op.h" #include "win_ver.h" #include "dll.h" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <fcntl.h> //open, close #include <unistd.h> //read, write #endif @@ -22,7 +22,7 @@ using namespace zen; namespace { -#ifdef FFS_WIN +#ifdef ZEN_WIN //(try to) enhance error messages by showing which processes lock the file Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred { @@ -45,7 +45,7 @@ Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string return Zstring(); } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC //"filename" could be a named pipe which *blocks* forever during "open()"! https://sourceforge.net/p/freefilesync/bugs/221/ void checkForUnsupportedType(const Zstring& filename) //throw FileError { @@ -77,10 +77,11 @@ void checkForUnsupportedType(const Zstring& filename) //throw FileError FileInput::FileInput(FileHandle handle, const Zstring& filename) : FileInputBase(filename), fileHandle(handle) {} -FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting +FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting FileInputBase(filename) { -#ifdef FFS_WIN +#ifdef ZEN_WIN + const wchar_t functionName[] = L"CreateFile"; fileHandle = ::CreateFile(applyLongPathPrefix(filename).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, @@ -113,41 +114,42 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis */ nullptr); if (fileHandle == INVALID_HANDLE_VALUE) -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever! + const wchar_t functionName[] = L"fopen"; fileHandle = ::fopen(filename.c_str(), "r,type=record,noseek"); //utilize UTF-8 filename if (!fileHandle) #endif { - const ErrorCode lastError = getLastError(); - const std::wstring shortMsg = errorCodeForNotExisting(lastError) ? + const ErrorCode lastError = getLastError(); //copy before making other system calls! + const std::wstring errorMsg = errorCodeForNotExisting(lastError) ? replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) : replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)); - std::wstring errorMsg = shortMsg + L"\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorDescr = formatSystemError(functionName, lastError); -#ifdef FFS_WIN +#ifdef ZEN_WIN if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! lastError == ERROR_LOCK_VIOLATION) { const Zstring procList = getLockingProcessNames(filename); //throw() if (!procList.empty()) - errorMsg = shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList; + errorDescr = _("The file is locked by another process:") + L"\n" + procList; } #endif if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(errorMsg); + throw ErrorNotExisting(errorMsg, errorDescr); - throw FileError(errorMsg + L" (open)"); + throw FileError(errorMsg, errorDescr); } } FileInput::~FileInput() { -#ifdef FFS_WIN +#ifdef ZEN_WIN ::CloseHandle(fileHandle); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC ::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash!; fileHandle != nullptr in this context! #endif } @@ -157,34 +159,36 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number { assert(!eof()); if (bytesToRead == 0) return 0; -#ifdef FFS_WIN +#ifdef ZEN_WIN + const wchar_t functionName[] = L"ReadFile"; DWORD bytesRead = 0; - if (!::ReadFile(fileHandle, //__in HANDLE hFile, - buffer, //__out LPVOID lpBuffer, + if (!::ReadFile(fileHandle, //__in HANDLE hFile, + buffer, //__out LPVOID lpBuffer, static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead, - &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, - nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped -#elif defined FFS_LINUX || defined FFS_MAC + &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, + nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"fread"; const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle); if (::ferror(fileHandle) != 0) //checks status of stream, not fread()! #endif - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (ReadFile)"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(functionName, getLastError())); -#ifdef FFS_WIN +#ifdef ZEN_WIN if (bytesRead < bytesToRead) //verify only! setEof(); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC if (::feof(fileHandle) != 0) setEof(); if (bytesRead < bytesToRead) if (!eof()) //pathologic!? - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete read!"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"Incomplete read!"); //user should never see this #endif if (bytesRead > bytesToRead) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this return bytesRead; } @@ -196,7 +200,7 @@ FileOutput::FileOutput(FileHandle handle, const Zstring& filename) : FileOutputB FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting FileOutputBase(filename) { -#ifdef FFS_WIN +#ifdef ZEN_WIN const DWORD dwCreationDisposition = access == FileOutput::ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW; auto getHandle = [&](DWORD dwFlagsAndAttributes) @@ -218,7 +222,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil fileHandle = getHandle(FILE_ATTRIBUTE_NORMAL); if (fileHandle == INVALID_HANDLE_VALUE) { - DWORD lastError = ::GetLastError(); + DWORD lastError = ::GetLastError(); //copy before making other system calls! //CREATE_ALWAYS fails with ERROR_ACCESS_DENIED if the existing file is hidden or "system" http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx if (lastError == ERROR_ACCESS_DENIED && @@ -235,44 +239,46 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil //begin of "regular" error reporting if (fileHandle == INVALID_HANDLE_VALUE) { - const std::wstring shortMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)); - std::wstring errorMsg = shortMsg + L"\n\n" + zen::getLastErrorFormatted(lastError); + const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)); + std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! lastError == ERROR_LOCK_VIOLATION) { const Zstring procList = getLockingProcessNames(filename); //throw() if (!procList.empty()) - errorMsg = shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList; + errorDescr = _("The file is locked by another process:") + L"\n" + procList; } if (lastError == ERROR_FILE_EXISTS || //confirmed to be used lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 - throw ErrorTargetExisting(errorMsg); + throw ErrorTargetExisting(errorMsg, errorDescr); if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg); + throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMsg); + throw FileError(errorMsg, errorDescr); } } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC checkForUnsupportedType(filename); //throw FileError; writing a named pipe would block forever! fileHandle = ::fopen(filename.c_str(), //GNU extension: https://www.securecoding.cert.org/confluence/display/cplusplus/FIO03-CPP.+Do+not+make+assumptions+about+fopen()+and+file+creation access == ACC_OVERWRITE ? "w,type=record,noseek" : "wx,type=record,noseek"); if (!fileHandle) { - const int lastError = errno; - const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + zen::getLastErrorFormatted(lastError); + const ErrorCode lastError = getLastError(); //copy before making other system calls! + const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())); + const std::wstring errorDescr = formatSystemError(L"fopen", lastError); + if (lastError == EEXIST) - throw ErrorTargetExisting(errorMessage); + throw ErrorTargetExisting(errorMsg, errorDescr); if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMessage); + throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMessage); + throw FileError(errorMsg, errorDescr); } #endif } @@ -280,9 +286,9 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil FileOutput::~FileOutput() { -#ifdef FFS_WIN +#ifdef ZEN_WIN ::CloseHandle(fileHandle); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC ::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash! #endif } @@ -290,25 +296,27 @@ FileOutput::~FileOutput() void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError { -#ifdef FFS_WIN +#ifdef ZEN_WIN + const wchar_t functionName[] = L"WriteFile"; DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx if (!::WriteFile(fileHandle, //__in HANDLE hFile, buffer, //__out LPVOID lpBuffer, static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite, &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"fwrite"; const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle); if (::ferror(fileHandle) != 0) //checks status of stream, not fwrite()! #endif - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (WriteFile)"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(functionName, getLastError())); if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes! - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete write!"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"Incomplete write!"); //user should never see this } -#if defined FFS_LINUX || defined FFS_MAC +#if defined ZEN_LINUX || defined ZEN_MAC //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-5.0.tar.gz FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError, ErrorNotExisting @@ -316,14 +324,19 @@ FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBas checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever! fdFile = ::open(filename.c_str(), O_RDONLY); - if (fdFile == -1) + if (fdFile == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle { - const ErrorCode lastError = getLastError(); + const ErrorCode lastError = getLastError(); //copy before making other system calls! + + const std::wstring errorMsg = errorCodeForNotExisting(lastError) ? + replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) : + replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)); + const std::wstring errorDescr = formatSystemError(L"open", lastError); if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError)); + throw ErrorNotExisting(errorMsg, errorDescr); - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)"); + throw FileError(errorMsg, errorDescr); } } @@ -344,11 +357,11 @@ size_t FileInputUnbuffered::read(void* buffer, size_t bytesToRead) //throw FileE while (bytesRead < 0 && errno == EINTR); if (bytesRead < 0) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (read)"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(L"read", getLastError())); else if (bytesRead == 0) //"zero indicates end of file" setEof(); else if (bytesRead > static_cast<ssize_t>(bytesToRead)) //better safe than sorry - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this //if ::read is interrupted (EINTR) right in the middle, it will return successfully with "bytesRead < bytesToRead"! return bytesRead; @@ -363,15 +376,17 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filename, mode_t mode) fdFile = ::open(filename.c_str(), O_CREAT | O_WRONLY | O_EXCL, mode & (S_IRWXU | S_IRWXG | S_IRWXO)); if (fdFile == -1) { - const int lastError = errno; - const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + zen::getLastErrorFormatted(lastError); + const int lastError = errno; //copy before making other system calls! + const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)); + const std::wstring errorDescr = formatSystemError(L"open", lastError); + if (lastError == EEXIST) - throw ErrorTargetExisting(errorMessage); + throw ErrorTargetExisting(errorMsg, errorDescr); if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMessage); + throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMessage); + throw FileError(errorMsg, errorDescr); } } @@ -396,10 +411,10 @@ void FileOutputUnbuffered::write(const void* buffer, size_t bytesToWrite) //thro if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers errno = ENOSPC; - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (write)"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), formatSystemError(L"write", getLastError())); } if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())), L"buffer overflow"); //user should never see this //if ::write is interrupted (EINTR) right in the middle, it will return successfully with "bytesWritten < bytesToWrite"! buffer = static_cast<const char*>(buffer) + bytesWritten; //suppress warning about pointer arithmetics on void* diff --git a/zen/file_io.h b/zen/file_io.h index 407109f7..93267a39 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -10,9 +10,9 @@ #include "file_io_base.h" #include "file_error.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" //includes "windows.h" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <cstdio> #include <sys/stat.h> #endif @@ -20,19 +20,19 @@ namespace zen { -#ifdef FFS_WIN +#ifdef ZEN_WIN static const char LINE_BREAK[] = "\r\n"; -#elif defined FFS_LINUX +#elif defined ZEN_LINUX static const char LINE_BREAK[] = "\n"; -#elif defined FFS_MAC +#elif defined ZEN_MAC static const char LINE_BREAK[] = "\r"; #endif //buffered file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks) -#ifdef FFS_WIN +#ifdef ZEN_WIN typedef HANDLE FileHandle; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC typedef FILE* FileHandle; #endif @@ -64,7 +64,7 @@ private: FileHandle fileHandle; }; -#if defined FFS_LINUX || defined FFS_MAC +#if defined ZEN_LINUX || defined ZEN_MAC class FileInputUnbuffered : public FileInputBase { public: diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 53049c21..a3e8491a 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -5,11 +5,11 @@ // ************************************************************************** #include "file_traverser.h" -#include "last_error.h" +#include "sys_error.h" #include "assert_static.h" #include "symlink_target.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <zen/win_ver.h> #include "long_path_prefix.h" #include "dst_hack.h" @@ -17,11 +17,11 @@ #include "dll.h" #include "FindFilePlus/find_file_plus.h" -#elif defined FFS_MAC +#elif defined ZEN_MAC #include <zen/osx_string.h> #endif -#if defined FFS_LINUX || defined FFS_MAC +#if defined ZEN_LINUX || defined ZEN_MAC #include <sys/stat.h> #include <dirent.h> #endif @@ -76,7 +76,7 @@ bool tryReportingItemError(Command cmd, zen::TraverseCallback& callback, const Z } -#ifdef FFS_WIN +#ifdef ZEN_WIN void getInfoFromFileSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output) //throw FileError { //open handle to target of symbolic link @@ -88,24 +88,25 @@ void getInfoFromFileSymlink(const Zstring& linkName, zen::TraverseCallback::File FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory -> keep it even if we expect to open a file! See comment below nullptr); if (hFile == INVALID_HANDLE_VALUE) - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)"); + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"CreateFile", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); BY_HANDLE_FILE_INFORMATION fileInfo = {}; if (!::GetFileInformationByHandle(hFile, &fileInfo)) - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (GetFileInformationByHandle)"); + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"GetFileInformationByHandle", getLastError())); //a file symlink may incorrectly point to a directory, but both CreateFile() and GetFileInformationByHandle() will succeed and return garbage! - //- if we did not use FILE_FLAG_BACKUP_SEMANTICS above, CreateFile() would error out with an even less helpful ERROR_ACCESS_DENIED! + //- if we did not use FILE_FLAG_BACKUP_SEMANTICS above, CreateFile() would error out with an even less helpful ERROR_ACCESS_DENIED! //- reinterpreting the link as a directory symlink would still fail during traversal, so just show an error here //- OTOH a directory symlink that points to a file fails immediately in ::FindFirstFile() with ERROR_DIRECTORY! -> nothing to do in this case if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted(ERROR_FILE_INVALID) + L" (GetFileInformationByHandle)"); + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"GetFileInformationByHandle", static_cast<DWORD>(ERROR_FILE_INVALID))); //write output output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); output.lastWriteTime = toTimeT(fileInfo.ftLastWriteTime); output.id = extractFileID(fileInfo); //consider detection of moved files: allow for duplicate file ids, renaming affects symlink, not target, ... + //output.symlinkInfo -> not filled here } @@ -191,15 +192,16 @@ struct Win32Traverser //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH if (hnd.searchHandle == INVALID_HANDLE_VALUE) { + const ErrorCode lastError = getLastError(); //copy before making other system calls! hnd.haveData = false; - if (::GetLastError() == ERROR_FILE_NOT_FOUND) + if (lastError == ERROR_FILE_NOT_FOUND) { //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility if (dirExists(dirname)) //yes, a race-condition, still the best we can do return; } - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"FindFirstFile", lastError)); } } @@ -220,10 +222,11 @@ struct Win32Traverser if (!::FindNextFile(hnd.searchHandle, &fileInfo)) { - if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation + const ErrorCode lastError = getLastError(); //copy before making other system calls! + if (lastError == ERROR_NO_MORE_FILES) //not an error situation return false; //else we have a problem... report it: - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"FindNextFile", lastError)); } return true; } @@ -259,7 +262,7 @@ struct FilePlusTraverser { hnd.searchHandle = ::openDir(applyLongPathPrefix(dirname).c_str()); if (hnd.searchHandle == nullptr) - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted() + L" (+)"); + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"openDir", getLastError())); } static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw() @@ -284,7 +287,7 @@ struct FilePlusTraverser } //else we have a problem... report it: - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (+)"); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"readDir", lastError)); } return true; @@ -310,10 +313,8 @@ class DirTraverser { public: DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) : - isFatFileSystem(dst::isFatDrive(baseDirectory)) + needDstHack(dstCallback ? dst::isFatDrive(baseDirectory) : false) { - warn_static("ineffizient, wenn kein dst hack/file ids gebraucht werden???") - try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail) { activatePrivilege(SE_BACKUP_NAME); //throw FileError @@ -326,8 +327,9 @@ public: traverse<Win32Traverser>(baseDirectory, sink, 0); //apply daylight saving time hack AFTER file traversing, to give separate feedback to user - if (dstCallback && isFatFileSystem) - applyDstHack(*dstCallback); + if (needDstHack) + if (dstCallback) //bound if "needDstHack == true" + applyDstHack(*dstCallback); } private: @@ -378,8 +380,11 @@ private: case TraverseCallback::LINK_FOLLOW: if (Trav::isDirectory(findData)) { - if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse<Trav>(fullName, *rv, retrieveVolumeSerial(fullName)); //symlink may link to different volume => redetermine volume serial! + if (TraverseCallback* trav = sink.onDir(shortName, fullName)) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse<Trav>(fullName, *trav, retrieveVolumeSerial(fullName)); //symlink may link to different volume => redetermine volume serial! + } } else //a file { @@ -387,13 +392,12 @@ private: const bool validLink = tryReportingItemError([&] //try to resolve symlink (and report error on failure!!!) { getInfoFromFileSymlink(fullName, targetInfo); //throw FileError + targetInfo.symlinkInfo = &linkInfo; }, sink, shortName); if (validLink) sink.onFile(shortName, fullName, targetInfo); - // else //broken symlink - // sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); - warn_static("impact of ignored broken file/incomplete dir read on two-way variant!?") + // else //broken symlink -> ignore: it's client's responsibility to handle error! } break; @@ -403,8 +407,11 @@ private: } else if (Trav::isDirectory(findData)) { - if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse<Trav>(fullName, *rv, volumeSerial); + if (TraverseCallback* trav = sink.onDir(shortName, fullName)) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse<Trav>(fullName, *trav, volumeSerial); + } } else //a file { @@ -412,12 +419,12 @@ private: Trav::extractFileInfo(findData, volumeSerial, fileInfo); //####################################### DST hack ########################################### - if (isFatFileSystem) + if (needDstHack) { const dst::RawTime rawTime(Trav::getCreateTimeRaw(findData), Trav::getModTimeRaw(findData)); if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error - fileInfo.lastWriteTime = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error) + fileInfo.lastWriteTime = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw std::runtime_error else markForDstHack.push_back(std::make_pair(fullName, toTimeT(rawTime.writeTimeRaw))); } @@ -476,13 +483,13 @@ private: } } - const bool isFatFileSystem; - typedef std::vector<std::pair<Zstring, Int64> > FilenameTimeList; + const bool needDstHack; + typedef std::vector<std::pair<Zstring, Int64>> FilenameTimeList; FilenameTimeList markForDstHack; //####################################### DST hack ########################################### }; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC class DirTraverser { public: @@ -498,8 +505,8 @@ public: 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 size_t maxPath = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) - buffer.resize(offsetof(struct ::dirent, d_name) + maxPath + 1); + const size_t nameMax = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) + buffer.resize(offsetof(struct ::dirent, d_name) + nameMax + 1); traverse(directoryFormatted, sink); } @@ -517,7 +524,7 @@ private: { dirObj = ::opendir(dirname.c_str()); //directory must NOT end with path separator, except "/" if (!dirObj) - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"opendir", getLastError())); }, sink)) return; //ignored error ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash @@ -528,7 +535,7 @@ private: tryReportingDirError([&] { if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)), formatSystemError(L"readdir_r", getLastError())); }, sink); if (!dirEntry) //no more items or ignored error return; @@ -538,7 +545,7 @@ private: if (shortName[0] == '.' && (shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0))) continue; -#ifdef FFS_MAC +#ifdef ZEN_MAC //some file system abstraction layers fail to properly return decomposed UTF8: http://developer.apple.com/library/mac/#qa/qa1173/_index.html //so we need to do it ourselves; perf: ~600 ns per conversion //note: it's not sufficient to apply this in z_impl::compareFilenamesNoCase: if UTF8 forms differ, FFS assumes a rename in case sensitivity and @@ -564,7 +571,7 @@ private: if (!tryReportingItemError([&] { if (::lstat(fullName.c_str(), &statData) != 0) //lstat() does not resolve symlinks - throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(fullName)), formatSystemError(L"lstat", getLastError())); }, sink, shortName)) continue; //ignore error: skip file @@ -582,15 +589,18 @@ private: bool validLink = tryReportingItemError([&] { if (::stat(fullName.c_str(), &statDataTrg) != 0) - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)), formatSystemError(L"stat", getLastError())); }, sink, shortName); if (validLink) { if (S_ISDIR(statDataTrg.st_mode)) //a directory { - if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse(fullName, *rv); + if (TraverseCallback* trav = sink.onDir(shortName, fullName)) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse(fullName, *trav); + } } else //a file or named pipe, ect. { @@ -598,11 +608,11 @@ private: fileInfo.fileSize = zen::UInt64(statDataTrg.st_size); fileInfo.lastWriteTime = statDataTrg.st_mtime; //UTC time (time_t format); unit: 1 second fileInfo.id = extractFileID(statDataTrg); + fileInfo.symlinkInfo = &linkInfo; sink.onFile(shortName, fullName, fileInfo); } } - // else //report broken symlink as file! - // sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); + // else //broken symlink -> ignore: it's client's responsibility to handle error! } break; @@ -612,8 +622,11 @@ private: } else if (S_ISDIR(statData.st_mode)) //a directory { - if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse(fullName, *rv); + if (TraverseCallback* trav = sink.onDir(shortName, fullName)) + { + ZEN_ON_SCOPE_EXIT(sink.releaseDirTraverser(trav)); + traverse(fullName, *trav); + } } else //a file or named pipe, ect. { @@ -635,7 +648,7 @@ private: } std::vector<char> buffer; -#ifdef FFS_MAC +#ifdef ZEN_MAC std::vector<char> bufferUtfDecomposed; #endif }; diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 13b76966..a1d8ac9e 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -7,7 +7,7 @@ #ifndef FILETRAVERSER_H_INCLUDED #define FILETRAVERSER_H_INCLUDED -#include <memory> +//#include <memory> #include "zstring.h" #include "int64.h" #include "file_id_def.h" @@ -20,17 +20,18 @@ struct TraverseCallback { virtual ~TraverseCallback() {} - struct FileInfo + struct SymlinkInfo { - UInt64 fileSize; //unit: bytes! Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC - FileId id; //optional: initial if not supported! - //std::unique_ptr<SymlinkInfo> symlinkInfo; //only filled if file is dereferenced symlink }; - struct SymlinkInfo + struct FileInfo { + FileInfo() : symlinkInfo() {} + UInt64 fileSize; //unit: bytes! Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC + FileId id; //optional: initial if not supported! + const SymlinkInfo* symlinkInfo; //only filled if file is a followed symlink }; enum HandleLink @@ -45,22 +46,24 @@ struct TraverseCallback ON_ERROR_IGNORE }; - virtual std::shared_ptr<TraverseCallback> //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback - /**/ onDir (const Zchar* shortName, const Zstring& fullName) = 0; - virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0; - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0; + virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0; + virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0; + virtual TraverseCallback* onDir (const Zchar* shortName, const Zstring& fullName) = 0; + //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback => implement releaseDirTraverser() if necessary! + virtual void releaseDirTraverser(TraverseCallback* trav) {} + virtual HandleError reportDirError (const std::wstring& msg) = 0; //failed directory traversal -> consider directory data as incomplete! - virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) = 0; //failed to get single file/dir/symlink only! + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) = 0; //failed to get data for single file/dir/symlink only! }; -#ifdef FFS_WIN +#ifdef ZEN_WIN struct DstHackCallback { virtual ~DstHackCallback() {} virtual void requestUiRefresh(const Zstring& filename) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user }; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC struct DstHackCallback; //DST hack not required on Unix #endif diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 60eb6869..9e5975d9 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -12,12 +12,12 @@ #include <ctime> #include <cstdio> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <zen/win.h> //includes "windows.h" #include <zen/win_ver.h> -#elif defined FFS_LINUX || defined FFS_MAC -#include <clocale> //thousands separator +#elif defined ZEN_LINUX || defined ZEN_MAC +#include <clocale> //thousands separator #include <zen/utf.h> // #endif @@ -152,7 +152,7 @@ std::wstring zen::fractionToString(double fraction) } -#ifdef FFS_WIN +#ifdef ZEN_WIN namespace { bool getUserSetting(LCTYPE lt, UINT& setting) @@ -233,7 +233,7 @@ private: std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) { -#ifdef FFS_WIN +#ifdef ZEN_WIN if (IntegerFormat::isValid()) { int bufferSize = ::GetNumberFormat(LOCALE_USER_DEFAULT, 0, number.c_str(), &IntegerFormat::get(), nullptr, 0); @@ -251,7 +251,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) } return number; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC //we have to include thousands separator ourselves; this doesn't work for all countries (e.g india), but is better than nothing //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale @@ -277,7 +277,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) } -#ifdef FFS_WIN +#ifdef ZEN_WIN namespace { const bool useNewLocalTimeCalculation = zen::vistaOrLater(); @@ -289,7 +289,7 @@ std::wstring zen::utcToLocalTimeString(Int64 utcTime) { auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L")"; }; -#ifdef FFS_WIN +#ifdef ZEN_WIN FILETIME lastWriteTimeUtc = tofiletime(utcTime); //convert ansi C time to FILETIME SYSTEMTIME systemTimeLocal = {}; @@ -326,7 +326,7 @@ std::wstring zen::utcToLocalTimeString(Int64 utcTime) loc.minute = systemTimeLocal.wMinute; loc.second = systemTimeLocal.wSecond; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC zen::TimeComp loc = zen::localTime(to<time_t>(utcTime)); #endif diff --git a/zen/int64.h b/zen/int64.h index 4c63a24f..7d03f35d 100644 --- a/zen/int64.h +++ b/zen/int64.h @@ -15,7 +15,7 @@ #include "assert_static.h" #include "type_tools.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" #endif @@ -60,7 +60,7 @@ public: Int64& operator=(const Int64& rhs) { value = rhs.value; return *this; } -#ifdef FFS_WIN +#ifdef ZEN_WIN Int64(DWORD low, LONG high) { assert_static(sizeof(low) + sizeof(high) == sizeof(value)); @@ -139,7 +139,7 @@ public: UInt64& operator=(const UInt64& rhs) { value = rhs.value; return *this; } -#ifdef FFS_WIN +#ifdef ZEN_WIN UInt64(DWORD low, DWORD high) { assert_static(sizeof(low) + sizeof(high) == sizeof(value)); @@ -206,7 +206,7 @@ template <> inline UInt64 to(Int64 number) { checkRange<std::uint64_t>(number.va template <> inline Int64 to(UInt64 number) { checkRange<std:: int64_t>(number.value); return Int64(number.value); } -#ifdef FFS_WIN +#ifdef ZEN_WIN //convert FILETIME (number of 100-nanosecond intervals since January 1, 1601 UTC) // to time_t (number of seconds since Jan. 1st 1970 UTC) // diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp index 29958f0c..3815887a 100644 --- a/zen/notify_removal.cpp +++ b/zen/notify_removal.cpp @@ -46,7 +46,7 @@ private: MessageProvider(const MessageProvider&); MessageProvider& operator=(const MessageProvider&); - static const wchar_t WINDOW_NAME[]; + static const wchar_t dummyWindowName[]; friend LRESULT CALLBACK topWndProc(HWND, UINT, WPARAM, LPARAM); void processMessage(UINT message, WPARAM wParam, LPARAM lParam); @@ -58,7 +58,7 @@ private: }; -const wchar_t MessageProvider::WINDOW_NAME[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name +const wchar_t MessageProvider::dummyWindowName[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name LRESULT CALLBACK topWndProc(HWND hwnd, //handle to window @@ -82,22 +82,22 @@ MessageProvider::MessageProvider() : windowHandle(nullptr) { if (!hMainModule) - throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (GetModuleHandle)"); + throw FileError(_("Failed to register to receive system messages."), formatSystemError(L"GetModuleHandle", getLastError())); //register the main window class WNDCLASS wc = {}; wc.lpfnWndProc = topWndProc; wc.hInstance = hMainModule; - wc.lpszClassName = WINDOW_NAME; + wc.lpszClassName = dummyWindowName; if (::RegisterClass(&wc) == 0) - throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (RegisterClass)"); + throw FileError(_("Failed to register to receive system messages."), formatSystemError(L"RegisterClass", getLastError())); - ScopeGuard guardClass = makeGuard([&] { ::UnregisterClass(WINDOW_NAME, hMainModule); }); + ScopeGuard guardClass = makeGuard([&] { ::UnregisterClass(dummyWindowName, hMainModule); }); //create dummy-window - windowHandle = ::CreateWindow(WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word! - nullptr, //LPCTSTR lpWindowName, + windowHandle = ::CreateWindow(dummyWindowName, //LPCTSTR lpClassName OR ATOM in low-order word! + nullptr, //LPCTSTR lpWindowName, 0, //DWORD dwStyle, 0, //int x, 0, //int y, @@ -108,7 +108,7 @@ MessageProvider::MessageProvider() : hMainModule, //HINSTANCE hInstance, nullptr); //LPVOID lpParam if (!windowHandle) - throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (CreateWindow)"); + throw FileError(_("Failed to register to receive system messages."), formatSystemError(L"CreateWindow", getLastError())); guardClass.dismiss(); } @@ -118,7 +118,7 @@ MessageProvider::~MessageProvider() { //clean-up in reverse order ::DestroyWindow(windowHandle); - ::UnregisterClass(WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word! + ::UnregisterClass(dummyWindowName, //LPCTSTR lpClassName OR ATOM in low-order word! hMainModule); //HINSTANCE hInstance } @@ -128,8 +128,8 @@ void MessageProvider::processMessage(UINT message, WPARAM wParam, LPARAM lParam) std::for_each(listener.begin(), listener.end(), [&](Listener* ls) { ls->onMessage(message, wParam, lParam); }); } -//#################################################################################################### +//#################################################################################################### class NotifyRequestDeviceRemoval::Pimpl : private MessageProvider::Listener { @@ -139,6 +139,8 @@ public: { MessageProvider::instance().registerListener(*this); //throw FileError + ScopeGuard guardProvider = makeGuard([&] { MessageProvider::instance().unregisterListener(*this); }); + //register handles to receive notifications DEV_BROADCAST_HANDLE filter = {}; filter.dbch_size = sizeof(filter); @@ -154,13 +156,16 @@ public: 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(L"Could not register device removal notifications:" L"\n\n" + getLastErrorFormatted(lastError)); + throw zen::FileError(_("Failed to register to receive system messages."), formatSystemError(L"RegisterDeviceNotification", lastError)); } + + guardProvider.dismiss(); } ~Pimpl() { - ::UnregisterDeviceNotification(hNotification); + if (hNotification) + ::UnregisterDeviceNotification(hNotification); MessageProvider::instance().unregisterListener(*this); } @@ -172,10 +177,9 @@ private: { //DBT_DEVICEQUERYREMOVE example: http://msdn.microsoft.com/en-us/library/aa363427(v=VS.85).aspx if (message == WM_DEVICECHANGE) - { - if ( wParam == DBT_DEVICEQUERYREMOVE || - wParam == DBT_DEVICEQUERYREMOVEFAILED || - wParam == DBT_DEVICEREMOVECOMPLETE) + if (wParam == DBT_DEVICEQUERYREMOVE || + wParam == DBT_DEVICEQUERYREMOVEFAILED || + wParam == DBT_DEVICEREMOVECOMPLETE) { PDEV_BROADCAST_HDR header = reinterpret_cast<PDEV_BROADCAST_HDR>(lParam); if (header->dbch_devicetype == DBT_DEVTYP_HANDLE) @@ -202,14 +206,13 @@ private: } } } - } } NotifyRequestDeviceRemoval& parent_; HDEVNOTIFY hNotification; }; -//#################################################################################################### +//#################################################################################################### NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) { @@ -217,4 +220,4 @@ NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) } -NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~auto_ptr() works with complete type +NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~unique_ptr() works with complete type diff --git a/zen/notify_removal.h b/zen/notify_removal.h index dc8149a0..aca0912f 100644 --- a/zen/notify_removal.h +++ b/zen/notify_removal.h @@ -7,7 +7,6 @@ #ifndef NOTIFY_H_INCLUDED #define NOTIFY_H_INCLUDED -#include <vector> #include <memory> #include "win.h" //includes "windows.h" #include "file_error.h" diff --git a/zen/osx_error.h b/zen/osx_error.h deleted file mode 100644 index 4b0aeb3b..00000000 --- a/zen/osx_error.h +++ /dev/null @@ -1,27 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_ERRROR_834270598342753425 -#define OSX_ERRROR_834270598342753425 - -#include <string> - -namespace osx -{ -class OsxError //Exception base class used to notify file/directory copy/delete errors -{ -public: - explicit OsxError(const std::wstring& message) : msg(message) {} - virtual ~OsxError() {} - - const std::wstring& toString() const { return msg; } - -private: - std::wstring msg; -}; -} - -#endif //OSX_ERRROR_834270598342753425 diff --git a/zen/osx_string.h b/zen/osx_string.h index ce8b1062..0d77345c 100644 --- a/zen/osx_string.h +++ b/zen/osx_string.h @@ -7,8 +7,8 @@ #ifndef OSX_STRING_1873641732143214324 #define OSX_STRING_1873641732143214324 -#include <zen/zstring.h> #include <CoreFoundation/CoreFoundation.h> //CFString +#include "zstring.h" namespace osx { diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h index 31592854..018e9456 100644 --- a/zen/osx_throw_exception.h +++ b/zen/osx_throw_exception.h @@ -8,21 +8,21 @@ #define OSX_EXCEPTION_89274305834255 #import <Cocoa/Cocoa.h> -#include <zen/osx_error.h> -#include <zen/utf.h> +#include "sys_error.h" +#include "utf.h" namespace osx { //for use in Objective C implementation files only! -void throwOsxError(NSException* e); //throw OsxError +void throwSysError(NSException* e); //throw SysError -#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw OsxError +#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw SysError /* -Example: ZEN_COM_ASSERT(obj); +Example: ZEN_OSX_ASSERT(obj); Equivalent to: if (!obj) - throw OsxError(L"Assertion failed: \"obj\"."); + throw zen::SysError(L"Assertion failed: \"obj\"."); */ @@ -32,7 +32,7 @@ Equivalent to: //######################## implmentation ############################ inline -void throwOsxError(NSException* e) //throw OsxError +void throwSysError(NSException* e) //throw SysError { std::string msg; if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us! @@ -42,7 +42,7 @@ if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding]) msg += "\n"; msg += descr; } - throw OsxError(zen::utfCvrtTo<std::wstring>(msg)); + throw zen::SysError(zen::utfCvrtTo<std::wstring>(msg)); /* e.g. NSInvalidArgumentException @@ -51,6 +51,6 @@ if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding]) } } -#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw osx::OsxError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\"."); +#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\"."); #endif //OSX_EXCEPTION_89274305834255 @@ -10,7 +10,7 @@ #include "deprecate.h" #include "tick_count.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include <sstream> #else #include <iostream> @@ -33,7 +33,7 @@ public: ticksPerSec_(ticksPerSec()), startTime(), resultShown(false) { //std::clock() - "counts CPU time in C and wall time in VC++" - WTF!??? -#ifdef FFS_WIN +#ifdef ZEN_WIN if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0) //"should not be required unless there are bugs in BIOS or HAL" - msdn, QueryPerformanceCounter throw TimerError(); #endif @@ -51,7 +51,7 @@ public: throw TimerError(); const auto delta = static_cast<long>(1000.0 * dist(startTime, now) / ticksPerSec_); -#ifdef FFS_WIN +#ifdef ZEN_WIN std::ostringstream ss; ss << delta << " ms"; ::MessageBoxA(nullptr, ss.str().c_str(), "Timer", 0); diff --git a/zen/privilege.cpp b/zen/privilege.cpp index b474958e..44318517 100644 --- a/zen/privilege.cpp +++ b/zen/privilege.cpp @@ -15,14 +15,14 @@ bool privilegeIsActive(LPCTSTR privilege) //throw FileError if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, TOKEN_QUERY, //__in DWORD DesiredAccess, &hToken)) //__out PHANDLE TokenHandle - throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"OpenProcessToken", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); LUID luid = {}; if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, privilege, //__in LPCTSTR lpName, &luid )) //__out PLUID lpLuid - throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"LookupPrivilegeValue", getLastError())); PRIVILEGE_SET priv = {}; priv.PrivilegeCount = 1; @@ -34,7 +34,7 @@ bool privilegeIsActive(LPCTSTR privilege) //throw FileError if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken, &priv, //__inout PPRIVILEGE_SET RequiredPrivileges, &alreadyGranted)) //__out LPBOOL pfResult - throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"PrivilegeCheck", getLastError())); return alreadyGranted != FALSE; } @@ -46,14 +46,14 @@ void setPrivilege(LPCTSTR privilege, bool enable) //throw FileError if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess, &hToken)) //__out PHANDLE TokenHandle - throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"OpenProcessToken", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); LUID luid = {}; if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, privilege, //__in LPCTSTR lpName, &luid )) //__out PLUID lpLuid - throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"LookupPrivilegeValue", getLastError())); TOKEN_PRIVILEGES tp = {}; tp.PrivilegeCount = 1; @@ -66,10 +66,11 @@ void setPrivilege(LPCTSTR privilege, bool enable) //throw FileError 0, //__in DWORD BufferLength, nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState, nullptr)) //__out_opt PDWORD ReturnLength - throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"AdjustTokenPrivileges", getLastError())); - if (::GetLastError() == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success! - throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\"") + L"\n\n" + getLastErrorFormatted()); + const ErrorCode lastError = getLastError(); + if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success! + throw FileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), formatSystemError(L"AdjustTokenPrivileges", lastError)); } diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp index 96a6c2de..b09b35f5 100644 --- a/zen/process_priority.cpp +++ b/zen/process_priority.cpp @@ -5,16 +5,16 @@ // ************************************************************************** #include "process_priority.h" -#include <zen/last_error.h> +#include <zen/sys_error.h> #include <zen/i18n.h> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" //includes "windows.h" -#elif defined FFS_LINUX +#elif defined ZEN_LINUX //#include <sys/syscall.h> -#elif defined FFS_MAC +#elif defined ZEN_MAC #include <sys/resource.h> //getiopolicy_np #include <IOKit/pwr_mgt/IOPMLib.h> //keep in .cpp file to not pollute global namespace! e.g. with UInt64 #endif @@ -22,7 +22,7 @@ using namespace zen; -#ifdef FFS_WIN +#ifdef ZEN_WIN struct PreventStandby::Pimpl {}; PreventStandby::PreventStandby() @@ -49,7 +49,7 @@ struct ScheduleForBackgroundProcessing::Pimpl {}; ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() { if (!::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN)) //this call lowers CPU priority, too!! - throw FileError(_("Cannot change process I/O priorities.") + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot change process I/O priorities."), formatSystemError(L"SetPriorityClass", getLastError())); } @@ -58,7 +58,7 @@ ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END); } -#elif defined FFS_LINUX +#elif defined ZEN_LINUX struct PreventStandby::Pimpl {}; PreventStandby::PreventStandby() {} PreventStandby::~PreventStandby() {} @@ -101,7 +101,7 @@ private: }; */ -#elif defined FFS_MAC +#elif defined ZEN_MAC //https://developer.apple.com/library/mac/#qa/qa1340 struct PreventStandby::Pimpl { @@ -111,11 +111,12 @@ struct PreventStandby::Pimpl PreventStandby::PreventStandby() : pimpl(make_unique<Pimpl>()) { - if (::IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, - kIOPMAssertionLevelOn, - CFSTR("FreeFileSync"), - &pimpl->assertionID) != kIOReturnSuccess) - throw FileError(_("Failed to suspend system sleep mode.")); + IOReturn rv = ::IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, + kIOPMAssertionLevelOn, + CFSTR("FreeFileSync"), + &pimpl->assertionID); + if (rv != kIOReturnSuccess) + throw FileError(_("Failed to suspend system sleep mode."), replaceCpy<std::wstring>(L"IOReturn Code %x", L"%x", numberTo<std::wstring>(rv))); //could not find a better way to convert IOReturn to string } PreventStandby::~PreventStandby() @@ -135,10 +136,10 @@ ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() : pimpl(make_ { pimpl->oldIoPrio = ::getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS); if (pimpl->oldIoPrio == -1) - throw FileError(_("Cannot change process I/O priorities.") + L" (r)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot change process I/O priorities."), formatSystemError(L"getiopolicy_np", getLastError())); if (::setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE) != 0) - throw FileError(_("Cannot change process I/O priorities.") + L" (w)" + L"\n\n" + getLastErrorFormatted()); + throw FileError(_("Cannot change process I/O priorities."), formatSystemError(L"setiopolicy_np", getLastError())); } @@ -146,4 +147,4 @@ ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() { ::setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, pimpl->oldIoPrio); } -#endif
\ No newline at end of file +#endif diff --git a/zen/recycler.cpp b/zen/recycler.cpp index c062a26c..f1b8381a 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -9,7 +9,7 @@ //#include <iterator> #include <zen/file_handling.h> -#ifdef FFS_WIN +#ifdef ZEN_WIN //#include <algorithm> //#include <functional> #include <zen/dll.h> @@ -18,19 +18,19 @@ #include <zen/long_path_prefix.h> #include "IFileOperation/file_op.h" -#elif defined FFS_LINUX +#elif defined ZEN_LINUX #include <zen/scope_guard.h> #include <sys/stat.h> #include <gio/gio.h> -#elif defined FFS_MAC +#elif defined ZEN_MAC #include <CoreServices/CoreServices.h> #endif using namespace zen; -#ifdef FFS_WIN +#ifdef ZEN_WIN namespace { /* @@ -91,7 +91,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli const DllFun<FunType_getLastError> getLastError (getDllName(), funName_getLastError); if (!moveToRecycler || !getLastError) - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filenames[0])) + L"\n\n" + + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filenames[0])), replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); std::vector<const wchar_t*> cNames; @@ -108,8 +108,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli if (filenames.size() > 1) filenameFmt += L", ..."; //give at least some hint that there are multiple files, and the error need not be related to the first one - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", filenameFmt) + - L"\n\n" + getLastError()); //already includes details about locking errors! + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", filenameFmt), getLastError()); //already includes details about locking errors! } } else //regular recycle bin usage: available since XP @@ -134,7 +133,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli //"You should use fully-qualified path names with this function. Using it with relative path names is not thread safe." if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) { - throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filenames[0]))); //probably not the correct file name for file list larger than 1! + throw FileError(replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filenames[0]))); //probably not the correct file name for file list larger than 1! } } } @@ -146,12 +145,12 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError if (!somethingExists(filename)) //[!] do not optimize away, OS X needs this for reliable detection of "recycle bin missing" return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! -#ifdef FFS_WIN +#ifdef ZEN_WIN std::vector<Zstring> filenames; filenames.push_back(filename); recycleOrDelete(filenames, nullptr); //throw FileError -#elif defined FFS_LINUX +#elif defined ZEN_LINUX GFile* file = ::g_file_new_for_path(filename.c_str()); //never fails according to docu ZEN_ON_SCOPE_EXIT(g_object_unref(file);) @@ -160,10 +159,10 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError if (!::g_file_trash(file, nullptr, &error)) { - const std::wstring shortMsg = replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filename)); + const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filename)); if (!error) - throw FileError(shortMsg + L"\n\n" + L"Unknown error."); + throw FileError(errorMsg, L"Unknown error."); //user should never see this //implement same behavior as in Windows: if recycler is not existing, delete permanently if (error->code == G_IO_ERROR_NOT_SUPPORTED) @@ -179,11 +178,11 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError return true; } - throw FileError(shortMsg + L"\n\n" + L"Glib Error Code " + numberTo<std::wstring>(error->code) + /* L", " + - g_quark_to_string(error->domain) + */ L": " + utfCvrtTo<std::wstring>(error->message)); + throw FileError(errorMsg, replaceCpy<std::wstring>(L"Glib Error Code %x:", L"%x", numberTo<std::wstring>(error->code)) + L" " + utfCvrtTo<std::wstring>(error->message)); + //g_quark_to_string(error->domain) } -#elif defined FFS_MAC +#elif defined ZEN_MAC //we cannot use FSPathMoveObjectToTrashSync directly since it follows symlinks! assert_static(sizeof(Zchar) == sizeof(char)); @@ -191,12 +190,12 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError auto throwFileError = [&](OSStatus oss) { - std::wstring msg = replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filename)) + L"\n\n" - + L"Result Code " + numberTo<std::wstring>(oss); - const char* description = GetMacOSStatusCommentString(oss); - if (description) //found no documentation for proper use of GetMacOSStatusCommentString - msg += L": " + utfCvrtTo<std::wstring>(description); - throw FileError(msg); + const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the Recycle Bin."), L"%x", fmtFileName(filename)); + std::wstring errorDescr = L"OSStatus Code " + numberTo<std::wstring>(oss); + + if (const char* description = ::GetMacOSStatusCommentString(oss)) //found no documentation for proper use of GetMacOSStatusCommentString + errorDescr += L": " + utfCvrtTo<std::wstring>(description); + throw FileError(errorMsg, errorDescr); }; FSRef objectRef; @@ -234,7 +233,7 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError } -#ifdef FFS_WIN +#ifdef ZEN_WIN StatusRecycler zen::recycleBinStatus(const Zstring& pathName) { const DWORD bufferSize = MAX_PATH + 1; @@ -323,7 +322,7 @@ StatusRecycler zen::recycleBinStatus(const Zstring& pathName) */ } -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC /* We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not! diff --git a/zen/recycler.h b/zen/recycler.h index 8aca0ff3..f55971ac 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -34,7 +34,7 @@ Already included in package "gtk+-2.0"! bool recycleOrDelete(const Zstring& filename); //throw FileError, return "true" if file/dir was actually deleted -#ifdef FFS_WIN +#ifdef ZEN_WIN enum StatusRecycler { STATUS_REC_EXISTS, diff --git a/zen/scope_guard.h b/zen/scope_guard.h index cb28904f..000853fd 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** diff --git a/zen/serialize.h b/zen/serialize.h index 982165f9..415bd430 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -167,7 +167,7 @@ BinContainer loadBinStream(const Zstring& filename) //throw FileError, ErrorNotE const size_t blockSize = 128 * 1024; do { - contOut.resize(contOut.size() + blockSize); + contOut.resize(contOut.size() + blockSize); //container better implement exponential growth! const size_t bytesRead = fileIn.read(&*contOut.begin() + contOut.size() - blockSize, blockSize); //throw FileError if (bytesRead < blockSize) diff --git a/zen/stl_tools.h b/zen/stl_tools.h index b394b128..c93f2d61 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** @@ -129,7 +129,7 @@ ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const template <class BidirectionalIterator, class T> inline -BidirectionalIterator find_last(const BidirectionalIterator first, BidirectionalIterator last, const T& value) +BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value) { for (BidirectionalIterator iter = last; iter != first;) //reverse iteration: 1. check 2. decrement 3. evaluate { @@ -143,8 +143,8 @@ BidirectionalIterator find_last(const BidirectionalIterator first, Bidirectional template <class BidirectionalIterator1, class BidirectionalIterator2> inline -BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, - const BidirectionalIterator2 first2, BidirectionalIterator2 last2) +BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, + const BidirectionalIterator2 first2, const BidirectionalIterator2 last2) { const BidirectionalIterator1 iterNotFound = last1; diff --git a/zen/string_tools.h b/zen/string_tools.h index 990c823a..4639e1dd 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** @@ -463,41 +463,69 @@ perf: integer to string: (executed 10 mio. times) formatInteger - 778 ms */ -template <class S, class Num> inline -S formatInteger(Num n, bool hasMinus) +template <class OutputIterator, class Num> inline +void formatNegativeInteger(Num n, OutputIterator& it) { - assert(n >= 0); - typedef typename GetCharType<S>::Type CharType; - CharType buffer[2 + sizeof(Num) * 5 / 2]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency - //required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) < 2 + floor(sizeof(n) * 2.5) - - auto iter = std::end(buffer); + assert(n < 0); + typedef typename std::iterator_traits<OutputIterator>::value_type CharType; do { const Num tmp = n / 10; - *--iter = static_cast<CharType>('0' + (n - tmp * 10)); //8% faster than using modulus operator! + *--it = static_cast<CharType>('0' + (tmp * 10 - n)); //8% faster than using modulus operator! n = tmp; } while (n != 0); - if (hasMinus) - *--iter = static_cast<CharType>('-'); + *--it = static_cast<CharType>('-'); +} - return S(&*iter, std::end(buffer) - iter); +template <class OutputIterator, class Num> inline +void formatPositiveInteger(Num n, OutputIterator& it) +{ + assert(n >= 0); + typedef typename std::iterator_traits<OutputIterator>::value_type CharType; + do + { + const Num tmp = n / 10; + *--it = static_cast<CharType>('0' + (n - tmp * 10)); //8% faster than using modulus operator! + n = tmp; + } + while (n != 0); } + template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) { - return formatInteger<S>(number < 0 ? -number : number, number < 0); - //bug for "INT_MIN"! technically -INT_MIN == INT_MIN -> not worth the trouble + typedef typename GetCharType<S>::Type CharType; + CharType buffer[2 + sizeof(Num) * 241 / 100]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency + //required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1! + // <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41) + + //caveat: consider INT_MIN: technically -INT_MIN == INT_MIN + auto it = std::end(buffer); + if (number < 0) + formatNegativeInteger(number, it); + else + formatPositiveInteger(number, it); + assert(it >= std::begin(buffer)); + + return S(&*it, std::end(buffer) - it); } template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) { - return formatInteger<S>(number, false); + typedef typename GetCharType<S>::Type CharType; + CharType buffer[1 + sizeof(Num) * 241 / 100]; + //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41) + + auto it = std::end(buffer); + formatPositiveInteger(number, it); + assert(it >= std::begin(buffer)); + + return S(&*it, std::end(buffer) - it); } //-------------------------------------------------------------------------------- diff --git a/zen/string_traits.h b/zen/string_traits.h index 22aa2ffc..eb79831e 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** diff --git a/zen/symlink_target.h b/zen/symlink_target.h index bbced0fa..bfd9e038 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -10,27 +10,27 @@ #include "scope_guard.h" #include "file_error.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" //includes "windows.h" #include "WinIoCtl.h" #include "privilege.h" #include "long_path_prefix.h" #include "dll.h" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <unistd.h> +#include <stdlib.h> //realpath #endif namespace zen { -#ifdef FFS_WIN +#ifdef ZEN_WIN bool isSymlink(const WIN32_FIND_DATA& data); //*not* a simple FILE_ATTRIBUTE_REPARSE_POINT check! bool isSymlink(DWORD fileAttributes, DWORD reparseTag); - -Zstring getResolvedFilePath(const Zstring& filename); //throw FileError; requires Vista or later! #endif +Zstring getResolvedFilePath(const Zstring& linkPath); //throw FileError; Win: requires Vista or later! Zstring getSymlinkTargetRaw(const Zstring& linkPath); //throw FileError } @@ -84,7 +84,7 @@ namespace Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileError { using namespace zen; -#ifdef FFS_WIN +#ifdef ZEN_WIN //FSCTL_GET_REPARSE_POINT: http://msdn.microsoft.com/en-us/library/aa364571(VS.85).aspx //reading certain symlinks/junctions requires admin rights! @@ -100,7 +100,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); if (hLink == INVALID_HANDLE_VALUE) - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"CreateFile", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(hLink)); //respect alignment issues... @@ -116,7 +116,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro bufferSize, //__in DWORD nOutBufferSize, &bytesReturned, //__out_opt LPDWORD lpBytesReturned, nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"DeviceIoControl, FSCTL_GET_REPARSE_POINT", getLastError())); REPARSE_DATA_BUFFER& reparseData = *reinterpret_cast<REPARSE_DATA_BUFFER*>(&buffer[0]); //REPARSE_DATA_BUFFER needs to be artificially enlarged! @@ -132,7 +132,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro reparseData.MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); } else - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)) + L"\n\n" + L"Not a symbolic link or junction!"); + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), L"Not a symbolic link or junction!"); //absolute symlinks and junctions technically start with \??\ while relative ones do not if (startsWith(output, Zstr("\\??\\"))) @@ -140,29 +140,29 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro return output; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC const size_t BUFFER_SIZE = 10000; std::vector<char> buffer(BUFFER_SIZE); const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE); if (bytesWritten < 0 || bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation! { - std::wstring errorMessage = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)); + ErrorCode lastError = getLastError(); + const std::wstring errorMsg = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)); if (bytesWritten < 0) - errorMessage += L"\n\n" + getLastErrorFormatted(); - throw FileError(errorMessage); + throw FileError(errorMsg, formatSystemError(L"readlink", lastError)); + throw FileError(errorMsg); } return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination! #endif } -#ifdef FFS_WIN -Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError +Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError { using namespace zen; - - const HANDLE hDir = ::CreateFile(applyLongPathPrefix(filename).c_str(), +#ifdef ZEN_WIN + const HANDLE hDir = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, @@ -170,7 +170,7 @@ Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory nullptr); if (hDir == INVALID_HANDLE_VALUE) - throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)"); + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"CreateFile", getLastError())); ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); //GetFinalPathNameByHandle() is not available before Vista! @@ -178,11 +178,11 @@ Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW"); if (!getFinalPathNameByHandle) - throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\"")); + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\"")); const DWORD bufferSize = getFinalPathNameByHandle(hDir, nullptr, 0, 0); if (bufferSize == 0) - throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"GetFinalPathNameByHandle", getLastError())); std::vector<wchar_t> targetPath(bufferSize); const DWORD charsWritten = getFinalPathNameByHandle(hDir, //__in HANDLE hFile, @@ -191,16 +191,23 @@ Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError 0); //__in DWORD dwFlags if (charsWritten == 0 || charsWritten >= bufferSize) { - std::wstring errorMessage = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)); + const std::wstring errorMsg = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)); if (charsWritten == 0) - errorMessage += L"\n\n" + getLastErrorFormatted(); - throw FileError(errorMessage); + throw FileError(errorMsg, formatSystemError(L"GetFinalPathNameByHandle", getLastError())); + throw FileError(errorMsg); } return Zstring(&targetPath[0], charsWritten); -} + +#elif defined ZEN_LINUX || defined ZEN_MAC + char* targetPath = ::realpath(linkPath.c_str(), nullptr); + if (!targetPath) + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"realpath", getLastError())); + ZEN_ON_SCOPE_EXIT(::free(targetPath)); + return targetPath; #endif } +} namespace zen @@ -208,11 +215,10 @@ namespace zen inline Zstring getSymlinkTargetRaw(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); } - -#ifdef FFS_WIN inline -Zstring getResolvedFilePath(const Zstring& filename) { return getResolvedFilePath_impl(filename); } +Zstring getResolvedFilePath(const Zstring& linkPath) { return getResolvedFilePath_impl(linkPath); } +#ifdef ZEN_WIN /* Reparse Point Tags http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx diff --git a/zen/last_error.h b/zen/sys_error.h index ddee552f..bbac2eaa 100644 --- a/zen/last_error.h +++ b/zen/sys_error.h @@ -4,17 +4,18 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SYSTEMFUNCTIONS_H_INCLUDED -#define SYSTEMFUNCTIONS_H_INCLUDED +#ifndef LAST_ERROR_H_3284791347018951324534 +#define LAST_ERROR_H_3284791347018951324534 #include <string> #include "utf.h" #include "i18n.h" +#include "scope_guard.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" //includes "windows.h" -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC #include <cstring> #include <cerrno> #endif @@ -23,18 +24,30 @@ namespace zen { //evaluate GetLastError()/errno and assemble specific error message -#ifdef FFS_WIN +#ifdef ZEN_WIN typedef DWORD ErrorCode; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC typedef int ErrorCode; #endif -std::wstring getLastErrorFormatted(ErrorCode lastError = 0); ErrorCode getLastError(); +std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError); + bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" aliases +//A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"! +class SysError +{ +public: + explicit SysError(const std::wstring& msg) : msg_(msg) {} + const std::wstring& toString() const { return msg_; } + +private: + std::wstring msg_; +}; + @@ -46,44 +59,50 @@ bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" al inline ErrorCode getLastError() { -#ifdef FFS_WIN +#ifdef ZEN_WIN return ::GetLastError(); -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC return errno; //don't use "::", errno is a macro! #endif } + +std::wstring formatSystemError(const std::wstring& functionName, long long lastError); //not implemented! intentional overload ambiguity to catch usage errors with HRESULT! + + inline -std::wstring getLastErrorFormatted(ErrorCode lastError) +std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError) { - //determine error code if none was specified + //determine error code if none was specified -> still required?? if (lastError == 0) lastError = getLastError(); - std::wstring output = _("Error Code %x:"); - replace(output, L"%x", numberTo<std::wstring>(lastError)); -#ifdef FFS_WIN + std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo<std::wstring>(lastError)); + +#ifdef ZEN_WIN LPWSTR buffer = nullptr; if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_IGNORE_INSERTS | //important: without this flag ::FormatMessage() will fail if message contains placeholders FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, lastError, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0) - { if (buffer) //"don't trust nobody" { + ZEN_ON_SCOPE_EXIT(::LocalFree(buffer)); output += L" "; output += buffer; - ::LocalFree(buffer); } - } ::SetLastError(lastError); //restore last error -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC output += L" "; output += utfCvrtTo<std::wstring>(::strerror(lastError)); errno = lastError; //restore errno #endif + if (!endsWith(output, L" ")) //Windows messages seem to end with a blank... + output += L" "; + output += L"(" + functionName + L")"; + return output; } @@ -91,15 +110,15 @@ std::wstring getLastErrorFormatted(ErrorCode lastError) inline bool errorCodeForNotExisting(ErrorCode lastError) { -#ifdef FFS_WIN +#ifdef ZEN_WIN return lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_PATH_NOT_FOUND || lastError == ERROR_BAD_NETPATH || lastError == ERROR_NETNAME_DELETED; -#elif defined FFS_LINUX || defined FFS_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC return lastError == ENOENT; #endif } } -#endif // SYSTEMFUNCTIONS_H_INCLUDED +#endif //LAST_ERROR_H_3284791347018951324534 diff --git a/zen/tick_count.h b/zen/tick_count.h index d005a828..893d8e80 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -1,6 +1,6 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * 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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** @@ -10,13 +10,13 @@ #include <cstdint> #include "type_traits.h" #include "basic_math.h" -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "win.h" //includes "windows.h" -#elif defined FFS_LINUX +#elif defined ZEN_LINUX #include <time.h> //Posix ::clock_gettime() -#elif defined FFS_MAC +#elif defined ZEN_MAC #include <mach/mach_time.h> #endif //#include <algorithm> @@ -57,11 +57,11 @@ TickVal getTicks(); //return invalid value on error: !TickVal::isValid() class TickVal { public: -#ifdef FFS_WIN +#ifdef ZEN_WIN typedef LARGE_INTEGER NativeVal; -#elif defined FFS_LINUX +#elif defined ZEN_LINUX typedef timespec NativeVal; -#elif defined FFS_MAC +#elif defined ZEN_MAC typedef uint64_t NativeVal; #endif @@ -71,16 +71,16 @@ public: inline friend std::int64_t dist(const TickVal& lhs, const TickVal& rhs) { -#ifdef FFS_WIN +#ifdef ZEN_WIN return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow! -#elif defined FFS_LINUX +#elif defined ZEN_LINUX const auto distSec = numeric::dist(lhs.val_.tv_sec, rhs.val_.tv_sec); const auto distNsec = numeric::dist(lhs.val_.tv_nsec, rhs.val_.tv_nsec); if (distSec > (std::numeric_limits<std::int64_t>::max() - distNsec) / 1000000000) //truncate instead of overflow! return std::numeric_limits<std::int64_t>::max(); return distSec * 1000000000 + distNsec; -#elif defined FFS_MAC +#elif defined ZEN_MAC return numeric::dist(lhs.val_, rhs.val_); #endif } @@ -88,13 +88,13 @@ public: inline friend bool operator<(const TickVal& lhs, const TickVal& rhs) { -#ifdef FFS_WIN +#ifdef ZEN_WIN return lhs.val_.QuadPart < rhs.val_.QuadPart; -#elif defined FFS_LINUX +#elif defined ZEN_LINUX if (lhs.val_.tv_sec != rhs.val_.tv_sec) return lhs.val_.tv_sec < rhs.val_.tv_sec; return lhs.val_.tv_nsec < rhs.val_.tv_nsec; -#elif defined FFS_MAC +#elif defined ZEN_MAC return lhs.val_ < rhs.val_; #endif } @@ -109,17 +109,17 @@ private: inline std::int64_t ticksPerSec() //return 0 on error { -#ifdef FFS_WIN +#ifdef ZEN_WIN LARGE_INTEGER frequency = {}; if (!::QueryPerformanceFrequency(&frequency)) //MSDN promises: "The frequency cannot change while the system is running." return 0; static_assert(sizeof(std::int64_t) >= sizeof(frequency.QuadPart), ""); return frequency.QuadPart; -#elif defined FFS_LINUX +#elif defined ZEN_LINUX return 1000000000; //precision: nanoseconds -#elif defined FFS_MAC +#elif defined ZEN_MAC mach_timebase_info_data_t tbi = {}; if (::mach_timebase_info(&tbi) != KERN_SUCCESS) return 0; @@ -131,18 +131,18 @@ std::int64_t ticksPerSec() //return 0 on error inline TickVal getTicks() //return !isValid() on error { -#ifdef FFS_WIN +#ifdef ZEN_WIN LARGE_INTEGER now = {}; if (!::QueryPerformanceCounter(&now)) //msdn: SetThreadAffinityMask() may be required if there are bugs in BIOS or HAL" return TickVal(); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX //gettimeofday() seems fine but is deprecated timespec now = {}; if (::clock_gettime(CLOCK_MONOTONIC_RAW, &now) != 0) //CLOCK_MONOTONIC measures time reliably across processors! return TickVal(); -#elif defined FFS_MAC +#elif defined ZEN_MAC uint64_t now = ::mach_absolute_time(); //can this call fail??? #endif return TickVal(now); @@ -1,6 +1,6 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * 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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** @@ -197,8 +197,8 @@ template <class CharType> inline size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const struct std::tm* timeptr) { #if defined _MSC_VER && !defined NDEBUG - //it's no use: application init must register an invalid parameter that does nothing !!! - //=> strftime will abort with 0 and set errno to EINVAL instead of CRASH THE APPLICATION! + //there's no way around: application init must register an invalid parameter handler that does nothing !!! + //=> strftime will abort with 0 and set errno to EINVAL instead of CRASHING THE APPLICATION! _invalid_parameter_handler oldHandler = _set_invalid_parameter_handler(nullptr); assert(oldHandler); _set_invalid_parameter_handler(oldHandler); diff --git a/zen/type_tools.h b/zen/type_tools.h index c83e8f1e..e5ce29bd 100644 --- a/zen/type_tools.h +++ b/zen/type_tools.h @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** diff --git a/zen/type_traits.h b/zen/type_traits.h index 15352e8c..4f71f961 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** @@ -80,11 +80,6 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat - - - - - //################ implementation ###################### #define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : StaticBool<true> {}; @@ -1,5 +1,5 @@ // ************************************************************************** -// * This file is part of the zenXML project. It is distributed under the * +// * This file is part of the zen::Xml project. It is distributed under the * // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 085e6f72..a469ade2 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -7,11 +7,11 @@ #include "zstring.h" #include <stdexcept> -#ifdef FFS_WIN +#ifdef ZEN_WIN #include "dll.h" #include "win_ver.h" -#elif defined FFS_MAC +#elif defined ZEN_MAC //#include <zen/scope_guard.h> #include <ctype.h> //toupper() #endif @@ -64,7 +64,7 @@ private: const std::string message = std::string("Memory leak detected!") + "\n\n" + "Candidates:\n" + leakingStrings; -#ifdef FFS_WIN +#ifdef ZEN_WIN MessageBoxA(nullptr, message.c_str(), "Error", 0); #else std::cerr << message; @@ -85,7 +85,7 @@ private: void reportProblem(const std::string& message) //throw std::logic_error { -#ifdef FFS_WIN +#ifdef ZEN_WIN ::MessageBoxA(nullptr, message.c_str(), "Error", 0); #else std::cerr << message; @@ -128,7 +128,7 @@ time per call | function */ -#ifdef FFS_WIN +#ifdef ZEN_WIN namespace { #ifdef __MINGW32__ //MinGW is clueless... @@ -227,7 +227,7 @@ void z_impl::makeFilenameUpperCase(wchar_t* str, size_t size) throw std::runtime_error("Error converting to upper case! (LCMapString)"); } -#elif defined FFS_MAC +#elif defined ZEN_MAC int z_impl::compareFilenamesNoCase(const char* lhs, const char* rhs, size_t sizeLhs, size_t sizeRhs) { return ::strcasecmp(lhs, rhs); //locale-dependent! diff --git a/zen/zstring.h b/zen/zstring.h index 435f03a2..80c267e3 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -8,9 +8,9 @@ #define ZSTRING_H_INCLUDED #include "string_base.h" -#ifdef FFS_LINUX +#ifdef ZEN_LINUX #include <cstring> //strcmp -#elif defined FFS_MAC +#elif defined ZEN_MAC //#include <strings.h> //strcasecmp #endif @@ -49,12 +49,12 @@ public: //############################## helper functions ############################################# -#ifdef FFS_WIN //Windows encodes Unicode as UTF-16 wchar_t +#ifdef ZEN_WIN //Windows encodes Unicode as UTF-16 wchar_t typedef wchar_t Zchar; #define Zstr(x) L ## x const Zchar FILE_NAME_SEPARATOR = L'\\'; -#elif defined FFS_LINUX || defined FFS_MAC //Linux uses UTF-8 +#elif defined ZEN_LINUX || defined ZEN_MAC //Linux uses UTF-8 typedef char Zchar; #define Zstr(x) x const Zchar FILE_NAME_SEPARATOR = '/'; @@ -82,7 +82,7 @@ struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) == 0; } }; -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC template <template <class, class> class SP, class AP> void makeUpper(zen::Zbase<Zchar, SP, AP>& str); #endif @@ -101,7 +101,7 @@ Zstring appendSeparator(Zstring path) //support rvalue references! //################################# inline implementation ######################################## namespace z_impl { -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC int compareFilenamesNoCase(const Zchar* lhs, const Zchar* rhs, size_t sizeLhs, size_t sizeRhs); void makeFilenameUpperCase(Zchar* str, size_t size); #endif @@ -111,17 +111,17 @@ void makeFilenameUpperCase(Zchar* str, size_t size); template <template <class, class> class SP, class AP> inline int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) { -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC return z_impl::compareFilenamesNoCase(lhs.data(), rhs.data(), lhs.length(), rhs.length()); -#elif defined FFS_LINUX +#elif defined ZEN_LINUX return std::strcmp(lhs.c_str(), rhs.c_str()); //POSIX filenames don't have embedded 0 - //#elif defined FFS_MAC + //#elif defined ZEN_MAC // return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent! #endif } -#if defined FFS_WIN || defined FFS_MAC +#if defined ZEN_WIN || defined ZEN_MAC template <template <class, class> class SP, class AP> inline void makeUpper(zen::Zbase<Zchar, SP, AP>& str) { |