diff --git a/src/policies.hpp b/src/policies.hpp index 3ff833c5..c8220f4b 100644 --- a/src/policies.hpp +++ b/src/policies.hpp @@ -23,6 +23,7 @@ #include "policy_epff.hpp" #include "policy_eplfs.hpp" #include "policy_eplus.hpp" +#include "policy_eplup.hpp" #include "policy_epmfs.hpp" #include "policy_eppfrd.hpp" #include "policy_eprand.hpp" @@ -30,6 +31,7 @@ #include "policy_ff.hpp" #include "policy_lfs.hpp" #include "policy_lus.hpp" +#include "policy_lup.hpp" #include "policy_mfs.hpp" #include "policy_msplfs.hpp" #include "policy_msplus.hpp" @@ -49,6 +51,7 @@ struct Policies static Policy::EPAll::Action epall; static Policy::EPFF::Action epff; static Policy::EPLFS::Action eplfs; + static Policy::EPLUP::Action eplup; static Policy::EPLUS::Action eplus; static Policy::EPMFS::Action epmfs; static Policy::EPPFRD::Action eppfrd; @@ -56,6 +59,7 @@ struct Policies static Policy::ERoFS::Action erofs; static Policy::FF::Action ff; static Policy::LFS::Action lfs; + static Policy::LUP::Action lup; static Policy::LUS::Action lus; static Policy::MFS::Action mfs; static Policy::MSPLFS::Action msplfs; @@ -75,6 +79,7 @@ struct Policies static Policy::EPAll::Create epall; static Policy::EPFF::Create epff; static Policy::EPLFS::Create eplfs; + static Policy::EPLUP::Create eplup; static Policy::EPLUS::Create eplus; static Policy::EPMFS::Create epmfs; static Policy::EPPFRD::Create eppfrd; @@ -82,6 +87,7 @@ struct Policies static Policy::ERoFS::Create erofs; static Policy::FF::Create ff; static Policy::LFS::Create lfs; + static Policy::LUP::Create lup; static Policy::LUS::Create lus; static Policy::MFS::Create mfs; static Policy::MSPLFS::Create msplfs; @@ -101,6 +107,7 @@ struct Policies static Policy::EPAll::Search epall; static Policy::EPFF::Search epff; static Policy::EPLFS::Search eplfs; + static Policy::EPLUP::Search eplup; static Policy::EPLUS::Search eplus; static Policy::EPMFS::Search epmfs; static Policy::EPPFRD::Search eppfrd; @@ -108,6 +115,7 @@ struct Policies static Policy::ERoFS::Search erofs; static Policy::FF::Search ff; static Policy::LFS::Search lfs; + static Policy::LUP::Search lup; static Policy::LUS::Search lus; static Policy::MFS::Search mfs; static Policy::MSPLFS::Search msplfs; diff --git a/src/policy_eplup.cpp b/src/policy_eplup.cpp new file mode 100644 index 00000000..94f621f4 --- /dev/null +++ b/src/policy_eplup.cpp @@ -0,0 +1,236 @@ +/* + Copyright (c) 2025, François-Xavier Payet + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "policy_eplup.hpp" + +#include "errno.hpp" +#include "fs_exists.hpp" +#include "fs_info.hpp" +#include "fs_path.hpp" +#include "fs_statvfs_cache.hpp" +#include "policy.hpp" +#include "policy_error.hpp" +#include "rwlock.hpp" + +#include +#include + +using std::string; + +static int +_create(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) +{ + int rv; + int error; + fs::info_t info; + Branch *obranch; + + obranch = nullptr; + error = ENOENT; + + uint64_t best_used = 0; + uint64_t best_total = 1; /* avoid div-by-zero */ + + for (auto &branch : *branches_) + { + if (branch.ro_or_nc()) + error_and_continue(error, EROFS); + if (!fs::exists(branch.path, fusepath_)) + error_and_continue(error, ENOENT); + rv = fs::info(branch.path, &info); + if (rv < 0) + error_and_continue(error, ENOENT); + if (info.readonly) + error_and_continue(error, EROFS); + if (info.spaceavail < branch.minfreespace()) + error_and_continue(error, ENOSPC); + + uint64_t used = info.spaceused; + uint64_t total = info.spaceused + info.spaceavail; + if (total == 0) + { + used = 0; + total = 1; + } + + if (obranch == nullptr) + { + best_used = used; + best_total = total; + obranch = &branch; + continue; + } + + unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total; + unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total; + if (lhs >= rhs) + continue; + + best_used = used; + best_total = total; + obranch = &branch; + } + + if (obranch == nullptr) + return -error; + + paths_.emplace_back(obranch); + + return 0; +} + +static int +_action(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) +{ + int rv; + int error; + fs::info_t info; + Branch *obranch; + + obranch = nullptr; + error = ENOENT; + + uint64_t best_used = 0; + uint64_t best_total = 1; + + for (auto &branch : *branches_) + { + if (branch.ro()) + error_and_continue(error, EROFS); + if (!fs::exists(branch.path, fusepath_)) + error_and_continue(error, ENOENT); + rv = fs::info(branch.path, &info); + if (rv < 0) + error_and_continue(error, ENOENT); + if (info.readonly) + error_and_continue(error, EROFS); + + uint64_t used = info.spaceused; + uint64_t total = info.spaceused + info.spaceavail; + if (total == 0) + { + used = 0; + total = 1; + } + + if (obranch == nullptr) + { + best_used = used; + best_total = total; + obranch = &branch; + continue; + } + + unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total; + unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total; + if (lhs >= rhs) + continue; + + best_used = used; + best_total = total; + obranch = &branch; + } + + if (obranch == nullptr) + return -error; + + paths_.emplace_back(obranch); + + return 0; +} + +static int +_search(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) +{ + int rv; + uint64_t used; + uint64_t avail; + uint64_t best_used = 0; + uint64_t best_total = 1; + Branch *obranch; + + obranch = nullptr; + + for (auto &branch : *branches_) + { + if (!fs::exists(branch.path, fusepath_)) + continue; + rv = fs::statvfs_cache_spaceused(branch.path, &used); + if (rv < 0) + continue; + rv = fs::statvfs_cache_spaceavail(branch.path, &avail); + if (rv < 0) + continue; + + uint64_t total = used + avail; + if (total == 0) + { + used = 0; + total = 1; + } + + if (obranch == nullptr) + { + best_used = used; + best_total = total; + obranch = &branch; + continue; + } + + unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total; + unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total; + if (lhs >= rhs) + continue; + + best_used = used; + best_total = total; + obranch = &branch; + } + + if (obranch == nullptr) + return -ENOENT; + + paths_.emplace_back(obranch); + + return 0; +} + +int Policy::EPLUP::Action::operator()(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) const +{ + return ::_action(branches_, fusepath_, paths_); +} + +int Policy::EPLUP::Create::operator()(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) const +{ + return ::_create(branches_, fusepath_, paths_); +} + +int Policy::EPLUP::Search::operator()(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) const +{ + return ::_search(branches_, fusepath_, paths_); +} diff --git a/src/policy_eplup.hpp b/src/policy_eplup.hpp new file mode 100644 index 00000000..c9a13a33 --- /dev/null +++ b/src/policy_eplup.hpp @@ -0,0 +1,70 @@ +/* + ISC License + + Copyright (c) 2025, François-Xavier Payet + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +#include "policy.hpp" + +namespace Policy +{ + namespace EPLUP + { + class Action final : public Policy::ActionImpl + { + public: + Action() + : Policy::ActionImpl("eplup") + { + } + + public: + int operator()(const Branches::Ptr &, + const fs::path &, + std::vector &) const final; + }; + + class Create final : public Policy::CreateImpl + { + public: + Create() + : Policy::CreateImpl("eplup") + { + } + + public: + int operator()(const Branches::Ptr &, + const fs::path &, + std::vector &) const final; + bool path_preserving(void) const final { return true; } + }; + + class Search final : public Policy::SearchImpl + { + public: + Search() + : Policy::SearchImpl("eplup") + { + } + + public: + int operator()(const Branches::Ptr &, + const fs::path &, + std::vector &) const final; + }; + } +} diff --git a/src/policy_lup.cpp b/src/policy_lup.cpp new file mode 100644 index 00000000..9f9f452f --- /dev/null +++ b/src/policy_lup.cpp @@ -0,0 +1,112 @@ +/* + Copyright (c) 2025, François-Xavier Payet + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "policy_lup.hpp" + +#include "errno.hpp" +#include "fs_info.hpp" +#include "fs_path.hpp" +#include "fs_statvfs_cache.hpp" +#include "policy.hpp" +#include "policy_error.hpp" +#include "rwlock.hpp" + +#include +#include + +using std::string; + +static int +_create(const Branches::Ptr &branches_, + std::vector &paths_) +{ + int rv; + int error; + fs::info_t info; + Branch *obranch; + + obranch = nullptr; + error = ENOENT; + + uint64_t best_used = 0; + uint64_t best_total = 1; + + for (auto &branch : *branches_) + { + if (branch.ro_or_nc()) + error_and_continue(error, EROFS); + rv = fs::info(branch.path, &info); + if (rv < 0) + error_and_continue(error, ENOENT); + if (info.readonly) + error_and_continue(error, EROFS); + if (info.spaceavail < branch.minfreespace()) + error_and_continue(error, ENOSPC); + + uint64_t used = info.spaceused; + uint64_t total = info.spaceused + info.spaceavail; + if (total == 0) + { + used = 0; + total = 1; + } + + if (obranch == nullptr) + { + best_used = used; + best_total = total; + obranch = &branch; + continue; + } + + unsigned __int128 lhs = (unsigned __int128)used * (unsigned __int128)best_total; + unsigned __int128 rhs = (unsigned __int128)best_used * (unsigned __int128)total; + if (lhs >= rhs) + continue; + + best_used = used; + best_total = total; + obranch = &branch; + } + + if (obranch == nullptr) + return -error; + + paths_.push_back(obranch); + + return 0; +} + +int Policy::LUP::Action::operator()(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) const +{ + return Policies::Action::eplup(branches_, fusepath_, paths_); +} + +int Policy::LUP::Create::operator()(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) const +{ + return ::_create(branches_, paths_); +} + +int Policy::LUP::Search::operator()(const Branches::Ptr &branches_, + const fs::path &fusepath_, + std::vector &paths_) const +{ + return Policies::Search::eplup(branches_, fusepath_, paths_); +} diff --git a/src/policy_lup.hpp b/src/policy_lup.hpp new file mode 100644 index 00000000..83d30ffb --- /dev/null +++ b/src/policy_lup.hpp @@ -0,0 +1,70 @@ +/* + ISC License + + Copyright (c) 2025, François-Xavier Payet + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +#include "policy.hpp" + +namespace Policy +{ + namespace LUP + { + class Action final : public Policy::ActionImpl + { + public: + Action() + : Policy::ActionImpl("lup") + { + } + + public: + int operator()(const Branches::Ptr &, + const fs::path &, + std::vector &) const final; + }; + + class Create final : public Policy::CreateImpl + { + public: + Create() + : Policy::CreateImpl("lup") + { + } + + public: + int operator()(const Branches::Ptr &, + const fs::path &, + std::vector &) const final; + bool path_preserving() const final { return false; } + }; + + class Search final : public Policy::SearchImpl + { + public: + Search() + : Policy::SearchImpl("lup") + { + } + + public: + int operator()(const Branches::Ptr &, + const fs::path &, + std::vector &) const final; + }; + } +}