summaryrefslogtreecommitdiff
path: root/fileHierarchy.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:05:53 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:05:53 +0200
commit618dfb51d93898632830f1b87443d3f748780871 (patch)
treebac520a2e261154f8d35b0cb8aa345f5ab373811 /fileHierarchy.cpp
parent3.4 (diff)
downloadFreeFileSync-618dfb51d93898632830f1b87443d3f748780871.tar.gz
FreeFileSync-618dfb51d93898632830f1b87443d3f748780871.tar.bz2
FreeFileSync-618dfb51d93898632830f1b87443d3f748780871.zip
3.5
Diffstat (limited to 'fileHierarchy.cpp')
-rw-r--r--fileHierarchy.cpp406
1 files changed, 14 insertions, 392 deletions
diff --git a/fileHierarchy.cpp b/fileHierarchy.cpp
index 25ee618d..dc616db0 100644
--- a/fileHierarchy.cpp
+++ b/fileHierarchy.cpp
@@ -5,24 +5,9 @@
// **************************************************************************
//
#include "fileHierarchy.h"
-#include <wx/wfstream.h>
-#include <wx/zstream.h>
-#include "shared/globalFunctions.h"
-#include "shared/fileError.h"
-#include <wx/intl.h>
-#include "shared/stringConv.h"
-#include "shared/fileHandling.h"
-#include <wx/mstream.h>
-#include "shared/serialize.h"
#include "shared/buildInfo.h"
-#ifdef FFS_WIN
-#include <wx/msw/wrapwin.h> //includes "windows.h"
-#include "shared/longPathPrefix.h"
-#endif
-
using namespace FreeFileSync;
-using namespace globalFunctions;
struct LowerID
@@ -90,12 +75,12 @@ struct IsInvalid
void FileSystemObject::removeEmptyNonRec(HierarchyObject& hierObj)
{
//remove invalid files
- hierObj.subFiles.erase(std::remove_if(hierObj.subFiles.begin(), hierObj.subFiles.end(), IsInvalid()),
- hierObj.subFiles.end());
+ hierObj.useSubFiles().erase(std::remove_if(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), IsInvalid()),
+ hierObj.useSubFiles().end());
//remove invalid directories
- hierObj.subDirs.erase(std::remove_if(hierObj.subDirs.begin(), hierObj.subDirs.end(), IsInvalid()),
- hierObj.subDirs.end());
+ hierObj.useSubDirs().erase(std::remove_if(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), IsInvalid()),
+ hierObj.useSubDirs().end());
}
@@ -104,7 +89,7 @@ void removeEmptyRec(HierarchyObject& hierObj)
FileSystemObject::removeEmptyNonRec(hierObj);
//recurse into remaining directories
- std::for_each(hierObj.subDirs.begin(), hierObj.subDirs.end(), removeEmptyRec);
+ std::for_each(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), removeEmptyRec);
}
@@ -191,382 +176,19 @@ SyncOperation FileSystemObject::getSyncOperation(const CompareFilesResult cmpRes
}
-//-------------------------------------------------------------------------------------------------
const Zstring& FreeFileSync::getSyncDBFilename()
{
+ //32 and 64 bit for Linux and Windows builds are binary incompatible! So give them different names
+ //make sure they end with ".ffs_db". These files will not be included into comparison when located in base sync directories
#ifdef FFS_WIN
- static Zstring output(DefaultStr("sync.ffs_db"));
+ static Zstring output(Utility::is64BitBuild ?
+ DefaultStr("sync.x64.ffs_db") :
+ DefaultStr("sync.ffs_db"));
#elif defined FFS_LINUX
- static Zstring output(DefaultStr(".sync.ffs_db")); //files beginning with dots are hidden e.g. in Nautilus
+ //files beginning with dots are hidden e.g. in Nautilus
+ static Zstring output(Utility::is64BitBuild ?
+ DefaultStr(".sync.x64.ffs_db") :
+ DefaultStr(".sync.ffs_db"));
#endif
return output;
}
-
-
-//-------------------------------------------------------------------------------------------------------------------------------
-const char FILE_FORMAT_DESCR[] = "FreeFileSync";
-const int FILE_FORMAT_VER = Utility::is64BitBuild ? -3 : 3; //32 and 64 bit builds are binary incompatible! So give them different IDs
-//-------------------------------------------------------------------------------------------------------------------------------
-
-
-class ReadDirInfo : public Utility::ReadInputStream
-{
-public:
- ReadDirInfo(wxInputStream& stream, const wxString& errorObjName, DirInformation& dirInfo) : ReadInputStream(stream, errorObjName)
- {
- //read filter settings
- dirInfo.filter = BaseFilter::loadFilter(stream_);
- check();
-
- //start recursion
- execute(dirInfo.baseDirContainer);
- }
-
-private:
- void execute(DirContainer& dirCont)
- {
- unsigned int fileCount = readNumberC<unsigned int>();
- while (fileCount-- != 0)
- readSubFile(dirCont);
-
- unsigned int dirCount = readNumberC<unsigned int>();
- while (dirCount-- != 0)
- readSubDirectory(dirCont);
- }
-
- void readSubFile(DirContainer& dirCont)
- {
- //attention: order of function argument evaluation is undefined! So do it one after the other...
- const Zstring shortName = readStringC(); //file name
-
- const long modHigh = readNumberC<long>();
- const unsigned long modLow = readNumberC<unsigned long>();
-
- const unsigned long sizeHigh = readNumberC<unsigned long>();
- const unsigned long sizeLow = readNumberC<unsigned long>();
-
- //const Utility::FileID fileIdentifier(stream_);
- //check();
-
- dirCont.addSubFile(shortName,
- FileDescriptor(wxLongLong(modHigh, modLow),
- wxULongLong(sizeHigh, sizeLow)));
- }
-
- void readSubDirectory(DirContainer& dirCont)
- {
- const Zstring shortName = readStringC(); //directory name
- DirContainer& subDir = dirCont.addSubDir(shortName);
- execute(subDir); //recurse
- }
-};
-
-
-typedef boost::shared_ptr<std::vector<char> > MemoryStreamPtr; //byte stream representing DirInformation
-typedef std::map<Utility::UniqueId, MemoryStreamPtr> DirectoryTOC; //list of streams ordered by a UUID pointing to their partner database
-typedef std::pair<Utility::UniqueId, DirectoryTOC> DbStreamData; //header data: UUID representing this database, item data: list of dir-streams
-/* Example
-left side right side
---------- ----------
-DB-ID 123 <-\ /-> DB-ID 567
- \/
-Partner-ID 111 /\ Partner-ID 222
-Partner-ID 567 _/ \_ Partner-ID 123
- ... ...
-*/
-
-class ReadFileStream : public Utility::ReadInputStream
-{
-public:
- ReadFileStream(wxInputStream& stream, const wxString& filename, DbStreamData& output) : ReadInputStream(stream, filename)
- {
- if (readNumberC<int>() != FILE_FORMAT_VER) //read file format version
- throw FileError(wxString(_("Incompatible synchronization database format:")) + wxT(" \n") + wxT("\"") + filename + wxT("\""));
-
- //read DB id
- output.first = Utility::UniqueId(stream_);
- check();
-
- DirectoryTOC& dbList = output.second;
- dbList.clear();
-
- size_t dbCount = readNumberC<size_t>(); //number of databases: one for each sync-pair
- while (dbCount-- != 0)
- {
- const Utility::UniqueId partnerID(stream_); //DB id of partner databases
- check();
-
- CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation)
-
- dbList.insert(std::make_pair(partnerID, buffer));
- }
- }
-};
-
-
-DbStreamData loadFile(const Zstring& filename) //throw (FileError)
-{
- if (!FreeFileSync::fileExists(filename))
- throw FileError(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
- _("One of the FreeFileSync database files is not yet existing:") + wxT(" \n") +
- wxT("\"") + zToWx(filename) + wxT("\""));
-
-
- //read format description (uncompressed)
- wxFFileInputStream uncompressed(zToWx(filename), wxT("rb"));
-
- char formatDescr[sizeof(FILE_FORMAT_DESCR)];
- uncompressed.Read(formatDescr, sizeof(formatDescr));
- formatDescr[sizeof(FILE_FORMAT_DESCR) - 1] = 0;
- if (uncompressed.GetLastError() != wxSTREAM_NO_ERROR)
- throw FileError(wxString(_("Error reading from synchronization database:")) + wxT(" \n") + wxT("\"") + zToWx(filename) + wxT("\""));
- if (std::string(formatDescr) != FILE_FORMAT_DESCR)
- throw FileError(wxString(_("Incompatible synchronization database format:")) + wxT(" \n") + wxT("\"") + zToWx(filename) + wxT("\""));
-
-
- wxZlibInputStream input(uncompressed, wxZLIB_ZLIB);
-
- DbStreamData output;
- ReadFileStream(input, zToWx(filename), output);
- return output;
-}
-
-
-std::pair<DirInfoPtr, DirInfoPtr> FreeFileSync::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError)
-{
- const Zstring fileNameLeft = baseMapping.getDBFilename<LEFT_SIDE>();
- const Zstring fileNameRight = baseMapping.getDBFilename<RIGHT_SIDE>();
-
- //read file data: db ID + mapping of partner-ID/DirInfo-stream
- const DbStreamData dbEntriesLeft = ::loadFile(fileNameLeft);
- const DbStreamData dbEntriesRight = ::loadFile(fileNameRight);
-
- //find associated DirInfo-streams
- DirectoryTOC::const_iterator dbLeft = dbEntriesLeft.second.find(dbEntriesRight.first); //find left db-entry that corresponds to right database
- if (dbLeft == dbEntriesLeft.second.end())
- throw FileError(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
- _("One of the FreeFileSync database entries within the following file is not yet existing:") + wxT(" \n") +
- wxT("\"") + zToWx(fileNameLeft) + wxT("\""));
-
- DirectoryTOC::const_iterator dbRight = dbEntriesRight.second.find(dbEntriesLeft.first); //find left db-entry that corresponds to right database
- if (dbRight == dbEntriesRight.second.end())
- throw FileError(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
- _("One of the FreeFileSync database entries within the following file is not yet existing:") + wxT(" \n") +
- wxT("\"") + zToWx(fileNameRight) + wxT("\""));
-
- //read streams into DirInfo
- boost::shared_ptr<DirInformation> dirInfoLeft(new DirInformation);
- wxMemoryInputStream buffer(&(*dbLeft->second)[0], dbLeft->second->size()); //convert char-array to inputstream: no copying, ownership not transferred
- ReadDirInfo(buffer, zToWx(fileNameLeft), *dirInfoLeft); //read file/dir information
-
- boost::shared_ptr<DirInformation> dirInfoRight(new DirInformation);
- wxMemoryInputStream buffer2(&(*dbRight->second)[0], dbRight->second->size()); //convert char-array to inputstream: no copying, ownership not transferred
- ReadDirInfo(buffer2, zToWx(fileNameRight), *dirInfoRight); //read file/dir information
-
- return std::make_pair(dirInfoLeft, dirInfoRight);
-}
-
-
-//-------------------------------------------------------------------------------------------------------------------------
-
-template <SelectedSide side>
-struct IsNonEmpty
-{
- bool operator()(const FileSystemObject& fsObj) const
- {
- return !fsObj.isEmpty<side>();
- }
-};
-
-
-template <SelectedSide side>
-class SaveDirInfo : public Utility::WriteOutputStream
-{
-public:
- SaveDirInfo(const BaseDirMapping& baseMapping, const wxString& errorObjName, wxOutputStream& stream) : WriteOutputStream(errorObjName, stream)
- {
- //save filter settings
- baseMapping.getFilter()->saveFilter(stream_);
- check();
-
- //start recursion
- execute(baseMapping);
- }
-
-private:
- template<typename Iterator, typename Function>
- friend Function std::for_each(Iterator, Iterator, Function);
-
- void execute(const HierarchyObject& hierObj)
- {
- writeNumberC<unsigned int>(std::count_if(hierObj.subFiles.begin(), hierObj.subFiles.end(), IsNonEmpty<side>())); //number of (existing) files
- std::for_each(hierObj.subFiles.begin(), hierObj.subFiles.end(), *this);
-
- writeNumberC<unsigned int>(std::count_if(hierObj.subDirs.begin(), hierObj.subDirs.end(), IsNonEmpty<side>())); //number of (existing) directories
- std::for_each(hierObj.subDirs.begin(), hierObj.subDirs.end(), *this);
- }
-
- void operator()(const FileMapping& fileMap)
- {
- if (!fileMap.isEmpty<side>())
- {
- writeStringC(fileMap.getObjShortName()); //file name
- writeNumberC<long>( fileMap.getLastWriteTime<side>().GetHi()); //last modification time
- writeNumberC<unsigned long>(fileMap.getLastWriteTime<side>().GetLo()); //
- writeNumberC<unsigned long>(fileMap.getFileSize<side>().GetHi()); //filesize
- writeNumberC<unsigned long>(fileMap.getFileSize<side>().GetLo()); //
-
- //fileMap.getFileID<side>().toStream(stream_); //unique file identifier
- //check();
- }
- }
-
- void operator()(const DirMapping& dirMap)
- {
- if (!dirMap.isEmpty<side>())
- {
- writeStringC(dirMap.getObjShortName()); //directory name
- execute(dirMap); //recurse
- }
- }
-};
-
-
-class WriteFileStream : public Utility::WriteOutputStream
-{
-public:
- WriteFileStream(const DbStreamData& input, const wxString& filename, wxOutputStream& stream) : WriteOutputStream(filename, stream)
- {
- //save file format version
- writeNumberC<int>(FILE_FORMAT_VER);
-
- //write DB id
- input.first.toStream(stream_);
- check();
-
- const DirectoryTOC& dbList = input.second;
-
- writeNumberC<size_t>(dbList.size()); //number of database records: one for each sync-pair
-
- for (DirectoryTOC::const_iterator i = dbList.begin(); i != dbList.end(); ++i)
- {
- i->first.toStream(stream_); //DB id of partner database
- check();
-
- writeArrayC(*(i->second)); //write DirInformation stream
- }
- }
-};
-
-
-//save/load DirContainer
-void saveFile(const DbStreamData& dbStream, const Zstring& filename) //throw (FileError)
-{
- //write format description (uncompressed)
- wxFFileOutputStream uncompressed(zToWx(filename), wxT("wb"));
- uncompressed.Write(FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR));
- if (uncompressed.GetLastError() != wxSTREAM_NO_ERROR)
- throw FileError(wxString(_("Error writing to synchronization database:")) + wxT(" \n") +
- wxT("\"") + zToWx(filename) + wxT("\""));
-
- wxZlibOutputStream output(uncompressed, 4, wxZLIB_ZLIB);
- /* 4 - best compromise between speed and compression: (scanning 200.000 objects)
- 0 (uncompressed) 8,95 MB - 422 ms
- 2 2,07 MB - 470 ms
- 4 1,87 MB - 500 ms
- 6 1,77 MB - 613 ms
- 9 (maximal compression) 1,74 MB - 3330 ms */
-
- WriteFileStream(dbStream, zToWx(filename), output);
-
- //(try to) hide database file
-#ifdef FFS_WIN
- output.Close();
- ::SetFileAttributes(FreeFileSync::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN);
-#endif
-}
-
-
-void FreeFileSync::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError)
-{
- //transactional behaviour! write to tmp files first
- const Zstring fileNameLeftTmp = baseMapping.getDBFilename<LEFT_SIDE>() + DefaultStr(".tmp");
- const Zstring fileNameRightTmp = baseMapping.getDBFilename<RIGHT_SIDE>() + DefaultStr(".tmp");;
-
- //delete old tmp file, if necessary -> throws if deletion fails!
- removeFile(fileNameLeftTmp);
- removeFile(fileNameRightTmp);
-
- try
- {
- //load old database files...
-
- //read file data: db ID + mapping of partner-ID/DirInfo-stream: may throw!
- DbStreamData dbEntriesLeft;
- if (FreeFileSync::fileExists(baseMapping.getDBFilename<LEFT_SIDE>()))
- try
- {
- dbEntriesLeft = ::loadFile(baseMapping.getDBFilename<LEFT_SIDE>());
- }
- catch(FileError&) {} //if error occurs: just overwrite old file! User is informed about issues right after comparing!
- //else -> dbEntriesLeft has empty mapping, but already a DB-ID!
-
- //read file data: db ID + mapping of partner-ID/DirInfo-stream: may throw!
- DbStreamData dbEntriesRight;
- if (FreeFileSync::fileExists(baseMapping.getDBFilename<RIGHT_SIDE>()))
- try
- {
- dbEntriesRight = ::loadFile(baseMapping.getDBFilename<RIGHT_SIDE>());
- }
- catch(FileError&) {} //if error occurs: just overwrite old file! User is informed about issues right after comparing!
-
- //create new database entries
- MemoryStreamPtr dbEntryLeft(new std::vector<char>);
- {
- wxMemoryOutputStream buffer;
- SaveDirInfo<LEFT_SIDE>(baseMapping, zToWx(baseMapping.getDBFilename<LEFT_SIDE>()), buffer);
- dbEntryLeft->resize(buffer.GetSize()); //convert output stream to char-array
- buffer.CopyTo(&(*dbEntryLeft)[0], buffer.GetSize()); //
- }
-
- MemoryStreamPtr dbEntryRight(new std::vector<char>);
- {
- wxMemoryOutputStream buffer;
- SaveDirInfo<RIGHT_SIDE>(baseMapping, zToWx(baseMapping.getDBFilename<RIGHT_SIDE>()), buffer);
- dbEntryRight->resize(buffer.GetSize()); //convert output stream to char-array
- buffer.CopyTo(&(*dbEntryRight)[0], buffer.GetSize()); //
- }
-
- //create/update DirInfo-streams
- dbEntriesLeft.second[dbEntriesRight.first] = dbEntryLeft;
- dbEntriesRight.second[dbEntriesLeft.first] = dbEntryRight;
-
- //write (temp-) files...
- saveFile(dbEntriesLeft, fileNameLeftTmp); //throw (FileError)
- saveFile(dbEntriesRight, fileNameRightTmp); //throw (FileError)
-
- //operation finished: rename temp files -> this should work transactionally:
- //if there were no write access, creation of temp files would have failed
- removeFile(baseMapping.getDBFilename<LEFT_SIDE>());
- removeFile(baseMapping.getDBFilename<RIGHT_SIDE>());
- renameFile(fileNameLeftTmp, baseMapping.getDBFilename<LEFT_SIDE>()); //throw (FileError);
- renameFile(fileNameRightTmp, baseMapping.getDBFilename<RIGHT_SIDE>()); //throw (FileError);
- }
- catch (...)
- {
- try //clean up: (try to) delete old tmp files
- {
- removeFile(fileNameLeftTmp);
- }
- catch (...) {}
- try
- {
- removeFile(fileNameRightTmp);
- }
- catch (...) {}
-
- throw;
- }
-}
-
-
bgstack15