diff --git a/src/fs.cpp b/src/fs.cpp index 27a1775b..d8c58871 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -31,28 +31,76 @@ #include "fs_attr.hpp" #include "fs_path.hpp" #include "fs_xattr.hpp" +#include "statvfs_util.hpp" #include "str.hpp" +#include "success_fail.hpp" using std::string; using std::vector; namespace fs { + bool + exists(const string &path, + struct stat &st) + { + int rv; + + rv = ::lstat(path.c_str(),&st); + + return LSTAT_SUCCEEDED(rv); + } + + bool + exists(const string &path, + struct statvfs &st) + { + int rv; + + rv = ::statvfs(path.c_str(),&st); + + return STATVFS_SUCCEEDED(rv); + } + + bool + exists(const string &path) + { + struct stat st; + + return exists(path,st); + } + + bool + exists_on_rw_fs(const string &path, + struct statvfs &st) + { + int rv; + + rv = ::statvfs(path.c_str(),&st); + + return (STATVFS_SUCCEEDED(rv) && !StatVFS::readonly(st)); + } + + bool + exists_on_rw_fs(const string &path) + { + struct statvfs st; + + return exists_on_rw_fs(path,st); + } + void findallfiles(const vector &srcmounts, const char *fusepath, vector &paths) { - int rv; string fullpath; - struct stat st; for(size_t i = 0, ei = srcmounts.size(); i != ei; i++) { fs::path::make(&srcmounts[i],fusepath,fullpath); - rv = ::lstat(fullpath.c_str(),&st); - if(rv == 0) + if(fs::exists(fullpath)) paths.push_back(fullpath); } } @@ -145,16 +193,14 @@ namespace fs mfsidx = -1; for(size_t i = 0, ei = basepaths.size(); i != ei; i++) { - int rv; - struct statvfs fsstats; + struct statvfs st; const string &basepath = basepaths[i]; - rv = ::statvfs(basepath.c_str(),&fsstats); - if(rv == 0) + if(fs::exists(basepath,st)) { fsblkcnt_t spaceavail; - spaceavail = (fsstats.f_frsize * fsstats.f_bavail); + spaceavail = StatVFS::spaceavail(st); if((spaceavail > mfs) && (spaceavail >= minfreespace)) { mfs = spaceavail; @@ -170,4 +216,23 @@ namespace fs return 0; } + + bool + available(const string &path, + const bool needswritablefs, + struct statvfs &st) + { + return (needswritablefs ? + fs::exists_on_rw_fs(path,st) : + fs::exists(path,st)); + } + + bool + available(const string &path, + const bool needswritablefs) + { + return (needswritablefs ? + fs::exists_on_rw_fs(path) : + fs::exists(path)); + } }; diff --git a/src/fs.hpp b/src/fs.hpp index 31933201..f0622a77 100644 --- a/src/fs.hpp +++ b/src/fs.hpp @@ -26,6 +26,16 @@ namespace fs using std::string; using std::vector; + bool exists(const string &path, + struct stat &st); + bool exists(const string &path, + struct statvfs &st); + bool exists(const string &path); + + bool exists_on_rw_fs(const string &path, + struct statvfs &st); + bool exists_on_rw_fs(const string &path); + void findallfiles(const vector &srcmounts, const char *fusepath, vector &paths); @@ -45,6 +55,13 @@ namespace fs int mfs(const vector &srcs, const size_t minfreespace, string &path); + + bool available(const string &path, + const bool needswritablefs); + + bool available(const string &path, + const bool needswritablefs, + struct statvfs &st); }; #endif // __FS_HPP__ diff --git a/src/policy_all.cpp b/src/policy_all.cpp index 85314a04..decc8bdf 100644 --- a/src/policy_all.cpp +++ b/src/policy_all.cpp @@ -14,17 +14,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include #include #include #include +#include "fs.hpp" #include "fs_path.hpp" #include "policy.hpp" -#include "success_fail.hpp" using std::string; using std::vector; @@ -34,10 +31,9 @@ static int _all(const vector &basepaths, const char *fusepath, + const bool needswritablefs, vector &paths) { - int rv; - struct stat st; string fullpath; for(size_t i = 0, ei = basepaths.size(); i != ei; i++) @@ -46,9 +42,10 @@ _all(const vector &basepaths, fs::path::make(basepath,fusepath,fullpath); - rv = ::lstat(fullpath.c_str(),&st); - if(LSTAT_SUCCEEDED(rv)) - paths.push_back(basepath); + if(!fs::available(fullpath,needswritablefs)) + continue; + + paths.push_back(basepath); } if(paths.empty()) @@ -68,7 +65,9 @@ namespace mergerfs { const char *fp = ((type == Category::Enum::create) ? "" : fusepath); + const bool needswritablefs = + (type == Category::Enum::create); - return _all(basepaths,fp,paths); + return _all(basepaths,fp,needswritablefs,paths); } } diff --git a/src/policy_eplfs.cpp b/src/policy_eplfs.cpp index 3fde1e65..2b7b8e8c 100644 --- a/src/policy_eplfs.cpp +++ b/src/policy_eplfs.cpp @@ -15,36 +15,33 @@ */ #include -#include -#include -#include #include #include #include +#include "fs.hpp" #include "fs_path.hpp" #include "policy.hpp" -#include "success_fail.hpp" +#include "statvfs_util.hpp" using std::string; using std::vector; using std::size_t; using mergerfs::Policy; using mergerfs::Category; -typedef struct statvfs statvfs_t; static void -_calc_lfs(const statvfs_t &fsstats, - const string *basepath, - const size_t minfreespace, - fsblkcnt_t &lfs, - const string *&lfsbasepath) +_calc_lfs(const struct statvfs &st, + const string *basepath, + const size_t minfreespace, + fsblkcnt_t &lfs, + const string *&lfsbasepath) { fsblkcnt_t spaceavail; - spaceavail = (fsstats.f_frsize * fsstats.f_bavail); + spaceavail = StatVFS::spaceavail(st); if((spaceavail > minfreespace) && (spaceavail < lfs)) { lfs = spaceavail; @@ -54,15 +51,14 @@ _calc_lfs(const statvfs_t &fsstats, static int -_eplfs(const Category::Enum::Type type, - const vector &basepaths, - const char *fusepath, - const size_t minfreespace, - vector &paths) +_eplfs(const vector &basepaths, + const char *fusepath, + const size_t minfreespace, + const bool needswritablefs, + vector &paths) { - int rv; string fullpath; - statvfs_t fsstats; + struct statvfs st; fsblkcnt_t eplfs; const string *eplfsbasepath; @@ -74,9 +70,10 @@ _eplfs(const Category::Enum::Type type, fs::path::make(basepath,fusepath,fullpath); - rv = ::statvfs(fullpath.c_str(),&fsstats); - if(STATVFS_SUCCEEDED(rv)) - _calc_lfs(fsstats,basepath,minfreespace,eplfs,eplfsbasepath); + if(!fs::available(fullpath,needswritablefs,st)) + continue; + + _calc_lfs(st,basepath,minfreespace,eplfs,eplfsbasepath); } if(eplfsbasepath == NULL) @@ -94,13 +91,15 @@ namespace mergerfs const vector &basepaths, const char *fusepath, const size_t minfreespace, - vector &paths) + vector &paths) { int rv; + const bool needswritablefs = + (type == Category::Enum::create); const size_t minfs = ((type == Category::Enum::create) ? minfreespace : 0); - rv = _eplfs(type,basepaths,fusepath,minfs,paths); + rv = _eplfs(basepaths,fusepath,minfs,needswritablefs,paths); if(POLICY_FAILED(rv)) rv = Policy::Func::lfs(type,basepaths,fusepath,minfreespace,paths); diff --git a/src/policy_epmfs.cpp b/src/policy_epmfs.cpp index 3d727fd0..f94d0296 100644 --- a/src/policy_epmfs.cpp +++ b/src/policy_epmfs.cpp @@ -15,36 +15,33 @@ */ #include -#include -#include -#include #include #include #include +#include "fs.hpp" #include "fs_path.hpp" #include "policy.hpp" -#include "success_fail.hpp" +#include "statvfs_util.hpp" using std::string; using std::vector; using std::size_t; using mergerfs::Policy; using mergerfs::Category; -typedef struct statvfs statvfs_t; static void -_calc_mfs(const statvfs_t &fsstats, - const string *basepath, - const size_t minfreespace, - fsblkcnt_t &mfs, - const string *&mfsbasepath) +_calc_mfs(const struct statvfs &st, + const string *basepath, + const size_t minfreespace, + fsblkcnt_t &mfs, + const string *&mfsbasepath) { fsblkcnt_t spaceavail; - spaceavail = (fsstats.f_frsize * fsstats.f_bavail); + spaceavail = StatVFS::spaceavail(st); if((spaceavail > minfreespace) && (spaceavail > mfs)) { mfs = spaceavail; @@ -57,11 +54,11 @@ int _epmfs(const vector &basepaths, const char *fusepath, const size_t minfreespace, + const bool needswritablefs, vector &paths) { - int rv; string fullpath; - statvfs_t fsstats; + struct statvfs st; fsblkcnt_t epmfs; const string *epmfsbasepath; @@ -73,9 +70,10 @@ _epmfs(const vector &basepaths, fs::path::make(basepath,fusepath,fullpath); - rv = ::statvfs(fullpath.c_str(),&fsstats); - if(STATVFS_SUCCEEDED(rv)) - _calc_mfs(fsstats,basepath,minfreespace,epmfs,epmfsbasepath); + if(!fs::available(fullpath,needswritablefs,st)) + continue; + + _calc_mfs(st,basepath,minfreespace,epmfs,epmfsbasepath); } if(epmfsbasepath == NULL) @@ -98,8 +96,10 @@ namespace mergerfs int rv; const size_t minfs = ((type == Category::Enum::create) ? minfreespace : 0); + const bool needswritablefs = + (type == Category::Enum::create); - rv = _epmfs(basepaths,fusepath,minfs,paths); + rv = _epmfs(basepaths,fusepath,minfs,needswritablefs,paths); if(POLICY_FAILED(rv)) rv = Policy::Func::mfs(type,basepaths,fusepath,minfreespace,paths); diff --git a/src/policy_ff.cpp b/src/policy_ff.cpp index eb75790d..14edb338 100644 --- a/src/policy_ff.cpp +++ b/src/policy_ff.cpp @@ -14,17 +14,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include #include +#include #include #include +#include "fs.hpp" #include "fs_path.hpp" #include "policy.hpp" -#include "success_fail.hpp" +#include "statvfs_util.hpp" using std::string; using std::vector; @@ -34,11 +33,12 @@ static int _ff(const vector &basepaths, const char *fusepath, + const bool needswritablefs, vector &paths) { - int rv; string fullpath; - struct stat st; + struct statvfs st; + const string *fallback = NULL; for(size_t i = 0, ei = basepaths.size(); i != ei; i++) { @@ -46,15 +46,27 @@ _ff(const vector &basepaths, fs::path::make(basepath,fusepath,fullpath); - rv = ::lstat(fullpath.c_str(),&st); - if(LSTAT_FAILED(rv)) + if(!fs::exists(fullpath,st)) continue; + if(needswritablefs && StatVFS::readonly(st)) + { + if(fallback == NULL) + fallback = basepath; + continue; + } + paths.push_back(basepath); return POLICY_SUCCESS; } + if(fallback != NULL) + { + paths.push_back(fallback); + return POLICY_SUCCESS; + } + return (errno=ENOENT,POLICY_FAIL); } @@ -69,7 +81,9 @@ namespace mergerfs { const char *fp = ((type == Category::Enum::create) ? "" : fusepath); + const bool needswritablefs = + (type == Category::Enum::create); - return _ff(basepaths,fp,paths); + return _ff(basepaths,fp,needswritablefs,paths); } } diff --git a/src/policy_ffwp.cpp b/src/policy_ffwp.cpp index f5ad7746..a07d2b6d 100644 --- a/src/policy_ffwp.cpp +++ b/src/policy_ffwp.cpp @@ -14,10 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include #include +#include #include #include diff --git a/src/policy_fwfs.cpp b/src/policy_fwfs.cpp index d41a4e56..3c52c305 100644 --- a/src/policy_fwfs.cpp +++ b/src/policy_fwfs.cpp @@ -14,15 +14,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include +#include #include #include +#include "fs.hpp" #include "fs_path.hpp" #include "policy.hpp" -#include "success_fail.hpp" +#include "statvfs_util.hpp" using std::string; using std::vector; @@ -35,11 +36,11 @@ int _fwfs(const vector &basepaths, const char *fusepath, const size_t minfreespace, + const bool needswritablefs, vector &paths) { - int rv; string fullpath; - struct statvfs fsstats; + struct statvfs st; for(size_t i = 0, size = basepaths.size(); i != size; i++) { @@ -47,19 +48,15 @@ _fwfs(const vector &basepaths, fs::path::make(basepath,fusepath,fullpath); - rv = ::statvfs(fullpath.c_str(),&fsstats); - if(STATVFS_SUCCEEDED(rv)) - { - fsblkcnt_t spaceavail; + if(!fs::available(fullpath,needswritablefs,st)) + continue; - spaceavail = (fsstats.f_frsize * fsstats.f_bavail); - if(spaceavail < minfreespace) - continue; + if(StatVFS::spaceavail(st) < minfreespace) + continue; - paths.push_back(basepath); + paths.push_back(basepath); - return POLICY_SUCCESS; - } + return POLICY_SUCCESS; } return (errno=ENOENT,POLICY_FAIL); @@ -77,8 +74,10 @@ namespace mergerfs int rv; const char *fp = ((type == Category::Enum::create) ? "" : fusepath); + const bool needswritablefs = + (type == Category::Enum::create); - rv = _fwfs(basepaths,fp,minfreespace,paths); + rv = _fwfs(basepaths,fp,minfreespace,needswritablefs,paths); if(POLICY_FAILED(rv)) rv = Policy::Func::mfs(type,basepaths,fusepath,minfreespace,paths); diff --git a/src/policy_lfs.cpp b/src/policy_lfs.cpp index 0e5f6c20..f7e5844d 100644 --- a/src/policy_lfs.cpp +++ b/src/policy_lfs.cpp @@ -14,18 +14,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include #include +#include #include #include +#include "fs.hpp" #include "fs_path.hpp" #include "policy.hpp" -#include "success_fail.hpp" +#include "statvfs_util.hpp" using std::string; using std::vector; @@ -35,7 +33,7 @@ using mergerfs::Category; static void -_calc_lfs(const struct statvfs &fsstats, +_calc_lfs(const struct statvfs &st, const string *basepath, const size_t minfreespace, fsblkcnt_t &lfs, @@ -43,7 +41,7 @@ _calc_lfs(const struct statvfs &fsstats, { fsblkcnt_t spaceavail; - spaceavail = (fsstats.f_frsize * fsstats.f_bavail); + spaceavail = StatVFS::spaceavail(st); if((spaceavail > minfreespace) && (spaceavail < lfs)) { lfs = spaceavail; @@ -56,11 +54,11 @@ int _lfs(const vector &basepaths, const char *fusepath, const size_t minfreespace, + const bool needswritablefs, vector &paths) { - int rv; string fullpath; - struct statvfs fsstats; + struct statvfs st; fsblkcnt_t lfs; const string *lfsbasepath; @@ -72,9 +70,10 @@ _lfs(const vector &basepaths, fs::path::make(basepath,fusepath,fullpath); - rv = ::statvfs(fullpath.c_str(),&fsstats); - if(STATVFS_SUCCEEDED(rv)) - _calc_lfs(fsstats,basepath,minfreespace,lfs,lfsbasepath); + if(!fs::available(fullpath,needswritablefs,st)) + continue; + + _calc_lfs(st,basepath,minfreespace,lfs,lfsbasepath); } if(lfsbasepath == NULL) @@ -97,8 +96,10 @@ namespace mergerfs int rv; const char *fp = ((type == Category::Enum::create) ? "" : fusepath); + const bool needswritablefs = + (type == Category::Enum::create); - rv = _lfs(basepaths,fp,minfreespace,paths); + rv = _lfs(basepaths,fp,minfreespace,needswritablefs,paths); if(POLICY_FAILED(rv)) rv = Policy::Func::mfs(type,basepaths,fusepath,minfreespace,paths); diff --git a/src/policy_mfs.cpp b/src/policy_mfs.cpp index 3773e412..c11a1f71 100644 --- a/src/policy_mfs.cpp +++ b/src/policy_mfs.cpp @@ -14,15 +14,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include +#include #include #include +#include "fs.hpp" #include "fs_path.hpp" #include "policy.hpp" -#include "success_fail.hpp" +#include "statvfs_util.hpp" using std::string; using std::vector; @@ -30,14 +31,14 @@ using std::size_t; static void -_calc_mfs(const struct statvfs &fsstats, +_calc_mfs(const struct statvfs &st, const string *basepath, fsblkcnt_t &mfs, const string *&mfsbasepath) { fsblkcnt_t spaceavail; - spaceavail = (fsstats.f_frsize * fsstats.f_bavail); + spaceavail = StatVFS::spaceavail(st); if(spaceavail > mfs) { mfs = spaceavail; @@ -49,11 +50,11 @@ static int _mfs(const vector &basepaths, const char *fusepath, + const bool needswritablefs, vector &paths) { - int rv; string fullpath; - struct statvfs fsstats; + struct statvfs st; fsblkcnt_t mfs; const string *mfsbasepath; @@ -65,9 +66,10 @@ _mfs(const vector &basepaths, fs::path::make(basepath,fusepath,fullpath); - rv = ::statvfs(fullpath.c_str(),&fsstats); - if(STATVFS_SUCCEEDED(rv)) - _calc_mfs(fsstats,basepath,mfs,mfsbasepath); + if(!fs::available(fullpath,needswritablefs,st)) + continue; + + _calc_mfs(st,basepath,mfs,mfsbasepath); } if(mfsbasepath == NULL) @@ -87,9 +89,16 @@ namespace mergerfs const size_t minfreespace, vector &paths) { + int rv; const char *fp = ((type == Category::Enum::create) ? "" : fusepath); + const bool needswritablefs = + (type == Category::Enum::create); + + rv = _mfs(basepaths,fp,needswritablefs,paths); + if(POLICY_FAILED(rv)) + rv = Policy::Func::ff(type,basepaths,fusepath,minfreespace,paths); - return _mfs(basepaths,fp,paths); + return rv; } } diff --git a/src/policy_newest.cpp b/src/policy_newest.cpp index 9cbf3692..c0e83cda 100644 --- a/src/policy_newest.cpp +++ b/src/policy_newest.cpp @@ -14,10 +14,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include #include +#include +#include #include #include @@ -26,6 +25,7 @@ #include "fs_path.hpp" #include "policy.hpp" #include "success_fail.hpp" +#include "statvfs_util.hpp" using std::string; using std::vector; @@ -35,12 +35,12 @@ static int _newest(const vector &basepaths, const char *fusepath, + const bool needswritablefs, vector &paths) { - int rv; - struct stat st; - string fullpath; time_t newest; + string fullpath; + struct stat st; const string *newestbasepath; newest = std::numeric_limits::min(); @@ -51,12 +51,15 @@ _newest(const vector &basepaths, fs::path::make(basepath,fusepath,fullpath); - rv = ::lstat(fullpath.c_str(),&st); - if(LSTAT_SUCCEEDED(rv) && (st.st_mtime >= newest)) - { - newest = st.st_mtime; - newestbasepath = basepath; - } + if(!fs::exists(fullpath,st)) + continue; + if(st.st_mtime < newest) + continue; + if(needswritablefs && !fs::exists_on_rw_fs(fullpath)) + continue; + + newest = st.st_mtime; + newestbasepath = basepath; } if(newestbasepath == NULL) @@ -76,6 +79,9 @@ namespace mergerfs const size_t minfreespace, vector &paths) { - return _newest(basepaths,fusepath,paths); + const bool needswritablefs = + (type == Category::Enum::create); + + return _newest(basepaths,fusepath,needswritablefs,paths); } } diff --git a/src/statvfs_util.hpp b/src/statvfs_util.hpp new file mode 100644 index 00000000..4cc6266d --- /dev/null +++ b/src/statvfs_util.hpp @@ -0,0 +1,55 @@ +/* + 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 "success_fail.hpp" + +namespace StatVFS +{ + static + inline + bool + readonly(const struct statvfs &st) + { + return (st.f_flag & ST_RDONLY); + } + + static + inline + bool + readonly(const std::string &path) + { + int rv; + struct statvfs st; + + rv = ::statvfs(path.c_str(),&st); + if(STATVFS_FAILED(rv)) + return false; + + return readonly(st); + } + + static + inline + fsblkcnt_t + spaceavail(const struct statvfs &st) + { + return (st.f_frsize * st.f_bavail); + } +}