#include "pch.h" #include "IEngine.h" #include "Crypto.h" #include #include using namespace craftlab::fakeraid; namespace craftlab::fakeraid { class Engine : public IEngine { public: Engine(const std::vector& path); void DirExistsOrThrow() const override; std::vector GetRootPaths() const override; std::vector GetPaths() const override; std::deque GetCurrentDir() const override; bool SetWorkingDirectory(const std::vector& path) override; FileAndSumListByRepositoryIndex ListFiles() override; void ListFilesRecur(FileAndSumListByRepositoryIndex*, bool* threadStopping) override; size_t CountFilesRecur() override; void Cd(const std::string& dirName) override; std::string BuildCurrentDirPath(const std::string& root, const PathParts& pathParts) const override; std::string BuildCurrentDirPath(const File& file) const override; std::string BuildFullPath(const File& file) const override; bool AnyFileExists(const std::vector& files, const std::string& dropTo) const override; void RemoveFiles(const std::vector& paths) const override; void RemoveDirs(const std::vector& paths) const override; void CopyItems(const std::vector& itemsToCopy) const override; void AddFiles(const std::vector& files, const std::string& destination) const override; private: void ListFiles(FileAndSumListByRepositoryIndex& result, const PathParts& currentDir, std::string* lastFileScanned =nullptr, bool* threadStopping =nullptr); FileAndSumList ListFiles(const std::string& root, const PathParts& path, int repositoryIndex, std::string* lastFileScanned =nullptr, bool* threadStopping =nullptr); void CopyItem(const CopyInstruction& dst) const; const std::vector rootPaths; std::deque currentDir; }; } Engine::Engine(const std::vector& path): rootPaths(path), currentDir() {} std::vector Engine::GetRootPaths() const { return rootPaths; } std::vector Engine::GetPaths() const { std::vector paths; const std::string workingDir = BuildCurrentDirPath("", currentDir); std::transform(rootPaths.begin(), rootPaths.end(), std::back_inserter(paths), [&workingDir](const std::string& val) { return (std::filesystem::path(val) / std::filesystem::path(workingDir)).string(); }); return paths; } std::deque Engine::GetCurrentDir() const { return currentDir; } std::string Engine::BuildCurrentDirPath(const std::string& root, const PathParts& _currentDir) const { std::filesystem::path result(root); for (const std::string& path : _currentDir) result = result / path; return result.string(); } std::string Engine::BuildCurrentDirPath(const File& file) const { std::filesystem::path result; for (const std::string& path : file.directory) result = result / path; result = result / file.fileName; return result.string(); } std::string Engine::BuildFullPath(const File& file) const { std::filesystem::path result(rootPaths[file.repositoryIndex]); for (const std::string& path : file.directory) result /= path; result /= file.fileName; return result.string(); } bool vectorEquals(const std::vector& a, const std::deque& b) { if (a.size() != b.size()) return false; auto iterA = a.begin(); auto iterB = b.begin(); while (iterA != a.end()) { if (*iterA != *iterB) return false; ++iterA; ++iterB; } return true; } bool Engine::SetWorkingDirectory(const std::vector& path) { if (vectorEquals(path, currentDir)) return false; currentDir.clear(); std::copy(path.begin(), path.end(), std::back_inserter(currentDir)); return true; } void Engine::Cd(const std::string& dirName) { currentDir.push_back(dirName); } FileAndSumList Engine::ListFiles(const std::string& root, const std::deque& wd, int repositoryIndex, std::string* lastFileScanned, bool* threadStopping) { FileAndSumList result {}; const std::string path = BuildCurrentDirPath(root, wd); Crypto cryptoEngine; std::filesystem::directory_iterator directoryIterator; // FIXME error handling try { directoryIterator = std::filesystem::directory_iterator(path); } catch (std::exception& e) { return result; // FIXME } for (const std::filesystem::directory_entry& file : directoryIterator) { bool isDirectory = false; bool isFile = false; try { isDirectory = file.is_directory(); isFile = file.is_regular_file(); } catch (std::exception& e) {} if (isDirectory || isFile) { FileAndSum fileInfos; fileInfos.fileName = file.path().filename().string(); fileInfos.isDir = file.is_directory(); fileInfos.directory = wd; fileInfos.repositoryIndex = repositoryIndex; if (nullptr != lastFileScanned) *lastFileScanned = BuildCurrentDirPath(fileInfos); try { cryptoEngine.Compute(path, fileInfos); } catch (std::runtime_error&) { } result.push_back(fileInfos); } if (threadStopping && *threadStopping) return result; } return result; } void Engine::ListFiles(FileAndSumListByRepositoryIndex& result, const std::deque& currentDir, std::string* lastFileScanned, bool* threadStopping) { int index = 0; for (const auto& i : rootPaths) { const FileAndSumList files = ListFiles(i, currentDir, index++, lastFileScanned, threadStopping); result.FileAndSumListByRepositoryIndex.push_back(files); for (const FileAndSum& file : files) result.FileList[BuildCurrentDirPath(file)] = file; if (threadStopping && *threadStopping) return; } } FileAndSumListByRepositoryIndex Engine::ListFiles() { FileAndSumListByRepositoryIndex result; ListFiles(result, currentDir); return result; } void Engine::ListFilesRecur(FileAndSumListByRepositoryIndex* result, bool* threadStopping) { std::deque itemsToDo { currentDir }; do { FileAndSumListByRepositoryIndex newItems; const PathParts currentPath = itemsToDo.front(); ListFiles(newItems, currentPath, &result->lastInsertedFilename, threadStopping); itemsToDo.pop_front(); std::copy(newItems.FileAndSumListByRepositoryIndex.begin(), newItems.FileAndSumListByRepositoryIndex.end(), std::back_inserter(result->FileAndSumListByRepositoryIndex)); for (const auto& file : newItems.FileList) { result->FileList[file.first] = file.second; if (file.second.isDir) { PathParts dirPath = currentPath; dirPath.push_back(file.second.fileName); itemsToDo.push_back(dirPath); } } } while (!itemsToDo.empty() && !*threadStopping); } size_t Engine::CountFilesRecur() { size_t result =0; std::deque itemsToDo; for (const auto& i: rootPaths) itemsToDo.push_back(i); do { std::filesystem::directory_iterator directoryIterator; // FIXME error handling try { directoryIterator = std::filesystem::directory_iterator(itemsToDo.front()); } catch (std::exception& e) { itemsToDo.pop_front(); continue; } for (const std::filesystem::directory_entry& file : directoryIterator) { try { if (file.is_regular_file()) ++result; else if (file.is_directory()) { itemsToDo.push_back(file.path()); ++result; } } catch (std::exception) {} } itemsToDo.pop_front(); } while (!itemsToDo.empty()); return result; } void Engine::DirExistsOrThrow() const { char errorBuffer[1024]; for (const std::string& fullPath : GetPaths()) { struct stat fileInfo; if (stat(fullPath.c_str(), &fileInfo)) { strerror_s(errorBuffer, errno); throw std::runtime_error("cannot access " + fullPath + ": " + errorBuffer); } if (!(fileInfo.st_mode & S_IFDIR)) { strerror_s(errorBuffer, ENOTDIR); throw std::runtime_error("cannot access " + fullPath + ": " + errorBuffer); } } } void Engine::RemoveFiles(const std::vector& paths) const { for (const std::string& path: paths) std::filesystem::remove(path); // FIXME error handling } void Engine::RemoveDirs(const std::vector& paths) const { for (const std::string& path : paths) std::filesystem::remove_all(path); // FIXME error handling } void Engine::CopyItem(const IEngine::CopyInstruction& item) const { std::filesystem::copy(item.source, item.destination, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive); // FIXME error handling } void Engine::CopyItems(const std::vector& paths) const { for (const CopyInstruction& item: paths) CopyItem(item); } bool Engine::AnyFileExists(const std::vector& files, const std::string& destination) const { for (const std::string _file : files) { std::filesystem::path file(_file); for (const std::string& root : rootPaths) { std::filesystem::path outputPath(root + "/" + destination); outputPath /= file.filename(); if (std::filesystem::exists(outputPath)) { if (std::filesystem::equivalent(outputPath, file)) // FIXME error handling throw IEngine::SameFileError(); return true; } } } return false; } void Engine::AddFiles(const std::vector& files, const std::string& _destination) const { for (const std::string _file : files) { std::filesystem::path file(_file); for (const std::string& root : rootPaths) { std::filesystem::path destination(root + "/" + _destination); destination /= file.filename(); std::filesystem::copy(file, destination, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive); // FIXME error handling } } } IEngine* EngineManager::Open(const std::vector& path) { return new Engine(path); }