diff --git a/mkdocs/docs/config/functions_categories_policies.md b/mkdocs/docs/config/functions_categories_policies.md index 0a99c64e..1318dbb3 100644 --- a/mkdocs/docs/config/functions_categories_policies.md +++ b/mkdocs/docs/config/functions_categories_policies.md @@ -116,6 +116,7 @@ but it makes things a bit more uniform. | mfs (most free space) | Pick the branch with the most available free space. | | ff (first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found. | | lfs (least free space) | Pick the branch with the least available free space. | +| lup (least used percent) | Pick the branch with the least used percentage of space | | lus (least used space) | Pick the branch with the least used space. | | all | Search: For **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. | | msppfrd (most shared path, percentage free random distribution) | Like **eppfrd** but if it fails to find a branch it will try again with the parent directory. Continues this pattern till finding one. | diff --git a/src/policies.cpp b/src/policies.cpp index 4886a666..d15a869c 100644 --- a/src/policies.cpp +++ b/src/policies.cpp @@ -35,6 +35,7 @@ FUNC(ff) \ FUNC(lfs) \ FUNC(lus) \ + FUNC(lup) \ FUNC(mfs) \ FUNC(msplfs) \ FUNC(msplus) \ @@ -80,6 +81,7 @@ Policy::ERoFS::Action Policies::Action::erofs; Policy::FF::Action Policies::Action::ff; Policy::LFS::Action Policies::Action::lfs; Policy::LUS::Action Policies::Action::lus; +Policy::LUP::Action Policies::Action::lup; Policy::MFS::Action Policies::Action::mfs; Policy::MSPLFS::Action Policies::Action::msplfs; Policy::MSPLUS::Action Policies::Action::msplus; @@ -101,6 +103,7 @@ Policy::ERoFS::Create Policies::Create::erofs; Policy::FF::Create Policies::Create::ff; Policy::LFS::Create Policies::Create::lfs; Policy::LUS::Create Policies::Create::lus; +Policy::LUP::Create Policies::Create::lup; Policy::MFS::Create Policies::Create::mfs; Policy::MSPLFS::Create Policies::Create::msplfs; Policy::MSPLUS::Create Policies::Create::msplus; @@ -122,6 +125,7 @@ Policy::ERoFS::Search Policies::Search::erofs; Policy::FF::Search Policies::Search::ff; Policy::LFS::Search Policies::Search::lfs; Policy::LUS::Search Policies::Search::lus; +Policy::LUP::Search Policies::Search::lup; Policy::MFS::Search Policies::Search::mfs; Policy::MSPLFS::Search Policies::Search::msplfs; Policy::MSPLUS::Search Policies::Search::msplus; diff --git a/src/policies.hpp b/src/policies.hpp index 3ff833c5..5c6a9430 100644 --- a/src/policies.hpp +++ b/src/policies.hpp @@ -30,6 +30,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" @@ -56,6 +57,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; @@ -82,6 +84,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; @@ -108,6 +111,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_lup.cpp b/src/policy_lup.cpp new file mode 100644 index 00000000..a29de06d --- /dev/null +++ b/src/policy_lup.cpp @@ -0,0 +1,240 @@ +/* + Copyright (c) 2025, Antonio SJ Musumeci and contributors + + 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_exists.hpp" +#include "fs_info.hpp" +#include "fs_path.hpp" +#include "policies.hpp" +#include "fs_statvfs_cache.hpp" +#include "policy.hpp" +#include "policy_error.hpp" +#include "rwlock.hpp" + +#include +#include + +using std::string; + +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_.push_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_.push_back(obranch); + + return 0; +} + +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 ::_action(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 ::_search(branches_, fusepath_, paths_); +} diff --git a/src/policy_lup.hpp b/src/policy_lup.hpp new file mode 100644 index 00000000..25b6be32 --- /dev/null +++ b/src/policy_lup.hpp @@ -0,0 +1,67 @@ +/* + ISC License + + Copyright (c) 2025, Antonio SJ Musumeci and contributors + + 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; + }; + } +}