#include "StdAfx.h" #include "fs/CustomFilesystem.h" #include "fs/WritableFolderPack.h" #include "tl/assert.h" //#include "tl/algorithm/find.h" #include "fs/CopyStream.h" #include "fs/NativeFilesystem.h" #include "tl/unordered_set.h" #include "tl/algorithm/find.h" #include ////////////////////////////////////////////////////////////////////////// template class tl::abs_path>; template class tl::rel_path>; namespace fs { ////////////////////////////////////////////////////////////////////////// namespace { tl::atomic s_lastId(0); } ////////////////////////////////////////////////////////////////////////// tl::result CustomFilesystem::mountFront(AbsPathView mountPoint, tl::unique_ref pack) { return mount(std::move(mountPoint), std::move(pack), MountPolicy::Front); } ////////////////////////////////////////////////////////////////////////// tl::result CustomFilesystem::mountBack(AbsPathView mountPoint, tl::unique_ref pack) { return mount(std::move(mountPoint), std::move(pack), MountPolicy::Back); } ////////////////////////////////////////////////////////////////////////// cppcoro::generator> CustomFilesystem::unmountAll() { tl::fixed_vector, 32> packs; packs.reserve(m_packsData.size()); for (PackData& pd: m_packsData) packs.push_back(std::move(pd.pack)); m_packsData.clear(); for (auto& pack: packs) co_yield std::move(pack); } ////////////////////////////////////////////////////////////////////////// tl::unique_ptr CustomFilesystem::unmount(PackId packId) { if (const auto it = tl::find_if(m_packsData, [packId](const PackData& p) { return p.id == packId; }); it != m_packsData.end()) { auto p = std::move(it->pack); //move it before erasing m_packsData.erase(it); return std::move(p); } return nullptr; } ////////////////////////////////////////////////////////////////////////// OpenSourceResult CustomFilesystem::openSource(AbsPathView path, SourceFlags flags) const { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(filePackPath, path, pd.mountPoint); const ExistsResult existsResult = pd.pack->exists(filePackPath); if (existsResult == tl::success() && existsResult.value() == false) continue; tl::result, Error> result = pd.pack->openSource(filePackPath, flags); if (result.has_value()) return std::move(result.value()); if (result.error().code != ErrorCode::NotFound) return result; } } return tl::make_error(ErrorCode::NotFound, "Path '{}' was not found", path); } ////////////////////////////////////////////////////////////////////////// OpenStreamSourceResult CustomFilesystem::openStreamSource(AbsPathView path, SourceFlags flags) const { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(filePackPath, path, pd.mountPoint); const ExistsResult existsResult = pd.pack->exists(filePackPath); if (existsResult == tl::success() && existsResult.value() == false) continue; tl::result, Error> result = pd.pack->openStreamSource(filePackPath, flags); if (result.has_value()) return std::move(result.value()); if (result.error().code != ErrorCode::NotFound) return result; } } return tl::make_error(ErrorCode::NotFound, "Path '{}' was not found", path); } ////////////////////////////////////////////////////////////////////////// OpenMapSourceResult CustomFilesystem::openMapSource(AbsPathView path, MapView mapView, SourceFlags flags) const { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(filePackPath, path, pd.mountPoint); const ExistsResult existsResult = pd.pack->exists(filePackPath); if (existsResult == tl::success() && existsResult.value() == false) continue; tl::result, Error> result = pd.pack->openMapSource(filePackPath, mapView, flags); if (result.has_value()) return std::move(result.value()); if (result.error().code != ErrorCode::NotFound) return result.error(); } } return tl::make_error(ErrorCode::NotFound, "Path '{}' was not found", path); } ////////////////////////////////////////////////////////////////////////// OpenMapSinkResult CustomFilesystem::openMapSink(AbsPathView path, Mode mode, size_t size, SinkFlags flags) { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { if (const tl::lent_ptr pack = tl::dynamic_lent_cast(pd.pack)) { convertToPackPath(filePackPath, path, pd.mountPoint); OpenMapSinkResult result = pack->openMapSink(filePackPath, mode, size, flags); if (result.has_value()) return std::move(result.value()); if (result.error().code != ErrorCode::NotFound) return result; } else return tl::make_error(ErrorCode::NotAllowed, "The filesystem is read only"); } } return tl::make_error(ErrorCode::NotFound, "Path '{}' was not found", path); } ////////////////////////////////////////////////////////////////////////// OpenSinkResult CustomFilesystem::openSink(AbsPathView path, Mode mode, SinkFlags flags) { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { if (const tl::lent_ptr pack = tl::dynamic_lent_cast(pd.pack)) { convertToPackPath(filePackPath, path, pd.mountPoint); OpenSinkResult result = pack->openSink(filePackPath, mode, flags); if (result.has_value()) return std::move(result.value()); if (result.error().code != ErrorCode::NotFound) return result; } } } return tl::make_error(ErrorCode::NotFound, "Path '{}' was not found or all mounted packs are read only", path); } ////////////////////////////////////////////////////////////////////////// OpenStreamSinkResult CustomFilesystem::openStreamSink(AbsPathView path, Mode mode, SinkFlags flags) { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { if (const tl::lent_ptr pack = tl::dynamic_lent_cast(pd.pack)) { convertToPackPath(filePackPath, path, pd.mountPoint); OpenStreamSinkResult result = pack->openStreamSink(filePackPath, mode, flags); if (result.has_value()) return std::move(result.value()); if (result.error().code != ErrorCode::NotFound) return result; } } } return tl::make_error(ErrorCode::NotFound, "Path '{}' was not found or all mounted packs are read only", path); } ////////////////////////////////////////////////////////////////////////// ExistsResult CustomFilesystem::exists(AbsPathView path) const { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(filePackPath, path, pd.mountPoint); if (filePackPath.empty()) //This is the exact mount point of the pack, which means this folder exists return true; auto result = pd.pack->exists(filePackPath); if (result.has_value() && result.value() == true) return true; if (result.has_error()) { //exists should not return not found TL_ASSERT(result.error().code != ErrorCode::NotFound); return result; } } } return false; } ////////////////////////////////////////////////////////////////////////// IsFileResult CustomFilesystem::isFile(AbsPathView path) const { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(filePackPath, path, pd.mountPoint); if (filePackPath.empty()) //This is the exact mount point of the pack, which means it is not a file return false; auto result = pd.pack->isFile(filePackPath); if (result.has_value()) return result.value(); if (result.error().code != Error::code_t::NotFound) return result; } } return tl::make_error(Error::code_t::NotFound, "Path '{}' not found in the filesystem", path); } ////////////////////////////////////////////////////////////////////////// RenameResult CustomFilesystem::rename(AbsPathView path, AbsPathView newPath) { AbsPath fromPackPath; AbsPath toPackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { if (const tl::lent_ptr pack = tl::dynamic_lent_cast(pd.pack)) { convertToPackPath(fromPackPath, path, pd.mountPoint); convertToPackPath(toPackPath, newPath, pd.mountPoint); auto result = pack->rename(fromPackPath, toPackPath); if (result.has_value()) return tl::success(); if (result.error().code != Error::code_t::NotFound) return result; } } } return tl::make_error(Error::code_t::NotFound, "Path '{}' was not found or all mounted packs are read only", path); } ////////////////////////////////////////////////////////////////////////// CopyResult CustomFilesystem::copy(AbsPathView path, AbsPathView newPath) { OUTCOME_TRY(const auto source, openStreamSource(path, SourceFlags(SourceFlag::SequentialHint, SourceFlag::Uncached))); OUTCOME_TRY(const auto destination, openStreamSink(newPath, Mode::CreateOrOpenAndClear)); CopyStreamResult copyResult = copyStream(*destination, *source); if (copyResult.has_error()) return copyResult.error(); return tl::success(); } ////////////////////////////////////////////////////////////////////////// MakeHardLinkResult CustomFilesystem::makeHardLink(AbsPathView sourcePath, AbsPathView linkPath) { OUTCOME_TRY(const auto sp, convertToNativePath(sourcePath)); OUTCOME_TRY(const auto lp, convertToNativePath(linkPath)); return native.makeHardLink(sp, lp); } ////////////////////////////////////////////////////////////////////////// MakeSoftLinkResult CustomFilesystem::makeSymLink(AbsPathView sourcePath, AbsPathView linkPath) { OUTCOME_TRY(const auto sp, convertToNativePath(sourcePath)); OUTCOME_TRY(const auto lp, convertToNativePath(linkPath)); return native.makeSymLink(sp, lp); } ////////////////////////////////////////////////////////////////////////// IsFolderResult CustomFilesystem::isFolder(AbsPathView path) const { AbsPath folderPackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(folderPackPath, path, pd.mountPoint); if (folderPackPath.empty()) //This is the exact mount point of the pack, which means it is a folder return true; auto result = pd.pack->isFolder(folderPackPath); if (result.has_value()) return result.value(); if (result.error().code != Error::code_t::NotFound) return result; } } return tl::make_error(Error::code_t::NotFound, "Path '{}' not found in the filesystem", path); } ////////////////////////////////////////////////////////////////////////// MakeFolderResult CustomFilesystem::makeFolder(AbsPathView path) { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { if (const tl::lent_ptr pack = tl::dynamic_lent_cast(pd.pack)) { convertToPackPath(filePackPath, path, pd.mountPoint); MakeFolderResult result = pack->makeFolder(filePackPath); if (result.has_value()) return result; if (result.error().code != ErrorCode::NotFound) return result; } else return tl::make_error(ErrorCode::NotAllowed, "The filesystem is read only"); } } return tl::make_error(Error::code_t::SystemError, "Not implemented"); } ////////////////////////////////////////////////////////////////////////// RemoveResult CustomFilesystem::remove(AbsPathView path) { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { if (const tl::lent_ptr pack = tl::dynamic_lent_cast(pd.pack)) { convertToPackPath(filePackPath, path, pd.mountPoint); auto result = pack->remove(filePackPath); if (result.has_value()) return tl::success(); if (result.error().code != Error::code_t::NotFound) return result; } } } return tl::make_error(ErrorCode::NotFound, "Path '{}' was not found or all mounted packs are read only", path); } ////////////////////////////////////////////////////////////////////////// RemoveRecursivelyResult CustomFilesystem::removeRecursively(AbsPathView path) { for (const EnumerateEntry& ee : enumerate(path)) { AbsPath p = path + ee.path; if (ee.isFolder) { OUTCOME_TRY(removeRecursively(p)); } else { OUTCOME_TRY(remove(p)); } } return tl::success(); } ////////////////////////////////////////////////////////////////////////// ConvertToNativePathResult CustomFilesystem::convertToNativePath(AbsPathView path) const { AbsPath folderPackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(folderPackPath, path, pd.mountPoint); auto result = pd.pack->convertToNativePath(folderPackPath); if (result.has_value()) return result.value(); if (result.error().code != ErrorCode::NotFound) return result; } } return tl::make_error(Error::code_t::NotFound, "Cannot convert '{}' to a native path", path); } ////////////////////////////////////////////////////////////////////////// SetWriteTimeResult CustomFilesystem::setWriteTime(AbsPathView path, time_t time) { AbsPath filePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { if (const tl::lent_ptr pack = tl::dynamic_lent_cast(pd.pack)) { convertToPackPath(filePackPath, path, pd.mountPoint); SetWriteTimeResult result = pack->setWriteTime(filePackPath, time); if (result.has_value()) return tl::success(); if (result.error().code != ErrorCode::NotFound) return result; } } } return tl::make_error(Error::code_t::NotFound, "Path '{}' was not found or all mounted packs are read only", path); } ////////////////////////////////////////////////////////////////////////// GetStatResult CustomFilesystem::getStat(AbsPathView path) const { AbsPath folderPackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(folderPackPath, path, pd.mountPoint); auto result = pd.pack->getStat(folderPackPath); if (result.has_value()) return result.value(); if (result.error().code != ErrorCode::NotFound) return result; } } return tl::make_error(Error::code_t::NotFound, "File {} not found", path); } ////////////////////////////////////////////////////////////////////////// cppcoro::generator CustomFilesystem::enumerate(AbsPath path) const { AbsPath basePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(basePackPath, path, pd.mountPoint); //it's possible that even if the mount point is const IsFolderResult isFolderResult = pd.pack->isFolder(basePackPath); if (isFolderResult.has_error() || isFolderResult.value() == false) continue; for (EnumerateEntry ee : pd.pack->enumerate(basePackPath)) co_yield std::move(ee); } } } ////////////////////////////////////////////////////////////////////////// cppcoro::generator CustomFilesystem::enumerateRecursively(AbsPath path) const { tl::unordered_set checkedFiles; AbsPath basePackPath; for (const auto& pd : m_packsData) { if (pd.mountPoint.is_prefix_of(path)) { convertToPackPath(basePackPath, path, pd.mountPoint); if (!basePackPath.empty()) { const IsFolderResult isFolderResult = pd.pack->isFolder(basePackPath); if (isFolderResult.has_error() || isFolderResult.value() == false) continue; } for (EnumerateEntry ee: pd.pack->enumerateRecursively(basePackPath)) { if (checkedFiles.insert(ee.path).second) co_yield std::move(ee); } } else if (path.is_prefix_of(pd.mountPoint)) { const RelPathView parentPath = path.path_to(pd.mountPoint); RelPath p = parentPath; for (EnumerateEntry ee : pd.pack->enumerateRecursively(AbsPath(PosixRootTag::value()))) { const size_t eePathSize = ee.path.size(); p += ee.path; ee.path = p; p.shrink(eePathSize); if (checkedFiles.insert(ee.path).second) co_yield std::move(ee); } } } } ////////////////////////////////////////////////////////////////////////// void CustomFilesystem::convertToPackPath(AbsPath& packPath, AbsPathView path, AbsPathView mountPoint) const { RelPathView basePath = mountPoint.prefix_path_to(path); packPath = PosixRootTag::value(); packPath.reserve(basePath.size() + 1); for (const tl::string& element : basePath) packPath.push_back(element); } ////////////////////////////////////////////////////////////////////////// tl::result CustomFilesystem::mount(AbsPathView mountPoint, tl::unique_ref pack, MountPolicy policy) { if (!mountPoint.is_valid()) return tl::make_generic_error("Invalid mount point"); auto isFileResult = isFile(mountPoint); if (isFileResult.has_value() && isFileResult.value() == true) return tl::make_generic_error("Invalid shadowing (mount point shadowing a file)"); const PackId id(++s_lastId); PackData data(std::move(pack), id); data.mountPoint = std::move(mountPoint); switch (policy) { case MountPolicy::Front: m_packsData.insert(m_packsData.begin(), std::move(data)); break; case MountPolicy::Back: m_packsData.push_back(std::move(data)); break; default: TL_FAIL(); break; } return id; } ////////////////////////////////////////////////////////////////////////// }