// ************************************************************************** // * 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 FILEHIERARCHY_H_INCLUDED #define FILEHIERARCHY_H_INCLUDED #include #include #include #include #include #include #include #include "structures.h" #include #include #include "structures.h" #include "lib/hard_filter.h" namespace zen { struct FileDescriptor { FileDescriptor() : fileIdx(), devId(), isFollowedSymlink() {} FileDescriptor(const Int64& lastWriteTimeRawIn, const UInt64& fileSizeIn, const FileId& idIn, bool isSymlink) : lastWriteTimeRaw(lastWriteTimeRawIn), fileSize(fileSizeIn), fileIdx(idIn.second), devId(idIn.first), isFollowedSymlink(isSymlink) {} Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) UInt64 fileSize; FileIndex fileIdx; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) DeviceId devId; //split into file id into components to avoid padding overhead of a std::pair! bool isFollowedSymlink; }; inline FileId getFileId(const FileDescriptor& fd) { return FileId(fd.devId, fd.fileIdx); } struct LinkDescriptor { LinkDescriptor() {} explicit LinkDescriptor(const Int64& lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {} Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) }; enum SelectedSide { LEFT_SIDE, RIGHT_SIDE }; template struct OtherSide; template <> struct OtherSide { static const SelectedSide result = RIGHT_SIDE; }; template <> struct OtherSide { static const SelectedSide result = LEFT_SIDE; }; template struct SelectParam; template <> struct SelectParam { template static T& get(T& left, T& right) { return left; } }; template <> struct SelectParam { template static T& get(T& left, T& right) { return right; } }; class BaseDirPair; class DirPair; class FilePair; class SymlinkPair; class FileSystemObject; //------------------------------------------------------------------ /* ERD: DirContainer 1 --> 0..n DirContainer DirContainer 1 --> 0..n FileDescriptor DirContainer 1 --> 0..n LinkDescriptor */ struct DirContainer { //------------------------------------------------------------------ typedef std::map DirList; // typedef std::map FileList; //key: shortName typedef std::map LinkList; // //------------------------------------------------------------------ DirList dirs; FileList files; LinkList links; //non-followed symlinks //convenience DirContainer& addSubDir(const Zstring& shortName) { //use C++11 emplace when available return dirs[shortName]; //value default-construction is okay here //return dirs.insert(std::make_pair(shortName, DirContainer())).first->second; } void addSubFile(const Zstring& shortName, const FileDescriptor& fileData) { files.insert(std::make_pair(shortName, fileData)); } void addSubLink(const Zstring& shortName, const LinkDescriptor& linkData) { links.insert(std::make_pair(shortName, linkData)); } }; /*------------------------------------------------------------------ inheritance diagram: ObjectMgr /|\ | FileSystemObject HierarchyObject /|\ /|\ _______________|______________ ______|______ | | | | | SymlinkPair FilePair DirPair BaseDirPair ------------------------------------------------------------------*/ class HierarchyObject { friend class DirPair; friend class FileSystemObject; public: typedef zen::FixedList SubFileVec; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() typedef zen::FixedList SubLinkVec; //Note: deque<> has circular dependency in VCPP! typedef zen::FixedList SubDirVec; DirPair& addSubDir(const Zstring& shortNameLeft, const Zstring& shortNameRight, CompareDirResult defaultCmpResult); template DirPair& addSubDir(const Zstring& shortName); //dir exists on one side only FilePair& addSubFile(const Zstring& shortNameLeft, const FileDescriptor& left, //file exists on both sides CompareFilesResult defaultCmpResult, const Zstring& shortNameRight, const FileDescriptor& right); template FilePair& addSubFile(const Zstring& shortName, //file exists on one side only const FileDescriptor& descr); SymlinkPair& addSubLink(const Zstring& shortNameLeft, const LinkDescriptor& left, //link exists on both sides CompareSymlinkResult defaultCmpResult, const Zstring& shortNameRight, const LinkDescriptor& right); template SymlinkPair& addSubLink(const Zstring& shortName, //link exists on one side only const LinkDescriptor& descr); const SubFileVec& refSubFiles() const { return subFiles; } /**/ SubFileVec& refSubFiles() { return subFiles; } const SubLinkVec& refSubLinks() const { return subLinks; } /**/ SubLinkVec& refSubLinks() { return subLinks; } const SubDirVec& refSubDirs() const { return subDirs; } /**/ SubDirVec& refSubDirs() { return subDirs; } BaseDirPair& getRoot() { return root_; } const Zstring& getObjRelativeNamePf() const { return objRelNamePf; } //postfixed or empty! protected: HierarchyObject(const Zstring& relativeNamePf, BaseDirPair& baseDirObj) : objRelNamePf(relativeNamePf), root_(baseDirObj) {} ~HierarchyObject() {} //don't need polymorphic deletion virtual void flip(); void removeEmptyRec(); private: virtual void notifySyncCfgChanged() {} HierarchyObject(const HierarchyObject&); //this class is referenced by it's child elements => make it non-copyable/movable! HierarchyObject& operator=(const HierarchyObject&); // SubFileVec subFiles; //contained file maps SubLinkVec subLinks; //contained symbolic link maps SubDirVec subDirs; //contained directory maps Zstring objRelNamePf; //postfixed or empty BaseDirPair& root_; }; //------------------------------------------------------------------ class BaseDirPair : public HierarchyObject //synchronization base directory { public: BaseDirPair(const Zstring& dirPostfixedLeft, bool dirExistsLeft, const Zstring& dirPostfixedRight, bool dirExistsRight, const HardFilter::FilterRef& filter, CompareVariant cmpVar, size_t fileTimeTolerance) : #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4355) //"The this pointer is valid only within nonstatic member functions. It cannot be used in the initializer list for a base class." #endif HierarchyObject(Zstring(), *this), #ifdef _MSC_VER #pragma warning(pop) #endif filter_(filter), cmpVar_(cmpVar), fileTimeTolerance_(fileTimeTolerance), baseDirPfL (dirPostfixedLeft ), baseDirPfR (dirPostfixedRight), dirExistsLeft_ (dirExistsLeft ), dirExistsRight_(dirExistsRight) {} template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR (or empty!) static void removeEmpty(BaseDirPair& baseDir) { baseDir.removeEmptyRec(); }; //physically remove all invalid entries (where both sides are empty) recursively template bool isExisting() const; //status of directory existence at the time of comparison! template void setExisting(bool value); //update after creating the directory in FFS //get settings which were used while creating BaseDirPair const HardFilter& getFilter() const { return *filter_; } CompareVariant getCompVariant() const { return cmpVar_; } size_t getFileTimeTolerance() const { return fileTimeTolerance_; } virtual void flip(); private: BaseDirPair(const BaseDirPair&); //this class is referenced by HierarchyObject => make it non-copyable/movable! BaseDirPair& operator=(const BaseDirPair&); // HardFilter::FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files! CompareVariant cmpVar_; size_t fileTimeTolerance_; Zstring baseDirPfL; //base sync dir postfixed Zstring baseDirPfR; // bool dirExistsLeft_; bool dirExistsRight_; }; template <> inline const Zstring& BaseDirPair::getBaseDirPf() const { return baseDirPfL; } template <> inline const Zstring& BaseDirPair::getBaseDirPf() const { return baseDirPfR; } //get rid of shared_ptr indirection template //target object type class DerefIter : public std::iterator { public: DerefIter() {} DerefIter(IterTy it) : iter(it) {} DerefIter(const DerefIter& other) : iter(other.iter) {} DerefIter& operator++() { ++iter; return *this; } DerefIter& operator--() { --iter; return *this; } DerefIter operator++(int) { DerefIter tmp(*this); operator++(); return tmp; } DerefIter operator--(int) { DerefIter tmp(*this); operator--(); return tmp; } inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter - rhs.iter; } inline friend bool operator==(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter == rhs.iter; } inline friend bool operator!=(const DerefIter& lhs, const DerefIter& rhs) { return !(lhs == rhs); } U& operator* () { return **iter; } U* operator->() { return &** iter; } private: IterTy iter; }; typedef std::vector> FolderComparison; //make sure pointers to sub-elements remain valid //don't change this back to std::vector too easily: comparison uses push_back to add entries which may result in a full copy! DerefIter inline begin(FolderComparison& vect) { return vect.begin(); } DerefIter inline end (FolderComparison& vect) { return vect.end (); } DerefIter inline begin(const FolderComparison& vect) { return vect.begin(); } DerefIter inline end (const FolderComparison& vect) { return vect.end (); } //------------------------------------------------------------------ class FSObjectVisitor { public: virtual ~FSObjectVisitor() {} virtual void visit(const FilePair& fileObj) = 0; virtual void visit(const SymlinkPair& linkObj) = 0; virtual void visit(const DirPair& dirObj) = 0; }; //inherit from this class to allow safe random access by id instead of unsafe raw pointer //allow for similar semantics like std::weak_ptr without having to use std::shared_ptr template class ObjectMgr { public: typedef ObjectMgr* ObjectId; typedef const ObjectMgr* ObjectIdConst; ObjectIdConst getId() const { return this; } /**/ ObjectId getId() { return this; } static const T* retrieve(ObjectIdConst id) //returns nullptr if object is not valid anymore { auto iter = activeObjects().find(id); return static_cast(iter == activeObjects().end() ? nullptr : *iter); } static T* retrieve(ObjectId id) { return const_cast(retrieve(static_cast(id))); } protected: ObjectMgr () { activeObjects().insert(this); } ~ObjectMgr() { activeObjects().erase (this); } private: ObjectMgr(const ObjectMgr& rhs); // ObjectMgr& operator=(const ObjectMgr& rhs); //it's not well-defined what coping an objects means regarding object-identity in this context static zen::hash_set& activeObjects() { static zen::hash_set inst; return inst; } //external linkage (even in header file!) }; //------------------------------------------------------------------ class FileSystemObject : public ObjectMgr { public: virtual void accept(FSObjectVisitor& visitor) const = 0; Zstring getObjShortName () const; //same as getShortName() but also returns value if either side is empty Zstring getObjRelativeName() const; //same as getRelativeName() but also returns value if either side is empty template bool isEmpty() const; template const Zstring& getShortName() const; //case sensitive! template Zstring getRelativeName() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR prefix template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR template Zstring getFullName() const; //getFullName() == getBaseDirPf() + getRelativeName() //comparison result CompareFilesResult getCategory() const { return cmpResult; } std::wstring getCatExtraDescription() const; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA //sync settings SyncDirection getSyncDir() const; void setSyncDir(SyncDirection newDir); void setSyncDirConflict(const std::wstring& description); //set syncDir = SyncDirection::NONE + fill conflict description bool isActive() const; void setActive(bool active); //sync operation virtual SyncOperation testSyncOperation(SyncDirection testSyncDir) const; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! virtual SyncOperation getSyncOperation() const; std::wstring getSyncOpConflict() const; //return conflict when determining sync direction or (still unresolved) conflict during categorization template void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion bool isEmpty() const; //true, if both sides are empty const HierarchyObject& parent() const { return parent_; } /**/ HierarchyObject& parent() { return parent_; } const BaseDirPair& root() const { return parent_.getRoot(); } /**/ BaseDirPair& root() { return parent_.getRoot(); } //for use during init in "CompareProcess" only: template void setCategory(); void setCategoryConflict(const std::wstring& description); void setCategoryDiffMetadata(const std::wstring& description); protected: FileSystemObject(const Zstring& shortNameLeft, const Zstring& shortNameRight, HierarchyObject& parentObj, CompareFilesResult defaultCmpResult) : cmpResult(defaultCmpResult), selectedForSynchronization(true), syncDir_(SyncDirection::NONE), shortNameLeft_(shortNameLeft), shortNameRight_(shortNameRight), //shortNameRight_(shortNameRight == shortNameLeft ? shortNameLeft : shortNameRight), -> strangely doesn't seem to shrink peak memory consumption at all! parent_(parentObj) { parent_.notifySyncCfgChanged(); } ~FileSystemObject() {} //don't need polymorphic deletion //mustn't call parent here, it is already partially destroyed and nothing more than a pure HierarchyObject! virtual void flip(); virtual void notifySyncCfgChanged() { parent().notifySyncCfgChanged(); /*propagate!*/ } void setSynced(const Zstring& shortName); private: virtual void removeObjectL() = 0; virtual void removeObjectR() = 0; //categorization std::unique_ptr cmpResultDescr; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA CompareFilesResult cmpResult; //although this uses 4 bytes there is currently *no* space wasted in class layout! bool selectedForSynchronization; SyncDirection syncDir_; //1 byte: optimize memory layout! std::unique_ptr syncDirConflict; //non-empty if we have a conflict setting sync-direction //get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!) //Note: we model *four* states with last two variables => "syncDirConflict is empty or syncDir == NONE" is a class invariant!!! Zstring shortNameLeft_; //slightly redundant under linux, but on windows the "same" filenames can differ in case Zstring shortNameRight_; //use as indicator: an empty name means: not existing! HierarchyObject& parent_; }; //------------------------------------------------------------------ class DirPair : public FileSystemObject, public HierarchyObject { friend class HierarchyObject; public: virtual void accept(FSObjectVisitor& visitor) const; CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult DirPair(const Zstring& shortNameLeft, //use empty shortname if "not existing" const Zstring& shortNameRight, // HierarchyObject& parentObj, CompareDirResult defaultCmpResult) : FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast(defaultCmpResult)), HierarchyObject(getObjRelativeName() + FILE_NAME_SEPARATOR, parentObj.getRoot()), syncOpBuffered(SO_DO_NOTHING), syncOpUpToDate(false) {} virtual SyncOperation getSyncOperation() const; void setSyncedTo(const Zstring& shortName); //call after sync, sets DIR_EQUAL private: virtual void flip(); virtual void removeObjectL(); virtual void removeObjectR(); virtual void notifySyncCfgChanged() { syncOpUpToDate = false; FileSystemObject::notifySyncCfgChanged(); HierarchyObject::notifySyncCfgChanged(); } mutable SyncOperation syncOpBuffered; //determining sync-op for directory may be expensive as it depends on child-objects -> buffer it mutable bool syncOpUpToDate; // }; //------------------------------------------------------------------ class FilePair : public FileSystemObject { friend class HierarchyObject; //construction public: virtual void accept(FSObjectVisitor& visitor) const; FilePair(const Zstring& shortNameLeft, //use empty string if "not existing" const FileDescriptor& left, CompareFilesResult defaultCmpResult, const Zstring& shortNameRight, // const FileDescriptor& right, HierarchyObject& parentObj) : FileSystemObject(shortNameLeft, shortNameRight, parentObj, defaultCmpResult), dataLeft(left), dataRight(right), moveFileRef(nullptr) {} template Int64 getLastWriteTime () const; template UInt64 getFileSize () const; template FileId getFileId () const; template bool isFollowedSymlink() const; void setMoveRef(ObjectId refId) { moveFileRef = refId; } //reference to corresponding renamed file ObjectId getMoveRef() const { return moveFileRef; } //may be nullptr CompareFilesResult getFileCategory() const; virtual SyncOperation testSyncOperation(SyncDirection testSyncDir) const; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! virtual SyncOperation getSyncOperation() const; template void setSyncedTo(const Zstring& shortName, //call after sync, sets FILE_EQUAL const UInt64& fileSize, const Int64& lastWriteTimeTrg, const Int64& lastWriteTimeSrc, const FileId& fileIdTrg, const FileId& fileIdSrc, bool isSymlinkTrg, bool isSymlinkSrc); private: SyncOperation applyMoveOptimization(SyncOperation op) const; virtual void flip(); virtual void removeObjectL(); virtual void removeObjectR(); FileDescriptor dataLeft; FileDescriptor dataRight; ObjectId moveFileRef; //optional, filled by redetermineSyncDirection() }; //------------------------------------------------------------------ class SymlinkPair : public FileSystemObject //this class models a TRUE symbolic link, i.e. one that is NEVER dereferenced: deref-links should be directly placed in class File/DirPair { friend class HierarchyObject; //construction public: virtual void accept(FSObjectVisitor& visitor) const; template zen::Int64 getLastWriteTime() const; //write time of the link, NOT target! CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFilesResult SymlinkPair(const Zstring& shortNameLeft, //use empty string if "not existing" const LinkDescriptor& left, CompareSymlinkResult defaultCmpResult, const Zstring& shortNameRight, //use empty string if "not existing" const LinkDescriptor& right, HierarchyObject& parentObj) : FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast(defaultCmpResult)), dataLeft(left), dataRight(right) {} template void setSyncedTo(const Zstring& shortName, //call after sync, sets SYMLINK_EQUAL const Int64& lastWriteTimeTrg, const Int64& lastWriteTimeSrc); private: virtual void flip(); virtual void removeObjectL(); virtual void removeObjectR(); LinkDescriptor dataLeft; LinkDescriptor dataRight; }; //------------------------------------------------------------------ //generic type descriptions (usecase CSV legend, sync config) std::wstring getCategoryDescription(CompareFilesResult cmpRes); std::wstring getSyncOpDescription (SyncOperation op); //item-specific type descriptions std::wstring getCategoryDescription(const FileSystemObject& fsObj); std::wstring getSyncOpDescription (const FileSystemObject& fsObj); //------------------------------------------------------------------ //---------------Inline Implementation--------------------------------------------------- //inline virtual... admittedly its use may be limited inline void FilePair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } inline void DirPair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } inline void SymlinkPair::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } inline CompareFilesResult FilePair::getFileCategory() const { return getCategory(); } inline CompareDirResult DirPair::getDirCategory() const { return static_cast(getCategory()); } inline std::wstring FileSystemObject::getCatExtraDescription() const { assert(getCategory() == FILE_CONFLICT || getCategory() == FILE_DIFFERENT_METADATA); return cmpResultDescr ? *cmpResultDescr : std::wstring(); } inline SyncDirection FileSystemObject::getSyncDir() const { return syncDir_; } inline void FileSystemObject::setSyncDir(SyncDirection newDir) { syncDir_ = newDir; syncDirConflict.reset(); notifySyncCfgChanged(); } inline void FileSystemObject::setSyncDirConflict(const std::wstring& description) { syncDir_ = SyncDirection::NONE; syncDirConflict.reset(new std::wstring(description)); notifySyncCfgChanged(); } inline std::wstring FileSystemObject::getSyncOpConflict() const { assert(getSyncOperation() == SO_UNRESOLVED_CONFLICT); return syncDirConflict ? *syncDirConflict : std::wstring(); } inline bool FileSystemObject::isActive() const { return selectedForSynchronization; } inline void FileSystemObject::setActive(bool active) { selectedForSynchronization = active; notifySyncCfgChanged(); } template inline bool FileSystemObject::isEmpty() const { return SelectParam::get(shortNameLeft_, shortNameRight_).empty(); } inline bool FileSystemObject::isEmpty() const { return isEmpty() && isEmpty(); } template inline const Zstring& FileSystemObject::getShortName() const { return SelectParam::get(shortNameLeft_, shortNameRight_); //empty if not existing } template inline Zstring FileSystemObject::getRelativeName() const { return isEmpty() ? Zstring() : parent_.getObjRelativeNamePf() + getShortName(); } inline Zstring FileSystemObject::getObjRelativeName() const { return parent_.getObjRelativeNamePf() + getObjShortName(); } inline Zstring FileSystemObject::getObjShortName() const { return isEmpty() ? getShortName() : getShortName(); } template inline Zstring FileSystemObject::getFullName() const { return isEmpty() ? Zstring() : getBaseDirPf() + parent_.getObjRelativeNamePf() + getShortName(); } template inline const Zstring& FileSystemObject::getBaseDirPf() const { return root().getBaseDirPf(); } template <> inline void FileSystemObject::removeObject() { cmpResult = isEmpty() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY; shortNameLeft_.clear(); removeObjectL(); setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() } template <> inline void FileSystemObject::removeObject() { cmpResult = isEmpty() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY; shortNameRight_.clear(); removeObjectR(); setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() } inline void FileSystemObject::setSynced(const Zstring& shortName) { assert(!isEmpty()); shortNameRight_ = shortNameLeft_ = shortName; cmpResult = FILE_EQUAL; setSyncDir(SyncDirection::NONE); } template inline void FileSystemObject::setCategory() { cmpResult = res; } template <> void FileSystemObject::setCategory(); // template <> void FileSystemObject::setCategory(); //not defined! template <> void FileSystemObject::setCategory(); // template <> void FileSystemObject::setCategory(); // inline void FileSystemObject::setCategoryConflict(const std::wstring& description) { cmpResult = FILE_CONFLICT; cmpResultDescr.reset(new std::wstring(description)); } inline void FileSystemObject::setCategoryDiffMetadata(const std::wstring& description) { cmpResult = FILE_DIFFERENT_METADATA; cmpResultDescr.reset(new std::wstring(description)); } inline void FileSystemObject::flip() { std::swap(shortNameLeft_, shortNameRight_); switch (cmpResult) { case FILE_LEFT_SIDE_ONLY: cmpResult = FILE_RIGHT_SIDE_ONLY; break; case FILE_RIGHT_SIDE_ONLY: cmpResult = FILE_LEFT_SIDE_ONLY; break; case FILE_LEFT_NEWER: cmpResult = FILE_RIGHT_NEWER; break; case FILE_RIGHT_NEWER: cmpResult = FILE_LEFT_NEWER; break; case FILE_DIFFERENT: case FILE_EQUAL: case FILE_DIFFERENT_METADATA: case FILE_CONFLICT: break; } notifySyncCfgChanged(); } inline void HierarchyObject::flip() { for (FilePair& fileObj : refSubFiles()) fileObj.flip(); for (SymlinkPair& linkObj : refSubLinks()) linkObj.flip(); for (DirPair& dirObj : refSubDirs()) dirObj.flip(); } inline DirPair& HierarchyObject::addSubDir(const Zstring& shortNameLeft, const Zstring& shortNameRight, CompareDirResult defaultCmpResult) { subDirs.emplace_back(shortNameLeft, shortNameRight, *this, defaultCmpResult); return subDirs.back(); } template <> inline DirPair& HierarchyObject::addSubDir(const Zstring& shortName) { subDirs.emplace_back(shortName, Zstring(), *this, DIR_LEFT_SIDE_ONLY); return subDirs.back(); } template <> inline DirPair& HierarchyObject::addSubDir(const Zstring& shortName) { subDirs.emplace_back(Zstring(), shortName, *this, DIR_RIGHT_SIDE_ONLY); return subDirs.back(); } inline FilePair& HierarchyObject::addSubFile(const Zstring& shortNameLeft, const FileDescriptor& left, //file exists on both sides CompareFilesResult defaultCmpResult, const Zstring& shortNameRight, const FileDescriptor& right) { subFiles.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); return subFiles.back(); } template <> inline FilePair& HierarchyObject::addSubFile(const Zstring& shortName, const FileDescriptor& descr) { subFiles.emplace_back(shortName, descr, FILE_LEFT_SIDE_ONLY, Zstring(), FileDescriptor(), *this); return subFiles.back(); } template <> inline FilePair& HierarchyObject::addSubFile(const Zstring& shortName, const FileDescriptor& descr) { subFiles.emplace_back(Zstring(), FileDescriptor(), FILE_RIGHT_SIDE_ONLY, shortName, descr, *this); return subFiles.back(); } inline SymlinkPair& HierarchyObject::addSubLink( const Zstring& shortNameLeft, const LinkDescriptor& left, //link exists on both sides CompareSymlinkResult defaultCmpResult, const Zstring& shortNameRight, const LinkDescriptor& right) { subLinks.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); return subLinks.back(); } template <> inline SymlinkPair& HierarchyObject::addSubLink(const Zstring& shortName, const LinkDescriptor& descr) { subLinks.emplace_back(shortName, descr, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkDescriptor(), *this); return subLinks.back(); } template <> inline SymlinkPair& HierarchyObject::addSubLink(const Zstring& shortName, const LinkDescriptor& descr) { subLinks.emplace_back(Zstring(), LinkDescriptor(), SYMLINK_RIGHT_SIDE_ONLY, shortName, descr, *this); return subLinks.back(); } inline void BaseDirPair::flip() { HierarchyObject::flip(); std::swap(baseDirPfL, baseDirPfR); std::swap(dirExistsLeft_, dirExistsRight_); } inline void DirPair::flip() { HierarchyObject ::flip(); //call base class versions FileSystemObject::flip(); // } inline void DirPair::removeObjectL() { for (FilePair& fileObj : refSubFiles()) fileObj.removeObject(); for (SymlinkPair& linkObj : refSubLinks()) linkObj.removeObject(); for (DirPair& dirObj : refSubDirs()) dirObj.removeObject(); } inline void DirPair::removeObjectR() { for (FilePair& fileObj : refSubFiles()) fileObj.removeObject(); for (SymlinkPair& linkObj : refSubLinks()) linkObj.removeObject(); for (DirPair& dirObj : refSubDirs()) dirObj.removeObject(); } template inline bool BaseDirPair::isExisting() const { return SelectParam::get(dirExistsLeft_, dirExistsRight_); } template inline void BaseDirPair::setExisting(bool value) { SelectParam::get(dirExistsLeft_, dirExistsRight_) = value; } inline void FilePair::flip() { FileSystemObject::flip(); //call base class version std::swap(dataLeft, dataRight); } inline void FilePair::removeObjectL() { dataLeft = FileDescriptor(); } inline void FilePair::removeObjectR() { dataRight = FileDescriptor(); } template inline zen::Int64 FilePair::getLastWriteTime() const { return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; } template inline zen::UInt64 FilePair::getFileSize() const { return SelectParam::get(dataLeft, dataRight).fileSize; } template inline FileId FilePair::getFileId() const { return FileId(SelectParam::get(dataLeft, dataRight).devId, SelectParam::get(dataLeft, dataRight).fileIdx); } template inline bool FilePair::isFollowedSymlink() const { return SelectParam::get(dataLeft, dataRight).isFollowedSymlink; } template inline void FilePair::setSyncedTo(const Zstring& shortName, const UInt64& fileSize, const Int64& lastWriteTimeTrg, const Int64& lastWriteTimeSrc, const FileId& fileIdTrg, const FileId& fileIdSrc, bool isSymlinkTrg, bool isSymlinkSrc) { //FILE_EQUAL is only allowed for same short name and file size: enforced by this method! static const SelectedSide sideSrc = OtherSide::result; SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg); SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc); moveFileRef = nullptr; FileSystemObject::setSynced(shortName); //set FileSystemObject specific part } template inline void SymlinkPair::setSyncedTo(const Zstring& shortName, const Int64& lastWriteTimeTrg, const Int64& lastWriteTimeSrc) { static const SelectedSide sideSrc = OtherSide::result; SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeTrg); SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeSrc); FileSystemObject::setSynced(shortName); //set FileSystemObject specific part } inline void DirPair::setSyncedTo(const Zstring& shortName) { FileSystemObject::setSynced(shortName); //set FileSystemObject specific part } template inline zen::Int64 SymlinkPair::getLastWriteTime() const { return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; } inline CompareSymlinkResult SymlinkPair::getLinkCategory() const { return static_cast(getCategory()); } inline void SymlinkPair::flip() { FileSystemObject::flip(); //call base class versions std::swap(dataLeft, dataRight); } inline void SymlinkPair::removeObjectL() { dataLeft = LinkDescriptor(); } inline void SymlinkPair::removeObjectR() { dataRight = LinkDescriptor(); } } #endif // FILEHIERARCHY_H_INCLUDED