summaryrefslogtreecommitdiff
path: root/lib/db_file.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/db_file.cpp')
-rw-r--r--lib/db_file.cpp248
1 files changed, 130 insertions, 118 deletions
diff --git a/lib/db_file.cpp b/lib/db_file.cpp
index 2d02d634..8ae8f94b 100644
--- a/lib/db_file.cpp
+++ b/lib/db_file.cpp
@@ -60,16 +60,36 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false)
}
+class CheckedDbReader : public CheckedReader
+{
+public:
+ CheckedDbReader(wxInputStream& stream, const Zstring& errorObjName) : CheckedReader(stream), errorObjName_(errorObjName) {}
+
+private:
+ virtual void throwException() const { throw FileError(_("Error reading from synchronization database:") + L" \n" + L"\"" + errorObjName_ + L"\""); }
+
+ const Zstring errorObjName_;
+};
+
+
+class CheckedDbWriter : public CheckedWriter
+{
+public:
+ CheckedDbWriter(wxOutputStream& stream, const Zstring& errorObjName) : CheckedWriter(stream), errorObjName_(errorObjName) {}
+
+ virtual void throwException() const { throw FileError(_("Error writing to synchronization database:") + L" \n" + L"\"" + errorObjName_ + L"\""); }
+
+ const Zstring errorObjName_;
+};
+
+
+
StreamMapping loadStreams(const Zstring& filename) //throw FileError
{
- if (!zen::fileExists(filename))
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
- _("One of the FreeFileSync database files is not yet existing:") + L" \n" +
- L"\"" + filename + L"\"");
try
{
//read format description (uncompressed)
- FileInputStream rawStream(filename); //throw FileError
+ FileInputStream rawStream(filename); //throw FileError, ErrorNotExisting
//read FreeFileSync file identifier
char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {};
@@ -80,26 +100,32 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError
wxZlibInputStream decompressed(rawStream, wxZLIB_ZLIB);
- CheckedReader cr(decompressed, filename);
+ CheckedDbReader cr(decompressed, filename);
- std::int32_t version = cr.readNumberC<std::int32_t>();
+ std::int32_t version = cr.readPOD<std::int32_t>();
if (version != FILE_FORMAT_VER) //read file format version#
throw FileError(_("Incompatible synchronization database format:") + L" \n" + L"\"" + filename + L"\"");
//read stream lists
StreamMapping output;
- std::uint32_t dbCount = cr.readNumberC<std::uint32_t>(); //number of databases: one for each sync-pair
+ std::uint32_t dbCount = cr.readPOD<std::uint32_t>(); //number of databases: one for each sync-pair
while (dbCount-- != 0)
{
//DB id of partner databases
- const std::string sessionID = cr.readStringC<std::string>();
- const MemoryStream stream = cr.readStringC<MemoryStream>(); //read db-entry stream (containing DirInformation)
+ const std::string sessionID = cr.readString<std::string>();
+ const MemoryStream stream = cr.readString<MemoryStream>(); //read db-entry stream (containing DirInformation)
output.insert(std::make_pair(sessionID, stream));
}
return output;
}
+ catch (ErrorNotExisting&)
+ {
+ throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
+ _("One of the FreeFileSync database files is not yet existing:") + L" \n" +
+ L"\"" + filename + L"\"");
+ }
catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file
{
throw FileError(_("Error reading from synchronization database:") + L" (bad alloc)");
@@ -107,7 +133,7 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError
}
-class StreamParser : private CheckedReader
+class StreamParser : private CheckedDbReader
{
public:
static DirInfoPtr execute(const MemoryStream& stream, const Zstring& fileName) //throw FileError -> return value always bound!
@@ -127,14 +153,14 @@ public:
}
private:
- StreamParser(wxInputStream& stream, const Zstring& errorObjName, DirInformation& dirInfo) : CheckedReader(stream, errorObjName)
+ StreamParser(wxInputStream& stream, const Zstring& errorObjName, DirInformation& dirInfo) : CheckedDbReader(stream, errorObjName)
{
recurse(dirInfo.baseDirContainer);
}
Zstring readStringUtf8() const
{
- return utf8CvrtTo<Zstring>(readStringC<Zbase<char>>());
+ return utf8CvrtTo<Zstring>(readString<Zbase<char>>());
}
FileId readFileId() const
@@ -142,39 +168,39 @@ private:
assert_static(sizeof(FileId().first ) <= sizeof(std::uint64_t));
assert_static(sizeof(FileId().second) <= sizeof(std::uint64_t));
- const auto devId = static_cast<decltype(FileId().first )>(readNumberC<std::uint64_t>()); //
- const auto fId = static_cast<decltype(FileId().second)>(readNumberC<std::uint64_t>()); //silence "loss of precision" compiler warnings
+ const auto devId = static_cast<decltype(FileId().first )>(readPOD<std::uint64_t>()); //
+ const auto fId = static_cast<decltype(FileId().second)>(readPOD<std::uint64_t>()); //silence "loss of precision" compiler warnings
return std::make_pair(devId, fId);
}
void recurse(DirContainer& dirCont) const
{
- while (readNumberC<bool>()) //files
+ while (readPOD<bool>()) //files
{
//attention: order of function argument evaluation is undefined! So do it one after the other...
const Zstring shortName = readStringUtf8(); //file name
- const std::int64_t modTime = readNumberC<std::int64_t>();
- const std::uint64_t fileSize = readNumberC<std::uint64_t>();
+ const std::int64_t modTime = readPOD<std::int64_t>();
+ const std::uint64_t fileSize = readPOD<std::uint64_t>();
const FileId fileID = readFileId();
dirCont.addSubFile(shortName,
FileDescriptor(modTime, fileSize, fileID));
}
- while (readNumberC<bool>()) //symlinks
+ while (readPOD<bool>()) //symlinks
{
//attention: order of function argument evaluation is undefined! So do it one after the other...
const Zstring shortName = readStringUtf8(); //file name
- const std::int64_t modTime = readNumberC<std::int64_t>();
+ const std::int64_t modTime = readPOD<std::int64_t>();
const Zstring targetPath = readStringUtf8(); //file name
- const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readNumberC<std::int32_t>());
+ const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readPOD<std::int32_t>());
dirCont.addSubLink(shortName,
LinkDescriptor(modTime, targetPath, linkType));
}
- while (readNumberC<bool>()) //directories
+ while (readPOD<bool>()) //directories
{
const Zstring shortName = readStringUtf8(); //directory name
DirContainer& subDir = dirCont.addSubDir(shortName);
@@ -201,29 +227,28 @@ void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw
6 1,77 MB - 613 ms
9 (maximal compression) 1,74 MB - 3330 ms */
- CheckedWriter cw(compressed, filename);
+ CheckedDbWriter cw(compressed, filename);
//save file format version
- cw.writeNumberC<std::int32_t>(FILE_FORMAT_VER);
+ cw.writePOD<std::int32_t>(FILE_FORMAT_VER);
//save stream list
- cw.writeNumberC<std::uint32_t>(static_cast<std::uint32_t>(streamList.size())); //number of database records: one for each sync-pair
+ cw.writePOD<std::uint32_t>(static_cast<std::uint32_t>(streamList.size())); //number of database records: one for each sync-pair
for (auto iter = streamList.begin(); iter != streamList.end(); ++iter)
{
- cw.writeStringC<std::string >(iter->first ); //sync session id
- cw.writeStringC<MemoryStream>(iter->second); //DirInformation stream
+ cw.writeString<std::string >(iter->first ); //sync session id
+ cw.writeString<MemoryStream>(iter->second); //DirInformation stream
}
}
- //(try to) hide database file
#ifdef FFS_WIN
- ::SetFileAttributes(zen::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN);
+ ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file
#endif
}
template <SelectedSide side>
-class StreamGenerator : private CheckedWriter
+class StreamGenerator : private CheckedDbWriter
{
public:
static MemoryStream execute(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName)
@@ -238,7 +263,7 @@ public:
}
private:
- StreamGenerator(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName, wxOutputStream& stream) : CheckedWriter(stream, errorObjName)
+ StreamGenerator(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName, wxOutputStream& stream) : CheckedDbWriter(stream, errorObjName)
{
recurse(baseMapping, oldDirInfo);
}
@@ -247,32 +272,36 @@ private:
{
// for (const auto& fileMap : hierObj.refSubFiles()) { processFile(fileMap, oldDirInfo); }); !
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](const FileMapping& fileMap) { this->processFile(fileMap, oldDirInfo); });
- writeNumberC<bool>(false); //mark last entry
+ std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](const FileMapping& fileMap) { this->processFile(fileMap, oldDirInfo); });
+ writePOD<bool>(false); //mark last entry
std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](const SymLinkMapping& linkObj) { this->processLink(linkObj, oldDirInfo); });
- writeNumberC<bool>(false); //mark last entry
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](const DirMapping& dirMap) { this->processDir(dirMap, oldDirInfo); });
- writeNumberC<bool>(false); //mark last entry
+ writePOD<bool>(false); //mark last entry
+ std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](const DirMapping& dirMap) { this->processDir (dirMap, oldDirInfo); });
+ writePOD<bool>(false); //mark last entry
}
- void writeStringUtf8(const Zstring& str) { writeStringC(utf8CvrtTo<Zbase<char>>(str)); }
+ void writeStringUtf8(const Zstring& str) { writeString(utf8CvrtTo<Zbase<char>>(str)); }
void writeFileId(const FileId& id)
{
- writeNumberC<std::uint64_t>(id.first ); //device id
- writeNumberC<std::uint64_t>(id.second); //file id
+ writePOD<std::uint64_t>(id.first ); //device id
+ writePOD<std::uint64_t>(id.second); //file id
}
+#ifdef _MSC_VER
+ warn_static("support multiple folder pairs that differ in hard filter only?")
+#endif
+
void processFile(const FileMapping& fileMap, const DirContainer* oldParentDir)
{
if (fileMap.getCategory() == FILE_EQUAL) //data in sync: write current state
{
if (!fileMap.isEmpty<side>())
{
- writeNumberC<bool>(true); //mark beginning of entry
+ writePOD<bool>(true); //mark beginning of entry
writeStringUtf8(fileMap.getShortName<side>()); //save respecting case! (Windows)
- writeNumberC<std:: int64_t>(to<std:: int64_t>(fileMap.getLastWriteTime<side>()));
- writeNumberC<std::uint64_t>(to<std::uint64_t>(fileMap.getFileSize<side>()));
+ writePOD<std:: int64_t>(to<std:: int64_t>(fileMap.getLastWriteTime<side>()));
+ writePOD<std::uint64_t>(to<std::uint64_t>(fileMap.getFileSize<side>()));
writeFileId(fileMap.getFileId<side>());
}
}
@@ -283,10 +312,10 @@ private:
auto iter = oldParentDir->files.find(fileMap.getObjShortName());
if (iter != oldParentDir->files.end())
{
- writeNumberC<bool>(true); //mark beginning of entry
+ writePOD<bool>(true); //mark beginning of entry
writeStringUtf8(iter->first); //save respecting case! (Windows)
- writeNumberC<std:: int64_t>(to<std:: int64_t>(iter->second.lastWriteTimeRaw));
- writeNumberC<std::uint64_t>(to<std::uint64_t>(iter->second.fileSize));
+ writePOD<std:: int64_t>(to<std:: int64_t>(iter->second.lastWriteTimeRaw));
+ writePOD<std::uint64_t>(to<std::uint64_t>(iter->second.fileSize));
writeFileId(iter->second.id);
}
}
@@ -299,11 +328,11 @@ private:
{
if (!linkObj.isEmpty<side>())
{
- writeNumberC<bool>(true); //mark beginning of entry
+ writePOD<bool>(true); //mark beginning of entry
writeStringUtf8(linkObj.getShortName<side>()); //save respecting case! (Windows)
- writeNumberC<std::int64_t>(to<std::int64_t>(linkObj.getLastWriteTime<side>()));
+ writePOD<std::int64_t>(to<std::int64_t>(linkObj.getLastWriteTime<side>()));
writeStringUtf8(linkObj.getTargetPath<side>());
- writeNumberC<std::int32_t>(linkObj.getLinkType<side>());
+ writePOD<std::int32_t>(linkObj.getLinkType<side>());
}
}
else //not in sync: reuse last synchronous state
@@ -313,11 +342,11 @@ private:
auto iter = oldParentDir->links.find(linkObj.getObjShortName());
if (iter != oldParentDir->links.end())
{
- writeNumberC<bool>(true); //mark beginning of entry
+ writePOD<bool>(true); //mark beginning of entry
writeStringUtf8(iter->first); //save respecting case! (Windows)
- writeNumberC<std::int64_t>(to<std::int64_t>(iter->second.lastWriteTimeRaw));
+ writePOD<std::int64_t>(to<std::int64_t>(iter->second.lastWriteTimeRaw));
writeStringUtf8(iter->second.targetPath);
- writeNumberC<std::int32_t>(iter->second.type);
+ writePOD<std::int32_t>(iter->second.type);
}
}
}
@@ -325,8 +354,8 @@ private:
void processDir(const DirMapping& dirMap, const DirContainer* oldParentDir)
{
- const DirContainer* oldDir = NULL;
- const Zstring* oldDirName = NULL;
+ const DirContainer* oldDir = nullptr;
+ const Zstring* oldDirName = nullptr;
if (oldParentDir) //no data is also a "synchronous state"!
{
auto iter = oldParentDir->dirs.find(dirMap.getObjShortName());
@@ -343,7 +372,7 @@ private:
{
if (!dirMap.isEmpty<side>())
{
- writeNumberC<bool>(true); //mark beginning of entry
+ writePOD<bool>(true); //mark beginning of entry
writeStringUtf8(dirMap.getShortName<side>()); //save respecting case! (Windows)
recurse(dirMap, oldDir);
}
@@ -352,7 +381,7 @@ private:
{
if (oldDir)
{
- writeNumberC<bool>(true); //mark beginning of entry
+ writePOD<bool>(true); //mark beginning of entry
writeStringUtf8(*oldDirName); //save respecting case! (Windows)
recurse(dirMap, oldDir);
return;
@@ -372,7 +401,7 @@ private:
assert(false);
break;
case DIR_DIFFERENT_METADATA:
- writeNumberC<bool>(true);
+ writePOD<bool>(true);
writeStringUtf8(dirMap.getShortName<side>());
//ATTENTION: strictly this is a violation of the principle of reporting last synchronous state!
//however in this case this will result in "last sync unsuccessful" for this directory within <automatic> algorithm, which is fine
@@ -396,30 +425,23 @@ std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMa
const StreamMapping streamListRight = ::loadStreams(fileNameRight); //throw FileError
//find associated session: there can be at most one session within intersection of left and right ids
- StreamMapping::const_iterator streamLeft = streamListLeft .end();
- StreamMapping::const_iterator streamRight = streamListRight.end();
for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft)
{
auto iterRight = streamListRight.find(iterLeft->first);
if (iterRight != streamListRight.end())
{
- streamLeft = iterLeft;
- streamRight = iterRight;
- break;
+ //read streams into DirInfo
+ DirInfoPtr dirInfoLeft = StreamParser::execute(iterLeft ->second, fileNameLeft); //throw FileError
+ DirInfoPtr dirInfoRight = StreamParser::execute(iterRight->second, fileNameRight); //throw FileError
+
+ return std::make_pair(dirInfoLeft, dirInfoRight);
}
}
- if (streamLeft == streamListLeft .end() ||
- streamRight == streamListRight.end())
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
- _("Database files do not share a common synchronization session:") + L" \n" +
- L"\"" + fileNameLeft + L"\"\n" +
- L"\"" + fileNameRight + L"\"");
- //read streams into DirInfo
- DirInfoPtr dirInfoLeft = StreamParser::execute(streamLeft ->second, fileNameLeft); //throw FileError
- DirInfoPtr dirInfoRight = StreamParser::execute(streamRight->second, fileNameRight); //throw FileError
-
- return std::make_pair(dirInfoLeft, dirInfoRight);
+ throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
+ _("Database files do not share a common synchronization session:") + L" \n" +
+ L"\"" + fileNameLeft + L"\"\n" +
+ L"\"" + fileNameRight + L"\"");
}
@@ -440,75 +462,65 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw FileError
StreamMapping streamListLeft;
StreamMapping streamListRight;
- try //read file data: list of session ID + DirInfo-stream
- {
- streamListLeft = ::loadStreams(dbNameLeft);
- }
- catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing!
- try
- {
- streamListRight = ::loadStreams(dbNameRight);
- }
+ //read file data: list of session ID + DirInfo-stream
+ try { streamListLeft = ::loadStreams(dbNameLeft ); }
catch (FileError&) {}
+ try { streamListRight = ::loadStreams(dbNameRight); }
+ catch (FileError&) {}
+ //if error occurs: just overwrite old file! User is already informed about issues right after comparing!
//find associated session: there can be at most one session within intersection of left and right ids
- StreamMapping::iterator streamLeft = streamListLeft .end();
- StreamMapping::iterator streamRight = streamListRight.end();
+ auto streamLeftOld = streamListLeft .cend();
+ auto streamRightOld = streamListRight.cend();
for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft)
{
auto iterRight = streamListRight.find(iterLeft->first);
if (iterRight != streamListRight.end())
{
- streamLeft = iterLeft;
- streamRight = iterRight;
+ streamLeftOld = iterLeft;
+ streamRightOld = iterRight;
break;
}
}
//(try to) read old DirInfo
- DirInfoPtr oldDirInfoLeft;
- DirInfoPtr oldDirInfoRight;
- try
- {
- if (streamLeft != streamListLeft .end() &&
- streamRight != streamListRight.end())
+ DirInfoPtr dirInfoLeftOld;
+ DirInfoPtr dirInfoRightOld;
+ if (streamLeftOld != streamListLeft .end() &&
+ streamRightOld != streamListRight.end())
+ try
{
- oldDirInfoLeft = StreamParser::execute(streamLeft ->second, dbNameLeft ); //throw FileError
- oldDirInfoRight = StreamParser::execute(streamRight->second, dbNameRight); //throw FileError
+ dirInfoLeftOld = StreamParser::execute(streamLeftOld ->second, dbNameLeft ); //throw FileError
+ dirInfoRightOld = StreamParser::execute(streamRightOld->second, dbNameRight); //throw FileError
+ }
+ catch (FileError&)
+ {
+ //if error occurs: just overwrite old file! User is already informed about issues right after comparing!
+ dirInfoLeftOld .reset(); //read both or none!
+ dirInfoRightOld.reset(); //
}
- }
- catch (FileError&)
- {
- //if error occurs: just overwrite old file! User is already informed about issues right after comparing!
- oldDirInfoLeft .reset(); //read both or none!
- oldDirInfoRight.reset(); //
- }
//create new database entries
- MemoryStream newStreamLeft = StreamGenerator<LEFT_SIDE >::execute(baseMapping, oldDirInfoLeft .get() ? &oldDirInfoLeft ->baseDirContainer : NULL, dbNameLeft);
- MemoryStream newStreamRight = StreamGenerator<RIGHT_SIDE>::execute(baseMapping, oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL, dbNameRight);
+ MemoryStream rawStreamLeftNew = StreamGenerator<LEFT_SIDE >::execute(baseMapping, dirInfoLeftOld .get() ? &dirInfoLeftOld ->baseDirContainer : nullptr, dbNameLeft);
+ MemoryStream rawStreamRightNew = StreamGenerator<RIGHT_SIDE>::execute(baseMapping, dirInfoRightOld.get() ? &dirInfoRightOld->baseDirContainer : nullptr, dbNameRight);
//check if there is some work to do at all
- {
- const bool updateRequiredLeft = streamLeft == streamListLeft .end() || newStreamLeft != streamLeft ->second;
- const bool updateRequiredRight = streamRight == streamListRight.end() || newStreamRight != streamRight->second;
- //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed
- if (!updateRequiredLeft && !updateRequiredRight)
- return;
- }
-
- //create/update DirInfo-streams
- std::string sessionID = zen::generateGUID();
+ if (streamLeftOld != streamListLeft .end() && rawStreamLeftNew == streamLeftOld ->second &&
+ streamRightOld != streamListRight.end() && rawStreamRightNew == streamRightOld->second)
+ return; //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed
//erase old session data
- if (streamLeft != streamListLeft.end())
- streamListLeft.erase(streamLeft);
- if (streamRight != streamListRight.end())
- streamListRight.erase(streamRight);
+ if (streamLeftOld != streamListLeft.end())
+ streamListLeft.erase(streamLeftOld);
+ if (streamRightOld != streamListRight.end())
+ streamListRight.erase(streamRightOld);
+
+ //create/update DirInfo-streams
+ const std::string sessionID = zen::generateGUID();
//fill in new
- streamListLeft .insert(std::make_pair(sessionID, newStreamLeft));
- streamListRight.insert(std::make_pair(sessionID, newStreamRight));
+ streamListLeft .insert(std::make_pair(sessionID, rawStreamLeftNew));
+ streamListRight.insert(std::make_pair(sessionID, rawStreamRightNew));
//write (temp-) files...
zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); });
bgstack15