diff --git a/inc/Directory.hpp b/inc/Directory.hpp index c7c2485..3597e05 100644 --- a/inc/Directory.hpp +++ b/inc/Directory.hpp @@ -17,7 +17,20 @@ public: std::shared_ptr findNode(const std::vector& path, ui32 currentDepth = 0) override; std::shared_ptr findNode(const Key& path) override; [[nodiscard]] ui64 size() const override; - bool isDirectory() const override { return true; } + + void clearFlags(std::shared_ptr& directory) override; + bool isHard() const override; + void removeIncomingDynamicLinks() override; + + template + static void traverse(std::shared_ptr& node, tFunctor functor) { + functor(node); + if (std::shared_ptr directory = std::dynamic_pointer_cast(node)) { + for (auto& member : directory->mMembers) { + traverse(member.second, functor); + } + } + } private: void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const; diff --git a/inc/Link.hpp b/inc/Link.hpp index 2bd685a..ed3c8c6 100644 --- a/inc/Link.hpp +++ b/inc/Link.hpp @@ -18,11 +18,8 @@ public: // link on link is not allowed, so no inf looping here std::shared_ptr findNode(const std::vector& path, ui32 currentDepth) override; void dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) override; - bool isLink() const override { return true; } static bool linkNodes(const std::shared_ptr& link, const std::shared_ptr& target, bool hard); - static bool unlinkWithIncomingLinks(std::shared_ptr& target); - static bool removeOutgoingLinks(const std::shared_ptr& link); private: std::weak_ptr mLink; diff --git a/inc/Node.hpp b/inc/Node.hpp index da5c0ff..fc34aeb 100644 --- a/inc/Node.hpp +++ b/inc/Node.hpp @@ -13,6 +13,7 @@ typedef long i32; #include class Link; +class Directory; typedef std::string Key; @@ -35,9 +36,10 @@ public: virtual std::shared_ptr getTarget(); virtual std::shared_ptr findNode(const std::vector& path, ui32 currentDepth = 0); virtual std::shared_ptr findNode(const Key& path) { return nullptr; } - virtual bool isDirectory() const { return false; } - virtual bool isLink() const { return false; } - virtual bool isHard() const { return false; } + + virtual void clearFlags(std::shared_ptr& directory) {} + virtual bool isHard() const; + virtual void removeIncomingDynamicLinks(); ui32 getMaxDepth() const; void dump(std::stringstream& ss); @@ -46,8 +48,9 @@ public: static void indent(std::stringstream & ss, ui32 depth, std::vector& indents); public: + Key mKey; + std::weak_ptr mWorkingNodeFlag; std::weak_ptr mParent; - std::vector> mIncomingHardLinks; std::vector> mIncomingDynamicLinks; }; \ No newline at end of file diff --git a/src/Directory.cpp b/src/Directory.cpp index b4ad76c..015862f 100644 --- a/src/Directory.cpp +++ b/src/Directory.cpp @@ -11,17 +11,12 @@ Directory::~Directory() = default; bool Directory::attachNode(const Key &newKey, std::shared_ptr newNode) { assert(mMembers.find(newKey) == mMembers.end()); mMembers.insert({ newKey, newNode }); + newNode->mKey = newKey; return true; } bool Directory::detachNode(const Key& key) { assert(mMembers.find(key) != mMembers.end()); - - //if (removeNode->key.incomingLinksHard || removeNode->key.incomingLinksDynamic) { - // gError = "Cannot modify node with incoming hard links"; - // return false; - //} - mMembers.erase(key); return true; } @@ -92,3 +87,20 @@ std::shared_ptr Directory::clone() const { } return out; } + +void Directory::clearFlags(std::shared_ptr &directory) { + traverse(directory, [&](std::shared_ptr& node) { + node->mWorkingNodeFlag = directory; + }); +} + +bool Directory::isHard() const { + return std::any_of(mMembers.begin(), mMembers.end(), [](const auto& member) { return member.second->isHard(); }); +} + +void Directory::removeIncomingDynamicLinks() { + Node::removeIncomingDynamicLinks(); + for (auto& member : mMembers) { + member.second->removeIncomingDynamicLinks(); + } +} \ No newline at end of file diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index 2321d33..ba079e9 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -45,7 +45,7 @@ bool FileSystem::changeCurrent(const Path& path) { return false; } - if (!parentNode->isDirectory()) { + if (parentNode->getType() != Node::DIRECTORY) { gError = "Path is not a directory"; return false; } @@ -136,7 +136,7 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic return false; } - if (sourceNode->isLink()) { + if (sourceNode->getType() == Node::LINK) { gError = "Link on link is not allowed"; return false; } @@ -179,7 +179,7 @@ bool FileSystem::removeDirectory(const Path& path, bool recursively) { return false; } - if (!existingNode->isDirectory()) { + if (existingNode->getType() != Node::DIRECTORY) { gError = "Path is not a directory"; return false; } @@ -194,6 +194,15 @@ bool FileSystem::removeDirectory(const Path& path, bool recursively) { return false; } + existingNode->clearFlags(parentNode); + if (existingNode->isHard()) { + gError = "Directory is referenced by a hard links"; + return false; + } + + // deletes dynamic incoming links to this file system sub-ree + existingNode->removeIncomingDynamicLinks(); + if (!parentNode->detachNode(path.getFilename())) { gError = "Can not remove directory because it has hard references"; return false; @@ -221,11 +230,17 @@ bool FileSystem::removeFileOrLink(const Path &path) { return false; } - if (existingNode->isDirectory()) { + if (existingNode->getType() == Node::DIRECTORY) { gError = "Path is not a file or a link"; return false; } + existingNode->clearFlags(parentNode); + if (existingNode->isHard()) { + gError = "File or link is referenced by a hard links"; + return false; + } + if (!parentNode->detachNode(path.getFilename())) { gError = "Can not remove file or link because it has hard references"; return false; @@ -256,9 +271,9 @@ bool FileSystem::copyNode(const Path& source, const Path& target) { if (!sourceNode) { gError = "Invalid source path"; return false; - } + }; - if (!parentNodeTarget->isDirectory()) { + if (parentNodeTarget->getType() != Node::DIRECTORY) { gError = "Target path is not a directory"; return false; } @@ -308,8 +323,9 @@ bool FileSystem::moveNode(const Path &source, const Path &target) { return false; } + sourceNode->clearFlags(parentNodeSource); if (sourceNode->isHard()) { - gError = "Node contains incoming hard links"; + gError = "Node is referenced by a hard links"; return false; } @@ -327,9 +343,9 @@ void FileSystem::log() const { root->getNodeStraightPath(currentDirectory, currentPath); std::reverse(currentPath.begin(), currentPath.end()); - ss << "cd - /"; - for (auto key : currentPath) { - ss << "X" << "/"; + ss << "cd - "; + for (const auto& node : currentPath) { + ss << node->mKey << "/"; } ss << "\n"; diff --git a/src/Link.cpp b/src/Link.cpp index 339818c..857c059 100644 --- a/src/Link.cpp +++ b/src/Link.cpp @@ -32,26 +32,6 @@ bool Link::linkNodes(const std::shared_ptr& link, const std::shared_ptr& target) { - assert(target->mIncomingHardLinks.empty()); - auto& links = target->mIncomingDynamicLinks; - - auto shouldRemove = [](const std::weak_ptr& link) { - // Modify the condition as needed - return link.expired(); // Remove expired weak pointers - }; - - links.erase(std::remove_if(links.begin(), links.end(), shouldRemove), links.end()); -} - -bool Link::removeOutgoingLinks(const std::shared_ptr& link) { -// assert(mLink); -// auto& links = mIsHard ? mLink->mIncomingHardLinks : mLink->mIncomingDynamicLinks; -// links.erase(std::remove(links.begin(), links.end(), this), links.end()); -// Directory::updateTreeLinkCount(mLink); -// mLink = nullptr; -} - std::shared_ptr Link::getLink() const { auto target = mLink.lock(); assert(target); @@ -73,12 +53,12 @@ std::shared_ptr Link::findNode(const std::vector& path, ui32 currentD void Link::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) { indent(ss, currentDepth, indents); - ss << key << (isHard() ? " hlink[/" : " dlink[/"); + ss << key << (isHard() ? " hlink[" : " dlink["); std::vector> path; getNodeStraightPath(getLink(), path); std::reverse(path.begin(), path.end()); - for (auto tmp : path) - ss << "X" << "/"; + for (auto& node : path) + ss << node->mKey << "/"; ss << "]\n"; } diff --git a/src/Node.cpp b/src/Node.cpp index 7a1452a..129c25a 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -1,5 +1,6 @@ #include "Node.hpp" +#include "Link.hpp" #include #include @@ -31,6 +32,37 @@ std::shared_ptr Node::findNode(const std::vector& path, ui32 currentD return nullptr; } +bool Node::isHard() const { + return std::any_of(mIncomingHardLinks.begin(), mIncomingHardLinks.end(), [this](const std::weak_ptr& hardLink) { + auto linkNode = hardLink.lock(); + assert(linkNode); + auto linkTarget = linkNode->getLink()->mWorkingNodeFlag.lock(); + assert(linkTarget); + auto ownLink = mWorkingNodeFlag.lock(); + + // if hard link in the same working tree it does not count + return ownLink != linkTarget; + }); +} + +void Node::removeIncomingDynamicLinks() { + for (auto& dynamicLink : mIncomingDynamicLinks) { + auto linkNode = dynamicLink.lock(); + assert(linkNode); + auto linkTarget = linkNode->getLink()->mWorkingNodeFlag.lock(); + assert(linkTarget); + auto ownLink = mWorkingNodeFlag.lock(); + + if (ownLink == linkTarget) { + continue; + } + + auto parentNode = linkNode->mParent.lock(); + assert(parentNode); + parentNode->detachNode(linkNode->mKey); + } +} + std::shared_ptr Node::getTarget() { return nullptr; } diff --git a/test/Tests.cpp b/test/Tests.cpp index 96fe4b6..49afee7 100644 --- a/test/Tests.cpp +++ b/test/Tests.cpp @@ -31,6 +31,7 @@ int main() { interpreter.interpret("md a/b/c"); interpreter.interpret("mhl a /a/b/c/"); interpreter.interpret("md a/b/c/a/k"); + interpreter.interpret("copy a /"); interpreter.interpret("mdl a/b/c /a_copy/k");