summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:20:50 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:20:50 +0200
commit7e706cf64654aea466c059c307e5723e2423ed5d (patch)
treee85f0d28d7c81b6d21419fc38e1a654cca2212b1 /zen
parent5.5 (diff)
downloadFreeFileSync-7e706cf64654aea466c059c307e5723e2423ed5d.tar.gz
FreeFileSync-7e706cf64654aea466c059c307e5723e2423ed5d.tar.bz2
FreeFileSync-7e706cf64654aea466c059c307e5723e2423ed5d.zip
5.6
Diffstat (limited to 'zen')
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp21
-rw-r--r--zen/FindFilePlus/find_file_plus.h1
-rw-r--r--zen/IFileOperation/file_op.cpp2
-rw-r--r--zen/assert_static.h2
-rw-r--r--zen/basic_math.h2
-rw-r--r--zen/debug_log.h2
-rw-r--r--zen/debug_new.h9
-rw-r--r--zen/dir_watcher.cpp2
-rw-r--r--zen/file_handling.cpp38
-rw-r--r--zen/file_handling.h2
-rw-r--r--zen/file_io.cpp71
-rw-r--r--zen/file_traverser.cpp45
-rw-r--r--zen/fixed_list.h2
-rw-r--r--zen/recycler.h2
-rw-r--r--zen/serialize.h244
-rw-r--r--zen/string_base.h2
-rw-r--r--zen/string_tools.h19
-rw-r--r--zen/symlink_target.h64
18 files changed, 434 insertions, 96 deletions
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp
index 19f43998..a6e82617 100644
--- a/zen/FindFilePlus/find_file_plus.cpp
+++ b/zen/FindFilePlus/find_file_plus.cpp
@@ -107,13 +107,13 @@ bool findplus::initDllBinding() //evaluate in ::DllMain() when attaching process
class findplus::FileSearcher
{
public:
- FileSearcher(const wchar_t* dirname); //throw FileError
+ FileSearcher(const wchar_t* dirname); //throw NtFileError
~FileSearcher();
- void readDir(FileInformation& output); //throw FileError
+ void readDir(FileInformation& output); //throw NtFileError
private:
- template <class QueryPolicy> void readDirImpl(FileInformation& output); //throw FileError
+ template <class QueryPolicy> void readDirImpl(FileInformation& output); //throw NtFileError
UNICODE_STRING dirnameNt; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()!
HANDLE hDir;
@@ -136,7 +136,7 @@ FileSearcher::FileSearcher(const wchar_t* dirname) :
dirnameNt.Length = 0;
dirnameNt.MaximumLength = 0;
- zen::ScopeGuard guardConstructor = zen::makeGuard([&]() { this->~FileSearcher(); });
+ zen::ScopeGuard guardConstructor = zen::makeGuard([&] { this->~FileSearcher(); });
//--------------------------------------------------------------------------------------------------------------
//convert dosFileName, e.g. C:\Users or \\?\C:\Users to ntFileName \??\C:\Users
@@ -222,11 +222,11 @@ struct DirQueryFileId
inline
-void FileSearcher::readDir(FileInformation& output) { readDirImpl<DirQueryFileId>(output); } //throw FileError
+void FileSearcher::readDir(FileInformation& output) { readDirImpl<DirQueryFileId>(output); } //throw NtFileError
template <class QueryPolicy>
-void FileSearcher::readDirImpl(FileInformation& output) //throw FileError
+void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError
{
//although FILE_ID_FULL_DIR_INFORMATION should suffice for our purposes, there are problems on Windows XP for certain directories, e.g. "\\Vboxsvr\build"
//making NtQueryDirectoryFile() return with STATUS_INVALID_PARAMETER while other directories, e.g. "C:\" work fine for some reason
@@ -335,6 +335,11 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw FileError
output.fileAttributes = dirInfo.FileAttributes;
output.shortNameLength = dirInfo.FileNameLength / sizeof(TCHAR); //FileNameLength is in bytes!
+ if (dirInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) //analog to FindFirstFile(), confirmed for Win XP and Win 7
+ output.reparseTag = dirInfo.EaSize; //
+ else
+ output.reparseTag = 0;
+
if (dirInfo.FileNameLength + sizeof(TCHAR) > sizeof(output.shortName)) //this may actually happen if ::NtQueryDirectoryFile() decides to return a
throw NtFileError(STATUS_BUFFER_OVERFLOW); //short name of length MAX_PATH + 1, 0-termination is not required!
@@ -352,7 +357,7 @@ FindHandle findplus::openDir(const wchar_t* dirname)
{
try
{
- return new FileSearcher(dirname); //throw FileError
+ return new FileSearcher(dirname); //throw NtFileError
}
catch (const NtFileError& e)
{
@@ -366,7 +371,7 @@ bool findplus::readDir(FindHandle hnd, FileInformation& output)
{
try
{
- hnd->readDir(output); //throw FileError
+ hnd->readDir(output); //throw NtFileError
return true;
}
catch (const NtFileError& e)
diff --git a/zen/FindFilePlus/find_file_plus.h b/zen/FindFilePlus/find_file_plus.h
index 3799a1e1..49b18733 100644
--- a/zen/FindFilePlus/find_file_plus.h
+++ b/zen/FindFilePlus/find_file_plus.h
@@ -36,6 +36,7 @@ struct FileInformation
ULARGE_INTEGER fileSize;
ULARGE_INTEGER fileId; //optional: may be 0 if not supported
DWORD fileAttributes;
+ DWORD reparseTag; //set if "fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT"
DWORD shortNameLength;
WCHAR shortName[MAX_PATH + 1]; //shortName is 0-terminated
}; //no need for #pragma pack -> all members are perfectly 4, 8 byte aligned!
diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp
index 7c75a8e8..b7bc9f9c 100644
--- a/zen/IFileOperation/file_op.cpp
+++ b/zen/IFileOperation/file_op.cpp
@@ -247,7 +247,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);
diff --git a/zen/assert_static.h b/zen/assert_static.h
index b96b5909..d61dd1e3 100644
--- a/zen/assert_static.h
+++ b/zen/assert_static.h
@@ -37,7 +37,7 @@ struct CompileTimeError<true> {};
#endif
*/
-//C++11: at least get rid of this pointless string literal requirement
+//C++11: please get rid of this pointless string literal requirement!
#define assert_static(X) \
static_assert(X, "Static assert has failed!");
diff --git a/zen/basic_math.h b/zen/basic_math.h
index dbc2d922..6678b91e 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -134,7 +134,7 @@ const T& max(const T& a, const T& b, const T& c)
template <class T> inline
-void confine(T& val, const T& minVal, const T& maxVal)
+void confine(T& val, const T& minVal, const T& maxVal) //name trim?
{
assert(minVal <= maxVal);
if (val < minVal)
diff --git a/zen/debug_log.h b/zen/debug_log.h
index 6be81e89..d65f5e36 100644
--- a/zen/debug_log.h
+++ b/zen/debug_log.h
@@ -56,7 +56,7 @@ public:
const std::string& message)
{
const std::string logEntry = "[" + formatTime<std::string>(FORMAT_TIME) + "] " + afterLast(sourceFile, ZEN_FILE_NAME_SEPARATOR) +
- ", line " + numberTo<std::string>(sourceRow) + ": " + message + "\n";
+ " (" + numberTo<std::string>(sourceRow) + "): " + message + "\n";
const size_t bytesWritten = ::fwrite(logEntry.c_str(), 1, logEntry.size(), handle);
if (std::ferror(handle) != 0 || bytesWritten != logEntry.size())
diff --git a/zen/debug_new.h b/zen/debug_new.h
index ca46cc8e..8d616360 100644
--- a/zen/debug_new.h
+++ b/zen/debug_new.h
@@ -18,9 +18,14 @@
/*overwrite "operator new" to get more detailed error messages on bad_alloc, detect memory leaks and write memory dumps
Usage:
- Include everywhere before any other file: $(ProjectDir)\shared\debug_new.h
+
For Minidumps:
-- Compile "debug_new.cpp"
-- Compile with debugging symbols and optimization deactivated
+-------------
+1. Compile "debug_new.cpp"
+2. Compile "release" build with:
+ - debugging symbols
+ - optimization deactivated
+ - do not suppress frame pointer(/Oy-) - avoid call stack mess up
*/
namespace mem_check
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index e54c1b5b..29a2a3cf 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -222,7 +222,7 @@ public:
}
catch (boost::thread_interrupted&)
{
- throw; //this is the only reasonable exception!
+ throw; //this is the only exception expected!
}
}
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index ebfe4d19..e176f3a6 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -74,8 +74,14 @@ bool zen::dirExists(const Zstring& dirname)
bool zen::symlinkExists(const Zstring& linkname)
{
#ifdef FFS_WIN
- const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(linkname).c_str());
- return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ WIN32_FIND_DATA fileInfo = {};
+ {
+ const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &fileInfo);
+ if (searchHandle == INVALID_HANDLE_VALUE)
+ return false;
+ ::FindClose(searchHandle);
+ }
+ return isSymlink(fileInfo);
#elif defined FFS_LINUX
struct stat fileInfo = {};
@@ -115,8 +121,7 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl
// GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
// &sourceAttr)) //__out LPVOID lpFileInformation
- const bool isSymbolicLink = (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
- if (!isSymbolicLink || procSl == SYMLINK_DIRECT)
+ if (!isSymlink(fileInfo) || procSl == SYMLINK_DIRECT)
{
//####################################### DST hack ###########################################
const bool isDirectory = (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
@@ -1531,7 +1536,21 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
void zen::makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) //FileError, ErrorTargetExisting
{
- //remove trailing separator
+#ifdef FFS_WIN
+ //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS!
+ const Zstring dirTmp = removeLongPathPrefix(directory);
+ if (dirTmp.size() == 3 &&
+ std::iswalpha(dirTmp[0]) && endsWith(dirTmp, L":\\"))
+ {
+ 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);
+ if (lastError == ERROR_ALREADY_EXISTS)
+ throw ErrorTargetExisting(msg);
+ throw FileError(msg);
+ }
+#endif
+ //remove trailing separator (except for volume root directories!)
const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ?
beforeLast(directory, FILE_NAME_SEPARATOR) :
directory;
@@ -1550,11 +1569,14 @@ void zen::makeDirectory(const Zstring& directory)
{
makeNewDirectory(directory, Zstring(), false); //FileError, ErrorTargetExisting
}
- catch (const ErrorTargetExisting&)
+ catch (const FileError& e)
{
- if (dirExists(directory))
+ assert(dynamic_cast<const ErrorTargetExisting*>(&e)); (void)e;
+ //could there be situations where a directory/network path exists, but creation fails with
+ //error different than "ErrorTargetExisting"?? => better catch all "FileError" and check existence again
+ if (dirExists(directory)) //technically a file system race-condition!
return;
- throw; //clash with file (dir symlink is okay)
+ throw;
}
}
diff --git a/zen/file_handling.h b/zen/file_handling.h
index d6444da3..d1dcca22 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -48,7 +48,7 @@ UInt64 getFilesize(const Zstring& filename); //throw FileError
UInt64 getFreeDiskSpace(const Zstring& path); //throw FileError
//file handling
-bool removeFile(const Zstring& filename); //return "true" if file was actually deleted; throw FileError
+bool removeFile(const Zstring& filename); //throw FileError; return "true" if file was actually deleted
void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = nullptr); //throw FileError
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 77fcb691..f935de7a 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -24,7 +24,7 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis
filename_(filename)
{
#ifdef FFS_WIN
- fileHandle = ::CreateFile(zen::applyLongPathPrefix(filename).c_str(),
+ fileHandle = ::CreateFile(applyLongPathPrefix(filename).c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_DELETE,
nullptr,
@@ -123,31 +123,54 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
filename_(filename)
{
#ifdef FFS_WIN
- fileHandle = ::CreateFile(zen::applyLongPathPrefix(filename).c_str(),
- GENERIC_READ | GENERIC_WRITE,
- /* http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
- quote: When an application creates a file across a network, it is better
- to use GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE alone.
- The resulting code is faster, because the redirector can use the cache manager and send fewer SMBs with more data.
- This combination also avoids an issue where writing to a file across a network can occasionally return ERROR_ACCESS_DENIED. */
- FILE_SHARE_DELETE, //FILE_SHARE_DELETE is required to rename file while handle is open!
- nullptr,
- access == ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
- nullptr);
+ const DWORD dwCreationDisposition = access == FileOutput::ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW;
+
+ auto getHandle = [&](DWORD dwFlagsAndAttributes)
+ {
+ return ::CreateFile(applyLongPathPrefix(filename).c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ /* http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
+ quote: When an application creates a file across a network, it is better
+ to use GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE alone.
+ The resulting code is faster, because the redirector can use the cache manager and send fewer SMBs with more data.
+ This combination also avoids an issue where writing to a file across a network can occasionally return ERROR_ACCESS_DENIED. */
+ FILE_SHARE_DELETE, //FILE_SHARE_DELETE is required to rename file while handle is open!
+ nullptr,
+ dwCreationDisposition,
+ dwFlagsAndAttributes | FILE_FLAG_SEQUENTIAL_SCAN,
+ nullptr);
+ };
+
+ fileHandle = getHandle(FILE_ATTRIBUTE_NORMAL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
- const DWORD lastError = ::GetLastError();
- const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + zen::getLastErrorFormatted(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);
-
- if (lastError == ERROR_PATH_NOT_FOUND)
- throw ErrorTargetPathMissing(errorMessage);
-
- throw FileError(errorMessage);
+ DWORD lastError = ::GetLastError();
+
+ //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 &&
+ dwCreationDisposition == CREATE_ALWAYS)
+ {
+ const DWORD attrib = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
+ if (attrib != INVALID_FILE_ATTRIBUTES)
+ {
+ fileHandle = getHandle(attrib); //retry
+ lastError = ::GetLastError();
+ }
+ }
+ //"regular" error handling
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ {
+ const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename_)) + L"\n\n" + zen::getLastErrorFormatted(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);
+
+ if (lastError == ERROR_PATH_NOT_FOUND)
+ throw ErrorTargetPathMissing(errorMessage);
+
+ throw FileError(errorMessage);
+ }
}
#elif defined FFS_LINUX
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index aa46d4f0..986e0ad8 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -225,7 +225,6 @@ struct Win32Traverser
return true;
}
- template <class FindData>
static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output)
{
output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
@@ -233,23 +232,12 @@ struct Win32Traverser
output.id = FileId();
}
- template <class FindData>
- static Int64 getModTime(const FindData& fileInfo) { return toTimeT(fileInfo.ftLastWriteTime); }
-
- template <class FindData>
- static const FILETIME& getModTimeRaw(const FindData& fileInfo) { return fileInfo.ftLastWriteTime; }
-
- template <class FindData>
+ static Int64 getModTime (const FindData& fileInfo) { return toTimeT(fileInfo.ftLastWriteTime); }
+ static const FILETIME& getModTimeRaw (const FindData& fileInfo) { return fileInfo.ftLastWriteTime; }
static const FILETIME& getCreateTimeRaw(const FindData& fileInfo) { return fileInfo.ftCreationTime; }
-
- template <class FindData>
- static const wchar_t* getShortName(const FindData& fileInfo) { return fileInfo.cFileName; }
-
- template <class FindData>
- static bool isDirectory(const FindData& fileInfo) { return (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; }
-
- template <class FindData>
- static bool isSymlink(const FindData& fileInfo) { return (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; }
+ static const wchar_t* getShortName (const FindData& fileInfo) { return fileInfo.cFileName; }
+ static bool isDirectory (const FindData& fileInfo) { return (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; }
+ static bool isSymlink (const FindData& fileInfo) { return zen::isSymlink(fileInfo); } //[!] keep namespace
};
@@ -295,10 +283,10 @@ struct FilePlusTraverser
//else we have a problem... report it:
throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (+)");
}
+
return true;
}
- template <class FindData>
static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output)
{
output.fileSize = fileInfo.fileSize.QuadPart;
@@ -306,23 +294,12 @@ struct FilePlusTraverser
output.id = extractFileID(volumeSerial, fileInfo.fileId);
}
- template <class FindData>
- static Int64 getModTime(const FindData& fileInfo) { return toTimeT(fileInfo.lastWriteTime); }
-
- template <class FindData>
- static const FILETIME& getModTimeRaw(const FindData& fileInfo) { return fileInfo.lastWriteTime; }
-
- template <class FindData>
+ static Int64 getModTime (const FindData& fileInfo) { return toTimeT(fileInfo.lastWriteTime); }
+ static const FILETIME& getModTimeRaw (const FindData& fileInfo) { return fileInfo.lastWriteTime; }
static const FILETIME& getCreateTimeRaw(const FindData& fileInfo) { return fileInfo.creationTime; }
-
- template <class FindData>
- static const wchar_t* getShortName(const FindData& fileInfo) { return fileInfo.shortName; }
-
- template <class FindData>
- static bool isDirectory(const FindData& fileInfo) { return (fileInfo.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; }
-
- template <class FindData>
- static bool isSymlink(const FindData& fileInfo) { return (fileInfo.fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; }
+ static const wchar_t* getShortName (const FindData& fileInfo) { return fileInfo.shortName; }
+ static bool isDirectory (const FindData& fileInfo) { return (fileInfo.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; }
+ static bool isSymlink (const FindData& fileInfo) { return zen::isSymlink(fileInfo.fileAttributes, fileInfo.reparseTag); } //[!] keep namespace
};
diff --git a/zen/fixed_list.h b/zen/fixed_list.h
index 04a680ad..60d7e0e7 100644
--- a/zen/fixed_list.h
+++ b/zen/fixed_list.h
@@ -19,7 +19,7 @@ class FixedList
struct Node
{
Node() : next(nullptr), val() {}
- //no variadic templates on VC2010... :(
+ //no variadic templates on VC2010... :(
template <class A> Node(A&& a) : next(nullptr), val(std::forward<A>(a)) {}
template <class A, class B> Node(A&& a, B&& b) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b)) {}
template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c)) {}
diff --git a/zen/recycler.h b/zen/recycler.h
index 1778eb2e..955dfbee 100644
--- a/zen/recycler.h
+++ b/zen/recycler.h
@@ -29,7 +29,7 @@ Linker flags: `pkg-config --libs gio-2.0`
Already included in package "gtk+-2.0"!
*/
-//move a file or folder to Recycle Bin (deletes permanently if recycle is not available) -> crappy semantics, but we have no choice thanks to Windows' design
+//move a file or folder to Recycle Bin (deletes permanently if recycler is not available) -> crappy semantics, but we have no choice thanks to Windows' design
bool recycleOrDelete(const Zstring& filename); //throw FileError, return "true" if file/dir was actually deleted
diff --git a/zen/serialize.h b/zen/serialize.h
new file mode 100644
index 00000000..ff9695b1
--- /dev/null
+++ b/zen/serialize.h
@@ -0,0 +1,244 @@
+// **************************************************************************
+// * 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 (zhnmju123 AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef SERIALIZE_H_INCLUDED
+#define SERIALIZE_H_INCLUDED
+
+#include <cstdint>
+#include <zen/string_base.h>
+#include <zen/file_io.h>
+
+
+namespace zen
+{
+//high-performance unformatted serialization (avoiding wxMemoryOutputStream/wxMemoryInputStream inefficiencies)
+
+/*
+--------------------------
+|Binary Container Concept|
+--------------------------
+binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<char>, std::string, Zbase<char>)
+*/
+
+//binary container reference implementations
+typedef Zbase<char> Utf8String; //ref-counted + COW text stream + guaranteed performance: exponential growth
+class BinaryStream; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?)
+
+class BinaryStream //essentially a std::vector<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite
+{
+public:
+ BinaryStream() : buffer(std::make_shared<std::vector<char>>()) {}
+
+ typedef std::vector<char>::value_type value_type;
+ typedef std::vector<char>::iterator iterator;
+ typedef std::vector<char>::const_iterator const_iterator;
+
+ iterator begin() { return buffer->begin(); }
+ iterator end () { return buffer->end (); }
+
+ const_iterator begin() const { return buffer->begin(); }
+ const_iterator end () const { return buffer->end (); }
+
+ void resize(size_t len) { buffer->resize(len); }
+ size_t size() const { return buffer->size(); }
+ bool empty() const { return buffer->empty(); }
+
+ inline friend bool operator==(const BinaryStream& lhs, const BinaryStream& rhs) { return *lhs.buffer == *rhs.buffer; }
+
+private:
+ std::shared_ptr<std::vector<char>> buffer; //always bound!
+ //perf: shared_ptr indirection irrelevant: less than 1% slower!
+};
+
+//----------------------------------------------------------------------
+//functions based on binary container abstraction
+template <class BinContainer> void saveBinStream(const Zstring& filename, const BinContainer& cont); //throw FileError
+template <class BinContainer> BinContainer loadBinStream(const Zstring& filename); //throw FileError, ErrorNotExisting
+
+
+/*
+-----------------------------
+|Binary Input Stream Concept|
+-----------------------------
+struct BinInputStream
+{
+ const void* requestRead(size_t len); //expect external read of len bytes
+};
+
+------------------------------
+|Binary Output Stream Concept|
+------------------------------
+struct BinOutputStream
+{
+ void* requestWrite(size_t len); //expect external write of len bytes
+};
+*/
+
+//binary input/output stream reference implementation
+class UnexpectedEndOfStreamError {};
+
+struct BinStreamIn //throw UnexpectedEndOfStreamError
+{
+ BinStreamIn(const BinaryStream& cont) : buffer(cont), pos(0) {} //this better be cheap!
+
+ const void* requestRead(size_t len) //throw UnexpectedEndOfStreamError
+ {
+ if (pos + len > buffer.size())
+ throw UnexpectedEndOfStreamError();
+ size_t oldPos = pos;
+ pos += len;
+ return &*buffer.begin() + oldPos;
+ }
+
+private:
+ const BinaryStream buffer;
+ size_t pos;
+};
+
+struct BinStreamOut
+{
+ void* requestWrite(size_t len)
+ {
+ size_t oldSize = buffer.size();
+ buffer.resize(buffer.size() + len);
+ return &*buffer.begin() + oldSize;
+ }
+
+ BinaryStream get() { return buffer; }
+
+private:
+ BinaryStream buffer;
+};
+
+//----------------------------------------------------------------------
+//functions based on binary stream abstraction
+template <class N, class BinOutputStream> void writeNumber (BinOutputStream& stream, const N& num); //
+template <class C, class BinOutputStream> void writeContainer(BinOutputStream& stream, const C& str); //throw ()
+template < class BinOutputStream> void writeArray (BinOutputStream& stream, const void* data, size_t len); //
+
+//----------------------------------------------------------------------
+
+template <class N, class BinInputStream> N readNumber (BinInputStream& stream); //
+template <class C, class BinInputStream> C readContainer(BinInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data)
+template < class BinInputStream> void readArray (BinInputStream& stream, void* data, size_t len); //
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-----------------------implementation-------------------------------
+template <class BinContainer> inline
+void saveBinStream(const Zstring& filename, const BinContainer& cont) //throw FileError
+{
+ assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further)
+
+ FileOutput fileOut(filename, zen::FileOutput::ACC_OVERWRITE); //throw FileError
+ if (!cont.empty())
+ fileOut.write(&*cont.begin(), cont.size()); //throw FileError
+}
+
+
+template <class BinContainer> inline
+BinContainer loadBinStream(const Zstring& filename) //throw FileError, ErrorNotExisting
+{
+ assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further)
+
+ FileInput fileIn(filename); //throw FileError, ErrorNotExisting
+
+ BinContainer contOut;
+ const size_t blockSize = 64 * 1024;
+ do
+ {
+ contOut.resize(contOut.size() + blockSize);
+
+ const size_t bytesRead = fileIn.read(&*contOut.begin() + contOut.size() - blockSize, blockSize); //throw FileError
+ if (bytesRead < blockSize)
+ contOut.resize(contOut.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
+ }
+ while (!fileIn.eof());
+
+ return contOut;
+}
+
+
+template <class BinOutputStream> inline
+void writeArray(BinOutputStream& stream, const void* data, size_t len)
+{
+ std::copy(static_cast<const char*>(data),
+ static_cast<const char*>(data) + len,
+ static_cast< char*>(stream.requestWrite(len)));
+}
+
+
+template <class N, class BinOutputStream> inline
+void writeNumber(BinOutputStream& stream, const N& num)
+{
+ assert_static((IsArithmetic<N>::value || IsSameType<N, bool>::value));
+ writeArray(stream, &num, sizeof(N));
+}
+
+
+template <class C, class BinOutputStream> inline
+void writeContainer(BinOutputStream& stream, const C& cont) //don't even consider UTF8 conversions here, we're handling arbitrary binary data!
+{
+ const auto len = cont.size();
+ writeNumber(stream, static_cast<std::uint32_t>(len));
+ if (len > 0)
+ writeArray(stream, &*cont.begin(), sizeof(typename C::value_type) * len); //don't use c_str(), but access uniformly via STL interface
+}
+
+
+template <class BinInputStream> inline
+void readArray(BinInputStream& stream, void* data, size_t len)
+{
+ const char* const src = static_cast<const char*>(stream.requestRead(len)); //expect external write of len bytes
+ std::copy(src, src + len, static_cast<char*>(data));
+}
+
+
+template <class N, class BinInputStream> inline
+N readNumber(BinInputStream& stream)
+{
+ assert_static((IsArithmetic<N>::value || IsSameType<N, bool>::value));
+ N num = 0;
+ readArray(stream, &num, sizeof(N));
+ return num;
+}
+
+
+template <class C, class BinInputStream> inline
+C readContainer(BinInputStream& stream)
+{
+ C cont;
+ auto strLength = readNumber<std::uint32_t>(stream);
+ if (strLength > 0)
+ try
+ {
+ cont.resize(strLength); //throw std::bad_alloc
+ readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength);
+ }
+ catch (std::bad_alloc&) //most likely this is due to data corruption!
+ {
+ throw UnexpectedEndOfStreamError();
+ }
+ return cont;
+}
+}
+
+#endif //SERIALIZE_H_INCLUDED
diff --git a/zen/string_base.h b/zen/string_base.h
index 4c339d56..e2e9f19a 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -196,7 +196,7 @@ public:
Zbase(const Char* source, size_t length);
Zbase(const Zbase& source);
Zbase(Zbase&& tmp);
- explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but no error
+ explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error!
//allow explicit construction from different string type, prevent ambiguity via SFINAE
template <class S> explicit Zbase(const S& other, typename S::value_type = 0);
~Zbase();
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 00eb50ee..80232086 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -458,30 +458,29 @@ S formatInteger(Num n, bool hasMinus)
{
assert(n >= 0);
typedef typename GetCharType<S>::Type CharType;
+ CharType buffer[2 + 5 * sizeof(Num) / 2]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
+ //minimum required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) < 2 + floor(sizeof(n) * 2.5)
- const size_t bufferSize = 64; //sufficient for signed 128-bit numbers
- CharType buffer[bufferSize]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
- assert_static(2 + 5 * sizeof(n) / 2 <= bufferSize);
- //minimum required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.5)
-
- size_t startPos = bufferSize;
+ auto iter = std::end(buffer);
do
{
- buffer[--startPos] = static_cast<char>('0' + n % 10);
- n /= 10;
+ const Num tmp = n / 10;
+ *--iter = static_cast<CharType>('0' + (n - tmp * 10)); //8% faster than using modulus operator!
+ n = tmp;
}
while (n != 0);
if (hasMinus)
- buffer[--startPos] = static_cast<CharType>('-');
+ *--iter = static_cast<CharType>('-');
- return S(buffer + startPos, bufferSize - startPos);
+ return S(&*iter, std::end(buffer) - iter);
}
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
}
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 17ae1916..6d44d845 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -21,6 +21,33 @@
#endif
+namespace zen
+{
+#ifdef FFS_WIN
+bool isSymlink(const WIN32_FIND_DATA& data); //*not* a simple FILE_ATTRIBUTE_REPARSE_POINT check!
+bool isSymlink(DWORD fileAttributes, DWORD reparseTag);
+#endif
+
+Zstring getSymlinkRawTargetString(const Zstring& linkPath); //throw FileError
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//################################ implementation ################################
#ifdef _MSC_VER //I don't have Windows Driver Kit at hands right now, so unfortunately we need to redefine this structures and cross fingers...
typedef struct _REPARSE_DATA_BUFFER //from ntifs.h
{
@@ -55,10 +82,11 @@ typedef struct _REPARSE_DATA_BUFFER //from ntifs.h
#define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
#endif
+
namespace
{
//retrieve raw target data of symlink or junction
-Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError
+Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileError
{
using namespace zen;
#ifdef FFS_WIN
@@ -136,4 +164,38 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError
}
}
+
+namespace zen
+{
+inline
+Zstring getSymlinkRawTargetString(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); }
+
+
+#ifdef FFS_WIN
+/*
+ Reparse Point Tags
+ http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx
+ WIN32_FIND_DATA structure
+ http://msdn.microsoft.com/en-us/library/windows/desktop/aa365740(v=vs.85).aspx
+
+ The only surrogate reparse points are;
+ IO_REPARSE_TAG_MOUNT_POINT
+ IO_REPARSE_TAG_SYMLINK
+*/
+
+inline
+bool isSymlink(DWORD fileAttributes, DWORD reparseTag)
+{
+ return (fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 &&
+ IsReparseTagNameSurrogate(reparseTag);
+}
+
+inline
+bool isSymlink(const WIN32_FIND_DATA& data)
+{
+ return isSymlink(data.dwFileAttributes, data.dwReserved0);
+}
+#endif
+}
+
#endif // SYMLINK_WIN_H_INCLUDED
bgstack15