bug fixes

This commit is contained in:
IlyaShurupov 2024-03-30 17:51:11 +03:00
parent 7f1fa02ba3
commit 04ff6584ed
12 changed files with 153 additions and 74 deletions

View file

@ -19,8 +19,9 @@ public:
[[nodiscard]] ui64 size() const override;
void clearFlags(std::shared_ptr<Node>& directory) override;
bool isHard() const override;
bool isHardNode() const override;
void removeIncomingDynamicLinks() override;
void removeOutgoingLinks() override;
template <typename tFunctor>
static void traverse(std::shared_ptr<Node>& node, tFunctor functor) {

View file

@ -11,6 +11,7 @@
// copy operator exit if exists, do not rename
// clean-ups
// in subtree links should be in-tree after copy operation
// update link if exists
class FileSystem {
public:
@ -31,7 +32,7 @@ public:
static const std::string& getLastError();
private:
bool isPathContainsCurrent(const std::shared_ptr<Node>& node);
bool isPathContains(const std::shared_ptr<Node>& node, const std::shared_ptr<Node>& contains);
std::shared_ptr<Node> getNode(const Path& path, bool parent);
private:

View file

@ -11,7 +11,7 @@ public:
NodeType getType() const override { return LINK; }
[[nodiscard]] std::shared_ptr<Node> clone() const override;
[[nodiscard]] std::shared_ptr<Node> getLink() const;
[[nodiscard]] bool isHard() const;
[[nodiscard]] bool isHardLink() const;
std::shared_ptr<Node> getTarget() override;
@ -20,7 +20,8 @@ public:
void dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector<bool>& indents) override;
static bool linkNodes(const std::shared_ptr<Link>& link, const std::shared_ptr<Node>& target, bool hard);
static void unlinkNodes(const std::shared_ptr<Link>& link);
void removeOutgoingLinks() override;
private:
std::weak_ptr<Node> mLink;

View file

@ -38,8 +38,9 @@ public:
virtual std::shared_ptr<Node> findNode(const Key& path) { return nullptr; }
virtual void clearFlags(std::shared_ptr<Node>& directory) {}
virtual bool isHard() const;
virtual bool isHardNode() const;
virtual void removeIncomingDynamicLinks();
virtual void removeOutgoingLinks() {}
ui32 getMaxDepth() const;
void dump(std::stringstream& ss);

View file

@ -4,16 +4,11 @@
#include <sstream>
#include <algorithm>
#include <iostream>
Directory::Directory() = default;
Directory::~Directory() {
for (auto& member : mMembers) {
std::shared_ptr<Node>& node = member.second;
if (auto linkNode = std::dynamic_pointer_cast<Link>(node)) {
Link::unlinkNodes(linkNode);
}
}
}
bool Directory::attachNode(const Key &newKey, std::shared_ptr<Node> newNode) {
@ -64,6 +59,7 @@ void Directory::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDept
indent(ss, currentDepth, indents);
ss << key;
// ss << " [" << mIncomingHardLinks.size() << ":" << mIncomingDynamicLinks.size() << "] " << size();
ss << " [" << mWorkingNodeFlag.lock().get() << "] ";
ss << "\n";
currentDepth++;
@ -98,7 +94,7 @@ std::shared_ptr<Node> Directory::clone() const {
std::shared_ptr<Node>& node = member.second;
if (auto linkNode = std::dynamic_pointer_cast<Link>(node)) {
auto targetNode = linkNode->getTarget();
assert(Link::linkNodes(linkNode, targetNode, linkNode->isHard()));
assert(Link::linkNodes(linkNode, targetNode, linkNode->isHardLink()));
}
}
@ -111,8 +107,11 @@ void Directory::clearFlags(std::shared_ptr<Node> &directory) {
});
}
bool Directory::isHard() const {
return std::any_of(mMembers.begin(), mMembers.end(), [](const auto& member) { return member.second->isHard(); });
bool Directory::isHardNode() const {
if (Node::isHardNode()) return true;
return std::any_of(mMembers.begin(), mMembers.end(), [](const auto& member) {
return member.second->isHardNode();
});
}
void Directory::removeIncomingDynamicLinks() {
@ -120,4 +119,10 @@ void Directory::removeIncomingDynamicLinks() {
for (auto& member : mMembers) {
member.second->removeIncomingDynamicLinks();
}
}
void Directory::removeOutgoingLinks() {
for (auto& member : mMembers) {
member.second->removeOutgoingLinks();
}
}

View file

@ -12,6 +12,7 @@ static std::string gError;
FileSystem::FileSystem() {
root = std::make_shared<Directory>();
root->mKey = "C:";
currentDirectory = root;
initializeTransitions();
}
@ -34,7 +35,7 @@ std::shared_ptr<Node> FileSystem::getNode(const Path& path, bool parent) {
bool FileSystem::changeCurrent(const Path& path) {
if (path.isInvalid()) {
gError = "Path not found";
gError = "Invalid path";
return false;
}
@ -45,6 +46,10 @@ bool FileSystem::changeCurrent(const Path& path) {
return false;
}
while (parentNode->getTarget()) {
parentNode = parentNode->getTarget();
}
if (parentNode->getType() != Node::DIRECTORY) {
gError = "Path is not a directory";
return false;
@ -63,14 +68,14 @@ bool FileSystem::makeDirectory(const Path& path) {
auto parentNode = getNode(path, true);
if (!parentNode) {
gError = "Invalid path";
gError = "Path not found";
return false;
}
auto existingNode = parentNode->findNode(path.getFilename());
if (existingNode) {
if (!(existingNode->getType() == Node::DIRECTORY && existingNode->empty())) {
gError = "File or link with such name already exists";
gError = "File, link or not empty directory with such name already exists";
return false;
}
return true;
@ -92,7 +97,7 @@ bool FileSystem::makeFile(const Path& path) {
auto parentNode = getNode(path, true);
if (!parentNode) {
gError = "Invalid path";
gError = "Path not found";
return false;
}
@ -120,19 +125,19 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic
auto parentNodeSource = getNode(source, true);
if (!parentNodeSource) {
gError = "Invalid source path";
gError = "Source path not found";
return false;
}
auto parentNodeTarget = getNode(target, false);
if (!parentNodeTarget) {
gError = "Invalid target path";
gError = "Destination path not found";
return false;
}
auto sourceNode = parentNodeSource->findNode(source.getFilename());
if (!sourceNode) {
gError = "Invalid source path";
gError = "Source path not found";
return false;
}
@ -189,19 +194,20 @@ bool FileSystem::removeDirectory(const Path& path, bool recursively) {
return false;
}
if (isPathContainsCurrent(existingNode)) {
if (isPathContains(existingNode, currentDirectory)) {
gError = "Can not remove directory you are currently working in";
return false;
}
existingNode->clearFlags(parentNode);
if (existingNode->isHard()) {
gError = "Directory is referenced by a hard links";
existingNode->clearFlags(existingNode);
if (existingNode->isHardNode()) {
gError = "Directory is referenced by a hard link";
return false;
}
// deletes dynamic incoming links to this file system sub-ree
existingNode->removeIncomingDynamicLinks();
existingNode->removeOutgoingLinks();
if (!parentNode->detachNode(path.getFilename())) {
gError = "Can not remove directory because it has hard references";
@ -235,15 +241,13 @@ bool FileSystem::removeFileOrLink(const Path &path) {
return false;
}
existingNode->clearFlags(parentNode);
if (existingNode->isHard()) {
existingNode->clearFlags(existingNode);
if (existingNode->isHardNode()) {
gError = "File is referenced by a hard link";
return false;
}
if (auto link = dynamic_pointer_cast<Link>(existingNode)) {
Link::unlinkNodes(link);
}
existingNode->removeOutgoingLinks();
if (!parentNode->detachNode(path.getFilename())) {
gError = "Can not remove file or link because it is referenced by a hard link";
@ -320,6 +324,16 @@ bool FileSystem::moveNode(const Path &source, const Path &target) {
return false;
}
if (isPathContains(sourceNode, currentDirectory)) {
gError = "Can not move directory you are currently working in";
return false;
}
if (isPathContains(sourceNode, parentNodeTarget)) {
gError = "Can not move node into itself";
return false;
}
const Key& key = source.getFilename();
if (parentNodeTarget->findNode(key)) {
@ -327,8 +341,8 @@ bool FileSystem::moveNode(const Path &source, const Path &target) {
return false;
}
sourceNode->clearFlags(parentNodeSource);
if (sourceNode->isHard()) {
sourceNode->clearFlags(sourceNode);
if (sourceNode->isHardNode()) {
gError = "Node is referenced by a hard link";
return false;
}
@ -347,11 +361,12 @@ void FileSystem::log() const {
root->getNodeStraightPath(currentDirectory, currentPath);
std::reverse(currentPath.begin(), currentPath.end());
ss << "cd [";
for (const auto& node : currentPath) {
ss << node->mKey << "/";
ss << "pwd [ ";
for (auto it = currentPath.begin(); it != currentPath.end(); ++it) {
ss << (*it)->mKey;
if (std::distance(it, currentPath.end()) > 1) ss << "/";
}
ss << "]\n";
ss << " ]\n";
root->dump(ss);
std::cout << ss.str();
@ -361,11 +376,11 @@ const std::string& FileSystem::getLastError() {
return gError;
}
bool FileSystem::isPathContainsCurrent(const std::shared_ptr<Node>& node) {
bool FileSystem::isPathContains(const std::shared_ptr<Node>& node, const std::shared_ptr<Node>& current) {
std::vector<std::shared_ptr<Node>> currentPath;
std::vector<std::shared_ptr<Node>> path;
root->getNodeStraightPath(currentDirectory, currentPath);
root->getNodeStraightPath(current, currentPath);
root->getNodeStraightPath(node, path);
std::reverse(currentPath.begin(), currentPath.end());

View file

@ -109,6 +109,8 @@ bool Interpreter::interpret(const std::string& command) {
std::vector<std::string> words;
getWords(command, words);
std::cout << command << "\n";
if (words.empty()) {
reportError("Empty command");
return false;

View file

@ -35,18 +35,14 @@ bool Link::linkNodes(const std::shared_ptr<Link>& link, const std::shared_ptr<No
return true;
}
void Link::unlinkNodes(const std::shared_ptr<Link>& link) {
auto target = link->mLink.lock();
// this is happening when
if (!target) return;
auto& links = link->isHard() ? target->mIncomingHardLinks : target->mIncomingDynamicLinks;
void Link::removeOutgoingLinks() {
auto target = mLink.lock();
assert(target);
auto& links = isHardLink() ? target->mIncomingHardLinks : target->mIncomingDynamicLinks;
links.erase(std::remove_if(links.begin(), links.end(), [&](std::weak_ptr<Link>& node){
auto targetLink = node.lock();
assert(targetLink);
return targetLink == link;
return !targetLink || targetLink.get() == this;
}), links.end());
}
@ -56,7 +52,7 @@ std::shared_ptr<Node> Link::getLink() const {
return target;
}
bool Link::isHard() const {
bool Link::isHardLink() const {
return mIsHard;
}
@ -71,12 +67,16 @@ std::shared_ptr<Node> Link::findNode(const std::vector<Key>& path, ui32 currentD
void Link::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector<bool>& indents) {
indent(ss, currentDepth, indents);
ss << key << (isHard() ? " hlink[" : " dlink[");
ss << key << (isHardLink() ? " hlink[" : " dlink[");
std::vector<std::shared_ptr<Node>> path;
getNodeStraightPath(getLink(), path);
std::reverse(path.begin(), path.end());
for (auto& node : path)
ss << node->mKey << "/";
for (auto it = path.begin(); it != path.end(); ++it) {
ss << (*it)->mKey;
if (std::distance(it, path.end()) > 1) ss << "/";
}
ss << "]\n";
}

View file

@ -6,8 +6,11 @@
#include <sstream>
#include <cassert>
#include <algorithm>
#include <iostream>
Node::Node(const Node &node) {}
Node::Node(const Node &node) {
mKey = node.mKey;
}
Node::~Node() {
// assert(mIncomingHardLinks.empty());
@ -32,28 +35,27 @@ std::shared_ptr<Node> Node::findNode(const std::vector<Key>& path, ui32 currentD
return nullptr;
}
bool Node::isHard() const {
bool Node::isHardNode() const {
return std::any_of(mIncomingHardLinks.begin(), mIncomingHardLinks.end(), [this](const std::weak_ptr<Link>& hardLink) {
auto linkNode = hardLink.lock();
assert(linkNode);
auto linkTarget = linkNode->getLink()->mWorkingNodeFlag.lock();
assert(linkTarget);
auto ownLink = mWorkingNodeFlag.lock();
assert(linkNode && ownLink);
auto linkTarget = linkNode->mWorkingNodeFlag.lock();
// if hard link in the same working tree it does not count
return ownLink != linkTarget;
return !linkTarget || 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();
assert(linkNode && ownLink);
if (ownLink == linkTarget) {
auto linkTarget = linkNode->mWorkingNodeFlag.lock();
if (linkTarget && ownLink == linkTarget) {
continue;
}
@ -76,7 +78,7 @@ void Node::getNodeStraightPath(const std::shared_ptr<Node>& node, std::vector<st
void Node::dump(std::stringstream& ss) {
std::vector<bool> indents;
indents.resize(getMaxDepth());
dumpUtil(ss, "/", 0, indents);
dumpUtil(ss, mKey, 0, indents);
ss << "\n";
}

View file

@ -1,7 +1,9 @@
#include "Path.hpp"
#include <cassert>
#include <cstring>
std::vector<char> transitions;
constexpr auto drivePrefix = "C:";
void initializeTransitions() {
transitions.resize(256);
@ -50,14 +52,35 @@ const std::string& Path::getFilename() const {
}
void Path::set(const std::string& path) {
mIsValid = true;
if (path.empty()) {
mIsValid = false;
return;
}
std::string lowercasePath = path;
mIsValid = true;
mAbsolute = path.front() == '/';
const auto drivePrefixSize = std::strlen(drivePrefix);
if (drivePrefixSize) {
if ((lowercasePath.size() >= drivePrefixSize) && (std::memcmp(lowercasePath.c_str(), drivePrefix, drivePrefixSize) == 0)) {
lowercasePath.erase(0, drivePrefixSize);
mAbsolute = true;
if (lowercasePath.empty()) lowercasePath = "/";
else if (lowercasePath[0] != '/') {
mIsValid = false;
return;
}
} else {
if (lowercasePath[0] == '/') {
mIsValid = false;
return;
}
}
} else {
mAbsolute = path.front() == '/';
}
mDirectory = path.back() == '/';
mChain.clear();

View file

@ -4,6 +4,34 @@
int main() {
Interpreter interpreter;
interpreter.interpret("");
interpreter.interpret("md a");
interpreter.interpret("md a/b");
interpreter.interpret("mdl a a/b");
interpreter.interpret("copy a C:/");
interpreter.interpret("deltree a");
interpreter.interpret("deltree a_copy");
interpreter.interpret("md a");
interpreter.interpret("md a/b");
interpreter.interpret("mhl a a/b");
interpreter.interpret("copy a C:/");
interpreter.interpret("deltree a");
interpreter.interpret("deltree a_copy");
interpreter.interpret("del a/b/a");
interpreter.interpret("deltree a");
interpreter.interpret("md a");
interpreter.interpret("md a/b");
interpreter.interpret("mdl a a/b");
interpreter.interpret("copy a C:/");
interpreter.interpret("md c");
interpreter.interpret("move a c");
interpreter.interpret("move a_copy c");
interpreter.interpret("copy c C:/");
interpreter.interpret("deltree c");
return UnitTest::RunAllTests();
}

View file

@ -2,24 +2,24 @@
m
mD a
mD /
MD /A123
cd /A123
MD C:/A123
cd C:/A123
md asd
md asd/b
md asd/b/c
md asd/b/c/d
cd asd/b/c/d
cd /
copy A123 /
cd C:/
copy A123 C:/
rd d
deltree A123
deltree A123_copy
md a
md a/b
md a/b/c
mhl a /a/b/c/
mhl a C:/a/b/c/
md a/b/c/a/k
copy a /
mdl a/b/c /a_copy/k
move a/b/c /a_copy
del /a_copy/k/c
copy a C:/
mdl a/b/c C:/a_copy/k
move a/b/c C:/a_copy
del C:/a_copy/k/c