613 lines
18 KiB
C++
613 lines
18 KiB
C++
#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 <tl/atomic.h>
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
template class tl::abs_path<tl::path_systems<fs::WindowsSystem, fs::PosixSystem, fs::UncSystem>>;
|
|
template class tl::rel_path<tl::path_systems<fs::WindowsSystem, fs::PosixSystem, fs::UncSystem>>;
|
|
|
|
namespace fs
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace
|
|
{
|
|
tl::atomic<int> s_lastId(0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
tl::result<CustomFilesystem::PackId> CustomFilesystem::mountFront(AbsPathView mountPoint, tl::unique_ref<IPack> pack)
|
|
{
|
|
return mount(std::move(mountPoint), std::move(pack), MountPolicy::Front);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
tl::result<CustomFilesystem::PackId> CustomFilesystem::mountBack(AbsPathView mountPoint, tl::unique_ref<IPack> pack)
|
|
{
|
|
return mount(std::move(mountPoint), std::move(pack), MountPolicy::Back);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
cppcoro::generator<tl::unique_ref<IPack>> CustomFilesystem::unmountAll()
|
|
{
|
|
tl::fixed_vector<tl::unique_ref<IPack>, 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<IPack> 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<tl::unique_ref<ISource>, 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<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<tl::unique_ref<IStreamSource>, 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<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<tl::unique_ref<IMapSource>, 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<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<IWritablePack> pack = tl::dynamic_lent_cast<IWritablePack>(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<Error>(ErrorCode::NotAllowed, "The filesystem is read only");
|
|
}
|
|
}
|
|
|
|
return tl::make_error<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<IWritablePack> pack = tl::dynamic_lent_cast<IWritablePack>(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<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<IWritablePack> pack = tl::dynamic_lent_cast<IWritablePack>(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<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>(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<IWritablePack> pack = tl::dynamic_lent_cast<IWritablePack>(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>(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>(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<IWritablePack> pack = tl::dynamic_lent_cast<IWritablePack>(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<Error>(ErrorCode::NotAllowed, "The filesystem is read only");
|
|
}
|
|
}
|
|
|
|
return tl::make_error<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<IWritablePack> pack = tl::dynamic_lent_cast<IWritablePack>(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<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>(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<IWritablePack> pack = tl::dynamic_lent_cast<IWritablePack>(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>(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>(Error::code_t::NotFound, "File {} not found", path);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
cppcoro::generator<EnumerateEntry> 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<EnumerateEntry> CustomFilesystem::enumerateRecursively(AbsPath path) const
|
|
{
|
|
tl::unordered_set<RelPath> 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::PackId> CustomFilesystem::mount(AbsPathView mountPoint, tl::unique_ref<IPack> 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;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|