From 046844083f0f98ce18dfc44d8b212672913a36bb Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Wed, 19 Aug 2020 17:14:37 -0400 Subject: [PATCH] add {,ep,msp}pfrd policies Percentage Free Random Distribution Chooses a random branch based on the available space percentage free. IE: if branch A has 1G free and branch B has 2G then B should be chosen twice as often. --- README.md | 23 ++-- man/mergerfs.1 | 23 +++- src/policy.cpp | 18 ++- src/policy.hpp | 9 ++ src/policy_eppfrd.cpp | 273 +++++++++++++++++++++++++++++++++++++++++ src/policy_msppfrd.cpp | 172 ++++++++++++++++++++++++++ src/policy_pfrd.cpp | 140 +++++++++++++++++++++ src/rnd.cpp | 55 +++++++++ src/rnd.hpp | 33 +++++ 9 files changed, 729 insertions(+), 17 deletions(-) create mode 100644 src/policy_eppfrd.cpp create mode 100644 src/policy_msppfrd.cpp create mode 100644 src/policy_pfrd.cpp create mode 100644 src/rnd.cpp create mode 100644 src/rnd.hpp diff --git a/README.md b/README.md index f814dde4..1e6c8a68 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ % mergerfs(1) mergerfs user manual % Antonio SJ Musumeci -% 2020-08-07 +% 2020-08-20 # NAME @@ -306,22 +306,25 @@ Because of the nature of the behavior the policies act diffierently depending on | Policy | Description | |------------------|------------------------------------------------------------| -| all | Search: same as **epall**. Action: same as **epall**. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. | -| epall (existing path, all) | Search: same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action: apply to all found. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). | +| all | Search: Same as **epall**. Action: Same as **epall**. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. | +| epall (existing path, all) | Search: Same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action: apply to all found. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). | | epff (existing path, first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found where the relative path exists. | | eplfs (existing path, least free space) | Of all the branches on which the relative path exists choose the drive with the least free space. | | eplus (existing path, least used space) | Of all the branches on which the relative path exists choose the drive with the least used space. | | epmfs (existing path, most free space) | Of all the branches on which the relative path exists choose the drive with the most free space. | +| eppfrd (existing path, percentage free random distribution) | Like **pfrd** but limited to existing paths. | | eprand (existing path, random) | Calls **epall** and then randomizes. Returns 1. | | erofs | Exclusively return **-1** with **errno** set to **EROFS** (read-only filesystem). | -| ff (first found) | Search: same as **epff**. Action: same as **epff**. Create: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. | -| lfs (least free space) | Search: same as **eplfs**. Action: same as **eplfs**. Create: Pick the drive with the least available free space. | -| lus (least used space) | Search: same as **eplus**. Action: same as **eplus**. Create: Pick the drive with the least used space. | -| mfs (most free space) | Search: same as **epmfs**. Action: same as **epmfs**. Create: Pick the drive with the most available free space. | -| msplfs (most shared path, least free space) | Search: same as **eplfs**. Action: same as **eplfs**. Create: like **eplfs** but walk back the path if it fails to find a branch at that level. | -| msplus (most shared path, least used space) | Search: same as **eplus**. Action: same as **eplus**. Create: like **eplus** but walk back the path if it fails to find a branch at that level. | -| mspmfs (most shared path, most free space) | Search: same as **epmfss**. Action: same as **epmfs**. Create: like **eplmfs** but walk back the path if it fails to find a branch at that level. | +| ff (first found) | Search: Same as **epff**. Action: Same as **epff**. Create: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. | +| lfs (least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: Pick the drive with the least available free space. | +| lus (least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: Pick the drive with the least used space. | +| mfs (most free space) | Search: Same as **epmfs**. Action: Same as **epmfs**. Create: Pick the drive with the most available free space. | +| msplfs (most shared path, least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: like **eplfs** but walk back the path if it fails to find a branch at that level. | +| msplus (most shared path, least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: like **eplus** but walk back the path if it fails to find a branch at that level. | +| mspmfs (most shared path, most free space) | Search: Same as **epmfss**. Action: Same as **epmfs**. Create: like **eplmfs** but walk back the path if it fails to find a branch at that level. | +| msppfrd (most shared path, percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Like **eppfrd** but will walk back the path if it fails to find a branch at that level. | | newest | Pick the file / directory with the largest mtime. | +| pfrd (percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Chooses a branch at random with the likelihood of selection based on a branch's available space relative to the total. | | rand (random) | Calls **all** and then randomizes. Returns 1. | **NOTE:** If you are using an underlying filesystem that reserves blocks such as ext2, ext3, or ext4 be aware that mergerfs respects the reservation by using `f_bavail` (number of free blocks for unprivileged users) rather than `f_bfree` (number of free blocks) in policy calculations. **df** does NOT use `f_bavail`, it uses `f_bfree`, so direct comparisons between **df** output and mergerfs' policies is not appropriate. diff --git a/man/mergerfs.1 b/man/mergerfs.1 index 6e9cdb0d..8ddf23a0 100644 --- a/man/mergerfs.1 +++ b/man/mergerfs.1 @@ -1,7 +1,7 @@ .\"t .\" Automatically generated by Pandoc 1.19.2.4 .\" -.TH "mergerfs" "1" "2020\-08\-07" "mergerfs user manual" "" +.TH "mergerfs" "1" "2020\-08\-20" "mergerfs user manual" "" .hy .SH NAME .PP @@ -778,6 +778,11 @@ Of all the branches on which the relative path exists choose the drive with the most free space. T} T{ +eppfrd (existing path, percentage free random distribution) +T}@T{ +Like \f[B]pfrd\f[] but limited to existing paths. +T} +T{ eprand (existing path, random) T}@T{ Calls \f[B]epall\f[] and then randomizes. @@ -843,11 +848,27 @@ Create: like \f[B]eplmfs\f[] but walk back the path if it fails to find a branch at that level. T} T{ +msppfrd (most shared path, percentage free random distribution) +T}@T{ +Search: same as \f[B]eppfrd\f[]. +Action: same as \f[B]eppfrd\f[]. +Create: Like \f[B]eppfrd\f[] but will walk back the path if it fails to +find a branch at that level. +T} +T{ newest T}@T{ Pick the file / directory with the largest mtime. T} T{ +pfrd (percentage free random distribution) +T}@T{ +Search: same as \f[B]eppfrd\f[]. +Action: same as \f[B]eppfrd\f[]. +Create: Chooses a branch at random with the likelihood of selection +based on a branch\[aq]s available space relative to the total. +T} +T{ rand (random) T}@T{ Calls \f[B]all\f[] and then randomizes. diff --git a/src/policy.cpp b/src/policy.cpp index 3e62a638..3e854bad 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -32,6 +32,7 @@ const std::vector Policy::_policies_ = (POLICY(eplfs,PRESERVES_PATH)) (POLICY(eplus,PRESERVES_PATH)) (POLICY(epmfs,PRESERVES_PATH)) + (POLICY(eppfrd,PRESERVES_PATH)) (POLICY(eprand,PRESERVES_PATH)) (POLICY(erofs,DOESNT_PRESERVE_PATH)) (POLICY(ff,DOESNT_PRESERVE_PATH)) @@ -41,7 +42,9 @@ const std::vector Policy::_policies_ = (POLICY(msplfs,PRESERVES_PATH)) (POLICY(msplus,PRESERVES_PATH)) (POLICY(mspmfs,PRESERVES_PATH)) + (POLICY(msppfrd,PRESERVES_PATH)) (POLICY(newest,DOESNT_PRESERVE_PATH)) + (POLICY(pfrd,DOESNT_PRESERVE_PATH)) (POLICY(rand,DOESNT_PRESERVE_PATH)); const Policy * const Policy::policies = &_policies_[1]; @@ -55,6 +58,7 @@ CONST_POLICY(epff); CONST_POLICY(eplfs); CONST_POLICY(eplus); CONST_POLICY(epmfs); +CONST_POLICY(eppfrd); CONST_POLICY(eprand); CONST_POLICY(erofs); CONST_POLICY(ff); @@ -64,19 +68,21 @@ CONST_POLICY(mfs); CONST_POLICY(msplfs); CONST_POLICY(msplus); CONST_POLICY(mspmfs); +CONST_POLICY(msppfrd); CONST_POLICY(newest); +CONST_POLICY(pfrd); CONST_POLICY(rand); const Policy& Policy::find(const std::string &str) { -for(int i = Enum::BEGIN; i != Enum::END; ++i) - { - if(policies[i] == str) - return policies[i]; - } + for(int i = Enum::BEGIN; i != Enum::END; ++i) + { + if(policies[i] == str) + return policies[i]; + } - return invalid; + return invalid; } const Policy& diff --git a/src/policy.hpp b/src/policy.hpp index 9e722cd6..1b90c3db 100644 --- a/src/policy.hpp +++ b/src/policy.hpp @@ -37,6 +37,7 @@ public: eplfs, eplus, epmfs, + eppfrd, eprand, erofs, ff, @@ -46,7 +47,9 @@ public: msplfs, msplus, mspmfs, + msppfrd, newest, + pfrd, rand, END }; @@ -117,6 +120,7 @@ public: static int eplfs(Category,const Branches&,const char *,cuint64_t,strvec*); static int eplus(Category,const Branches&,const char *,cuint64_t,strvec*); static int epmfs(Category,const Branches&,const char *,cuint64_t,strvec*); + static int eppfrd(Category,const Branches&,const char *,cuint64_t,strvec*); static int eprand(Category,const Branches&,const char *,cuint64_t,strvec*); static int erofs(Category,const Branches&,const char *,cuint64_t,strvec*); static int ff(Category,const Branches&,const char *,cuint64_t,strvec*); @@ -126,7 +130,9 @@ public: static int msplfs(Category,const Branches&,const char *,cuint64_t,strvec*); static int msplus(Category,const Branches&,const char *,cuint64_t,strvec*); static int mspmfs(Category,const Branches&,const char *,cuint64_t,strvec*); + static int msppfrd(Category,const Branches&,const char *,cuint64_t,strvec*); static int newest(Category,const Branches&,const char *,cuint64_t,strvec*); + static int pfrd(Category,const Branches&,const char *,cuint64_t,strvec*); static int rand(Category,const Branches&,const char *,cuint64_t,strvec*); }; @@ -198,6 +204,7 @@ public: static const Policy &eplfs; static const Policy ⩱ static const Policy &epmfs; + static const Policy &eppfrd; static const Policy &eprand; static const Policy &erofs; static const Policy &ff; @@ -207,7 +214,9 @@ public: static const Policy &msplfs; static const Policy &msplus; static const Policy &mspmfs; + static const Policy &msppfrd; static const Policy &newest; + static const Policy &pfrd; static const Policy &rand; }; diff --git a/src/policy_eppfrd.cpp b/src/policy_eppfrd.cpp new file mode 100644 index 00000000..72cb2e3f --- /dev/null +++ b/src/policy_eppfrd.cpp @@ -0,0 +1,273 @@ +/* + Copyright (c) 2016, Antonio SJ Musumeci + + 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 "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 "rnd.hpp" +#include "rwlock.hpp" + +#include +#include + +using std::string; +using std::vector; + +struct BranchInfo +{ + uint64_t spaceavail; + const string *basepath; +}; + +typedef vector BranchInfoVec; + +namespace eppfrd +{ + static + int + get_branchinfo_create(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + BranchInfoVec *branchinfo_, + uint64_t *sum_) + { + int rv; + int error; + BranchInfo bi; + fs::info_t info; + const Branch *branch; + rwlock::ReadGuard guard(&branches_.lock); + + *sum_ = 0; + error = ENOENT; + for(size_t i = 0, ei = branches_.size(); i < ei; i++) + { + branch = &branches_[i]; + + 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 == -1) + error_and_continue(error,ENOENT); + if(info.readonly) + error_and_continue(error,EROFS); + if(info.spaceavail < minfreespace_) + error_and_continue(error,ENOSPC); + + *sum_ += info.spaceavail; + + bi.spaceavail = info.spaceavail; + bi.basepath = &branch->path; + branchinfo_->push_back(bi); + } + + return error; + } + + static + int + get_branchinfo_action(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + BranchInfoVec *branchinfo_, + uint64_t *sum_) + { + int rv; + int error; + BranchInfo bi; + fs::info_t info; + const Branch *branch; + rwlock::ReadGuard guard(&branches_.lock); + + *sum_ = 0; + error = ENOENT; + for(size_t i = 0, ei = branches_.size(); i < ei; i++) + { + branch = &branches_[i]; + + 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 == -1) + error_and_continue(error,ENOENT); + if(info.readonly) + error_and_continue(error,EROFS); + + *sum_ += info.spaceavail; + + bi.spaceavail = info.spaceavail; + bi.basepath = &branch->path; + branchinfo_->push_back(bi); + } + + return error; + } + + static + int + get_branchinfo_search(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + BranchInfoVec *branchinfo_, + uint64_t *sum_) + { + int rv; + BranchInfo bi; + uint64_t spaceavail; + const Branch *branch; + rwlock::ReadGuard guard(&branches_.lock); + + *sum_ = 0; + for(size_t i = 0, ei = branches_.size(); i < ei; i++) + { + branch = &branches_[i]; + + if(!fs::exists(branch->path,fusepath_)) + continue; + rv = fs::statvfs_cache_spaceavail(branch->path,&spaceavail); + if(rv == -1) + continue; + + *sum_ += spaceavail; + + bi.spaceavail = spaceavail; + bi.basepath = &branch->path; + branchinfo_->push_back(bi); + } + + return ENOENT; + } + + static + const + string* + get_branch(const BranchInfoVec &branchinfo_, + const uint64_t sum_) + { + uint64_t idx; + uint64_t threshold; + + idx = 0; + threshold = RND::rand64(sum_); + for(size_t i = 0; i < branchinfo_.size(); i++) + { + idx += branchinfo_[i].spaceavail; + + if(idx < threshold) + continue; + + return branchinfo_[i].basepath; + } + + return NULL; + } + + static + int + create(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) + { + int error; + uint64_t sum; + const string *basepath; + BranchInfoVec branchinfo; + + branchinfo.reserve(branches_.size()); + error = eppfrd::get_branchinfo_create(branches_,fusepath_,minfreespace_,&branchinfo,&sum); + basepath = eppfrd::get_branch(branchinfo,sum); + if(basepath == NULL) + return (errno=error,-1); + + paths_->push_back(*basepath); + + return 0; + } + + static + int + action(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) + { + int error; + uint64_t sum; + const string *basepath; + BranchInfoVec branchinfo; + + branchinfo.reserve(branches_.size()); + error = eppfrd::get_branchinfo_action(branches_,fusepath_,minfreespace_,&branchinfo,&sum); + basepath = eppfrd::get_branch(branchinfo,sum); + if(basepath == NULL) + return (errno=error,-1); + + paths_->push_back(*basepath); + + return 0; + } + + static + int + search(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) + { + int error; + uint64_t sum; + const string *basepath; + BranchInfoVec branchinfo; + + branchinfo.reserve(branches_.size()); + error = eppfrd::get_branchinfo_search(branches_,fusepath_,minfreespace_,&branchinfo,&sum); + basepath = eppfrd::get_branch(branchinfo,sum); + if(basepath == NULL) + return (errno=error,-1); + + paths_->push_back(*basepath); + + return 0; + } +} + +int +Policy::Func::eppfrd(const Category type_, + const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) +{ + switch(type_) + { + case Category::CREATE: + return eppfrd::create(branches_,fusepath_,minfreespace_,paths_); + case Category::ACTION: + return eppfrd::action(branches_,fusepath_,minfreespace_,paths_); + default: + case Category::SEARCH: + return eppfrd::search(branches_,fusepath_,minfreespace_,paths_); + } +} diff --git a/src/policy_msppfrd.cpp b/src/policy_msppfrd.cpp new file mode 100644 index 00000000..971352a8 --- /dev/null +++ b/src/policy_msppfrd.cpp @@ -0,0 +1,172 @@ +/* + ISC License + + Copyright (c) 2020, Antonio SJ Musumeci + + 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 "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 "rnd.hpp" +#include "rwlock.hpp" + +#include +#include + +using std::string; +using std::vector; + +struct BranchInfo +{ + uint64_t spaceavail; + const string *basepath; +}; + +typedef vector BranchInfoVec; + +namespace msppfrd +{ + static + int + create_1(const Branches &branches_, + const string &fusepath_, + const uint64_t minfreespace_, + BranchInfoVec *branchinfo_, + uint64_t *sum_) + { + int rv; + int error; + BranchInfo bi; + fs::info_t info; + const Branch *branch; + rwlock::ReadGuard guard(&branches_.lock); + + *sum_ = 0; + error = ENOENT; + for(size_t i = 0, ei = branches_.size(); i < ei; i++) + { + branch = &branches_[i]; + + 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 == -1) + error_and_continue(error,ENOENT); + if(info.readonly) + error_and_continue(error,EROFS); + if(info.spaceavail < minfreespace_) + error_and_continue(error,ENOSPC); + + *sum_ += info.spaceavail; + + bi.spaceavail = info.spaceavail; + bi.basepath = &branch->path; + branchinfo_->push_back(bi); + } + + return error; + } + + static + int + get_branchinfo(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + BranchInfoVec *branchinfo_, + uint64_t *sum_) + { + int error; + string fusepath; + + fusepath = fusepath_; + do + { + error = msppfrd::create_1(branches_,fusepath,minfreespace_,branchinfo_,sum_); + if(branchinfo_->size()) + return error; + + fusepath = fs::path::dirname(fusepath); + } + while(!fusepath.empty()); + + return error; + } + + static + const + string* + get_branch(const BranchInfoVec &branchinfo_, + const uint64_t sum_) + { + uint64_t idx; + uint64_t threshold; + + idx = 0; + threshold = RND::rand64(sum_); + for(size_t i = 0; i < branchinfo_.size(); i++) + { + idx += branchinfo_[i].spaceavail; + + if(idx < threshold) + continue; + + return branchinfo_[i].basepath; + } + + return NULL; + } + + static + int + create(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) + { + int error; + uint64_t sum; + const string *basepath; + BranchInfoVec branchinfo; + + branchinfo.reserve(branches_.size()); + error = msppfrd::get_branchinfo(branches_,fusepath_,minfreespace_,&branchinfo,&sum); + basepath = msppfrd::get_branch(branchinfo,sum); + if(basepath == NULL) + return (errno=error,-1); + + paths_->push_back(*basepath); + + return 0; + } +} + +int +Policy::Func::msppfrd(const Category type_, + const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) +{ + if(type_ == Category::CREATE) + return msppfrd::create(branches_,fusepath_,minfreespace_,paths_); + + return Policy::Func::eppfrd(type_,branches_,fusepath_,minfreespace_,paths_); +} diff --git a/src/policy_pfrd.cpp b/src/policy_pfrd.cpp new file mode 100644 index 00000000..6272f6a8 --- /dev/null +++ b/src/policy_pfrd.cpp @@ -0,0 +1,140 @@ +/* + Copyright (c) 2016, Antonio SJ Musumeci + + 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 "errno.hpp" +#include "fs_info.hpp" +#include "fs_path.hpp" +#include "policy.hpp" +#include "policy_error.hpp" +#include "rnd.hpp" +#include "rwlock.hpp" + +#include +#include + +using std::string; +using std::vector; + +struct BranchInfo +{ + uint64_t spaceavail; + const string *basepath; +}; + +typedef vector BranchInfoVec; + +namespace pfrd +{ + static + int + get_branchinfo(const Branches &branches_, + const uint64_t minfreespace_, + BranchInfoVec *branchinfo_, + uint64_t *sum_) + { + int rv; + int error; + BranchInfo bi; + fs::info_t info; + const Branch *branch; + rwlock::ReadGuard guard(&branches_.lock); + + *sum_ = 0; + error = ENOENT; + for(size_t i = 0, ei = branches_.size(); i < ei; i++) + { + branch = &branches_[i]; + + if(branch->ro_or_nc()) + error_and_continue(error,EROFS); + rv = fs::info(branch->path,&info); + if(rv == -1) + error_and_continue(error,ENOENT); + if(info.readonly) + error_and_continue(error,EROFS); + if(info.spaceavail < minfreespace_) + error_and_continue(error,ENOSPC); + + *sum_ += info.spaceavail; + + bi.spaceavail = info.spaceavail; + bi.basepath = &branch->path; + branchinfo_->push_back(bi); + } + + return error; + } + + static + const + string* + get_branch(const BranchInfoVec &branchinfo_, + const uint64_t sum_) + { + uint64_t idx; + uint64_t threshold; + + idx = 0; + threshold = RND::rand64(sum_); + for(size_t i = 0; i < branchinfo_.size(); i++) + { + idx += branchinfo_[i].spaceavail; + + if(idx < threshold) + continue; + + return branchinfo_[i].basepath; + } + + return NULL; + } + + static + int + create(const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) + { + int error; + uint64_t sum; + const string *basepath; + BranchInfoVec branchinfo; + + branchinfo.reserve(branches_.size()); + error = pfrd::get_branchinfo(branches_,minfreespace_,&branchinfo,&sum); + basepath = pfrd::get_branch(branchinfo,sum); + if(basepath == NULL) + return (errno=error,-1); + + paths_->push_back(*basepath); + + return 0; + } +} + +int +Policy::Func::pfrd(const Category type_, + const Branches &branches_, + const char *fusepath_, + const uint64_t minfreespace_, + vector *paths_) +{ + if(type_ == Category::CREATE) + return pfrd::create(branches_,fusepath_,minfreespace_,paths_); + + return Policy::Func::eppfrd(type_,branches_,fusepath_,minfreespace_,paths_); +} diff --git a/src/rnd.cpp b/src/rnd.cpp new file mode 100644 index 00000000..5eab6ce1 --- /dev/null +++ b/src/rnd.cpp @@ -0,0 +1,55 @@ +/* + ISC License + + Copyright (c) 2020, Antonio SJ Musumeci + + 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 "rnd.hpp" + +#include "wyhash.h" + +#include +#include + +static uint64_t G_SEED; +static RND G_RND; + +RND::RND() +{ + struct timeval tv; + + gettimeofday(&tv,NULL); + + G_SEED = ((tv.tv_sec << 32) | (tv.tv_usec)); +} + +uint64_t +RND::rand64(void) +{ + return wyrand(&G_SEED); +} + +uint64_t +RND::rand64(const uint64_t max_) +{ + return (wyrand(&G_SEED) % max_); +} + +uint64_t +RND::rand64(const uint64_t min_, + const uint64_t max_) +{ + return (min_ + (wyrand(&G_SEED) % (max_ - min_))); +} diff --git a/src/rnd.hpp b/src/rnd.hpp new file mode 100644 index 00000000..dd9b6648 --- /dev/null +++ b/src/rnd.hpp @@ -0,0 +1,33 @@ +/* + ISC License + + Copyright (c) 2020, Antonio SJ Musumeci + + 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 + +class RND +{ +public: + RND(); + +public: + static uint64_t rand64(void); + static uint64_t rand64(const uint64_t max_); + static uint64_t rand64(const uint64_t min_, + const uint64_t max_); +};