summaryrefslogtreecommitdiff
path: root/zen/file_traverser.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:24:59 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:24:59 +0200
commita1c91f4695e208d5a8f80dc37b1818169b7829ff (patch)
tree52f5134376d17c99b6c9e53133a2eb5cf171377c /zen/file_traverser.cpp
parent5.16 (diff)
downloadFreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.tar.gz
FreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.tar.bz2
FreeFileSync-a1c91f4695e208d5a8f80dc37b1818169b7829ff.zip
5.17
Diffstat (limited to 'zen/file_traverser.cpp')
-rw-r--r--zen/file_traverser.cpp105
1 files changed, 59 insertions, 46 deletions
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
};
bgstack15