From f7d3e8bf47c08ffc17c93ee07c76a30d130db5a2 Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Sat, 7 May 2016 14:05:38 -0400 Subject: [PATCH] create lus (least used space) policy. closes #273 --- README.md | 1 + src/fs.cpp | 18 +++++- src/fs.hpp | 6 +- src/policy.cpp | 2 + src/policy.hpp | 3 + src/policy_all.cpp | 3 +- src/policy_eplfs.cpp | 3 +- src/policy_epmfs.cpp | 3 +- src/policy_ff.cpp | 3 +- src/policy_lfs.cpp | 3 +- src/policy_lus.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++ src/policy_mfs.cpp | 3 +- src/statvfs_util.hpp | 8 +++ 13 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 src/policy_lus.cpp diff --git a/README.md b/README.md index de9af277..5a17351d 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ Due to FUSE limitations **ioctl** behaves differently if its acting on a directo | erofs | Exclusively return **-1** with **errno** set to **EROFS**. By setting **create** functions to this you can in effect turn the filesystem readonly. | | ff (first found) | Given the order of the drives, as defined at mount time or when configured via xattr interface, act on the first one found. For **create** category it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). | | lfs (least free space) | Pick the drive with the least available free space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. | +| lus (least used space) | Pick the drive with the least used space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. | | mfs (most free space) | Pick the drive with the most available free space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **ff**. | | newest (newest file) | Pick the file / directory with the largest mtime. For **create** category it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). | | rand (random) | Calls **all** and then randomizes. | diff --git a/src/fs.cpp b/src/fs.cpp index 9111d9c8..ecfcc72f 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -73,7 +73,8 @@ namespace fs bool info(const string &path, bool &readonly, - size_t &spaceavail) + size_t &spaceavail, + size_t &spaceused) { bool rv; struct statvfs st; @@ -83,6 +84,7 @@ namespace fs { readonly = StatVFS::readonly(st); spaceavail = StatVFS::spaceavail(st); + spaceused = StatVFS::spaceused(st); } return rv; @@ -113,6 +115,20 @@ namespace fs return rv; } + bool + spaceused(const string &path, + size_t &spaceused) + { + bool rv; + struct statvfs st; + + rv = fs::statvfs(path,st); + if(rv) + spaceused = StatVFS::spaceused(st); + + return rv; + } + void findallfiles(const vector &srcmounts, const char *fusepath, diff --git a/src/fs.hpp b/src/fs.hpp index 25049fee..84a79c26 100644 --- a/src/fs.hpp +++ b/src/fs.hpp @@ -35,13 +35,17 @@ namespace fs bool info(const string &path, bool &readonly, - size_t &spaceavail); + size_t &spaceavail, + size_t &spaceused); bool readonly(const string &path); bool spaceavail(const string &path, size_t &spaceavail); + bool spaceused(const string &path, + size_t &spaceavail); + void findallfiles(const vector &srcmounts, const char *fusepath, vector &paths); diff --git a/src/policy.cpp b/src/policy.cpp index d779c118..e83f79b7 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -35,6 +35,7 @@ namespace mergerfs (POLICY(erofs,DOESNT_PRESERVE_PATH)) (POLICY(ff,DOESNT_PRESERVE_PATH)) (POLICY(lfs,DOESNT_PRESERVE_PATH)) + (POLICY(lus,DOESNT_PRESERVE_PATH)) (POLICY(mfs,DOESNT_PRESERVE_PATH)) (POLICY(newest,DOESNT_PRESERVE_PATH)) (POLICY(rand,DOESNT_PRESERVE_PATH)); @@ -50,6 +51,7 @@ namespace mergerfs CONST_POLICY(erofs); CONST_POLICY(ff); CONST_POLICY(lfs); + CONST_POLICY(lus); CONST_POLICY(mfs); CONST_POLICY(newest); CONST_POLICY(rand); diff --git a/src/policy.hpp b/src/policy.hpp index dcaf220c..3807428b 100644 --- a/src/policy.hpp +++ b/src/policy.hpp @@ -48,6 +48,7 @@ namespace mergerfs erofs, ff, lfs, + lus, mfs, newest, rand, @@ -100,6 +101,7 @@ namespace mergerfs static int erofs(CType,cstrvec&,const char *,csize_t,cstrptrvec&); static int ff(CType,cstrvec&,const char *,csize_t,cstrptrvec&); static int lfs(CType,cstrvec&,const char *,csize_t,cstrptrvec&); + static int lus(CType,cstrvec&,const char *,csize_t,cstrptrvec&); static int mfs(CType,cstrvec&,const char *,csize_t,cstrptrvec&); static int newest(CType,cstrvec&,const char *,csize_t,cstrptrvec&); static int rand(CType,cstrvec&,const char *,csize_t,cstrptrvec&); @@ -171,6 +173,7 @@ namespace mergerfs static const Policy &erofs; static const Policy &ff; static const Policy &lfs; + static const Policy &lus; static const Policy &mfs; static const Policy &newest; static const Policy &rand; diff --git a/src/policy_all.cpp b/src/policy_all.cpp index 01e5fbe5..b5581383 100644 --- a/src/policy_all.cpp +++ b/src/policy_all.cpp @@ -37,9 +37,10 @@ _all_create(const vector &basepaths, { bool readonly; size_t spaceavail; + size_t _spaceused; const string *basepath = &basepaths[i]; - if(!fs::info(*basepath,readonly,spaceavail)) + if(!fs::info(*basepath,readonly,spaceavail,_spaceused)) continue; if(readonly) continue; diff --git a/src/policy_eplfs.cpp b/src/policy_eplfs.cpp index ecf953ac..df139d57 100644 --- a/src/policy_eplfs.cpp +++ b/src/policy_eplfs.cpp @@ -46,13 +46,14 @@ _eplfs_create(const vector &basepaths, { bool readonly; size_t spaceavail; + size_t _spaceused; const string *basepath = &basepaths[i]; fs::path::make(basepath,fusepath,fullpath); if(!fs::exists(fullpath)) continue; - if(!fs::info(*basepath,readonly,spaceavail)) + if(!fs::info(*basepath,readonly,spaceavail,_spaceused)) continue; if(readonly) continue; diff --git a/src/policy_epmfs.cpp b/src/policy_epmfs.cpp index 4e91948c..1db62095 100644 --- a/src/policy_epmfs.cpp +++ b/src/policy_epmfs.cpp @@ -46,13 +46,14 @@ _epmfs_create(const vector &basepaths, { bool readonly; size_t spaceavail; + size_t _spaceused; const string *basepath = &basepaths[i]; fs::path::make(basepath,fusepath,fullpath); if(!fs::exists(fullpath)) continue; - if(!fs::info(*basepath,readonly,spaceavail)) + if(!fs::info(*basepath,readonly,spaceavail,_spaceused)) continue; if(readonly) continue; diff --git a/src/policy_ff.cpp b/src/policy_ff.cpp index 1ea7416d..acf5217a 100644 --- a/src/policy_ff.cpp +++ b/src/policy_ff.cpp @@ -40,9 +40,10 @@ _ff_create(const vector &basepaths, { bool readonly; size_t spaceavail; + size_t _spaceused; const string *basepath = &basepaths[i]; - if(!fs::info(*basepath,readonly,spaceavail)) + if(!fs::info(*basepath,readonly,spaceavail,_spaceused)) continue; if(readonly) continue; diff --git a/src/policy_lfs.cpp b/src/policy_lfs.cpp index 1e1f2e7e..b371ac1d 100644 --- a/src/policy_lfs.cpp +++ b/src/policy_lfs.cpp @@ -45,9 +45,10 @@ _lfs_create(const vector &basepaths, { bool readonly; size_t spaceavail; + size_t _spaceused; const string *basepath = &basepaths[i]; - if(!fs::info(*basepath,readonly,spaceavail)) + if(!fs::info(*basepath,readonly,spaceavail,_spaceused)) continue; if(readonly) continue; diff --git a/src/policy_lus.cpp b/src/policy_lus.cpp new file mode 100644 index 00000000..a58b168f --- /dev/null +++ b/src/policy_lus.cpp @@ -0,0 +1,141 @@ +/* + 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 + +#include +#include +#include + +#include "fs.hpp" +#include "fs_path.hpp" +#include "policy.hpp" + +using std::string; +using std::vector; +using std::size_t; +using mergerfs::Category; + +static +int +_lus_create(const vector &basepaths, + const size_t minfreespace, + vector &paths) +{ + string fullpath; + size_t lus; + const string *lusbasepath; + + lus = std::numeric_limits::max(); + lusbasepath = NULL; + for(size_t i = 0, ei = basepaths.size(); i != ei; i++) + { + bool readonly; + size_t spaceused; + size_t spaceavail; + const string *basepath = &basepaths[i]; + + if(!fs::info(*basepath,readonly,spaceavail,spaceused)) + continue; + if(readonly) + continue; + if(spaceavail < minfreespace) + continue; + if(spaceused >= lus) + continue; + + lus = spaceused; + lusbasepath = basepath; + } + + if(lusbasepath == NULL) + return POLICY_FAIL_ENOENT; + + paths.push_back(lusbasepath); + + return POLICY_SUCCESS; +} + +static +int +_lus_other(const vector &basepaths, + const char *fusepath, + vector &paths) +{ + string fullpath; + size_t lus; + const string *lusbasepath; + + lus = 0; + lusbasepath = NULL; + for(size_t i = 0, ei = basepaths.size(); i != ei; i++) + { + size_t spaceused; + const string *basepath = &basepaths[i]; + + fs::path::make(basepath,fusepath,fullpath); + + if(!fs::exists(fullpath)) + continue; + if(!fs::spaceused(*basepath,spaceused)) + continue; + if(spaceused >= lus) + continue; + + lus = spaceused; + lusbasepath = basepath; + } + + if(lusbasepath == NULL) + return POLICY_FAIL_ENOENT; + + paths.push_back(lusbasepath); + + return POLICY_SUCCESS; +} + +static +int +_lus(const Category::Enum::Type type, + const vector &basepaths, + const char *fusepath, + const size_t minfreespace, + vector &paths) +{ + if(type == Category::Enum::create) + return _lus_create(basepaths,minfreespace,paths); + + return _lus_other(basepaths,fusepath,paths); +} + +namespace mergerfs +{ + int + Policy::Func::lus(const Category::Enum::Type type, + const vector &basepaths, + const char *fusepath, + const size_t minfreespace, + vector &paths) + { + int rv; + + rv = _lus(type,basepaths,fusepath,minfreespace,paths); + if(POLICY_FAILED(rv)) + rv = Policy::Func::mfs(type,basepaths,fusepath,minfreespace,paths); + + return rv; + } +} diff --git a/src/policy_mfs.cpp b/src/policy_mfs.cpp index a6657c49..aa6772ef 100644 --- a/src/policy_mfs.cpp +++ b/src/policy_mfs.cpp @@ -43,9 +43,10 @@ _mfs_create(const vector &basepaths, { bool readonly; size_t spaceavail; + size_t _spaceused; const string *basepath = &basepaths[i]; - if(!fs::info(*basepath,readonly,spaceavail)) + if(!fs::info(*basepath,readonly,spaceavail,_spaceused)) continue; if(readonly) continue; diff --git a/src/statvfs_util.hpp b/src/statvfs_util.hpp index 4cc6266d..06fe810b 100644 --- a/src/statvfs_util.hpp +++ b/src/statvfs_util.hpp @@ -52,4 +52,12 @@ namespace StatVFS { return (st.f_frsize * st.f_bavail); } + + static + inline + fsblkcnt_t + spaceused(const struct statvfs &st) + { + return (st.f_frsize * (st.f_blocks - st.f_bavail)); + } }