From 0ca1705d3ad7d5ee8b3cf57cb6638c3e43d31a2d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 5 Apr 2020 12:00:07 +0100 Subject: Support a fallback value in EntCache Includes minor refactor for testing. --- netfs/lib/entCache.cpp | 139 +----------------------------------- netfs/lib/entCache.h | 8 +++ netfs/lib/entCache.impl.h | 167 ++++++++++++++++++++++++++++++++++++++++++++ netfs/unittests/Jamfile.jam | 10 +++ netfs/unittests/testLib.cpp | 100 ++++++++++++++++++++++++++ 5 files changed, 286 insertions(+), 138 deletions(-) create mode 100644 netfs/lib/entCache.impl.h create mode 100644 netfs/unittests/testLib.cpp diff --git a/netfs/lib/entCache.cpp b/netfs/lib/entCache.cpp index d62f4bf..a4436b9 100644 --- a/netfs/lib/entCache.cpp +++ b/netfs/lib/entCache.cpp @@ -1,147 +1,10 @@ -#include "entCache.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include "entCache.impl.h" static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); - -template -class EntCache::Ids : public boost::multi_index::multi_index_container, - boost::multi_index::indexed_by< - boost::multi_index::ordered_unique< - boost::multi_index::tag, BOOST_MULTI_INDEX_MEMBER(entry_t, const id_t, id), std::less<>>, - boost::multi_index::ordered_unique< - boost::multi_index::tag, BOOST_MULTI_INDEX_MEMBER(entry_t, const std::string, name), std::less<>> - > > { - }; - -template -EntCache::EntCache() : - idcache(std::make_unique()) -{ -} - -template -EntCache::~EntCache() = default; - -template -void -EntCache::getID(const EntCache::name_t & u, EntCache::id_t * target) const -{ - auto e = getEntry(u); - *target = e->id; -} - -template -void -EntCache::getName(const EntCache::id_t & u, EntCache::name_t * target) const -{ - auto e = getEntry(u); - *target = e->name; -} - -template -template -typename EntCache::entry_ptr -EntCache::getEntry(const key_t & key) const -{ - if (fillTime + 60 > time(nullptr)) { - if (auto ent = getEntryNoFill(key)) { - return ent; - } - } - fillCache(); - if (auto ent = getEntryNoFill(key)) { - return ent; - } - throw NetFS::SystemError(EPERM); -} - -template -template -typename EntCache::entry_ptr -EntCache::getEntryNoFill(const key_t & key) const -{ - SharedLock(lock); - auto & collection = idcache->template get(); - auto i = collection.find(key); - if (i != collection.end()) { - return *i; - } - return nullptr; -} - -User::User(uid_t u, std::string n, gid_t g) : - id(u), - name(std::move(n)), - group(g) -{ -} - -const int BUFLEN = 8196; - -template<> -void -EntCache::fillCache() const -{ - Lock(lock); - setpwent(); - idcache->clear(); - std::array buf {}; - struct passwd pwbuf {}, * pwp; - while (getpwent_r(&pwbuf, buf.data(), buf.size(), &pwp) == 0) { - idcache->insert(std::make_shared(pwp->pw_uid, pwp->pw_name, pwp->pw_gid)); - } - endpwent(); - time(&fillTime); -} - -Group::Group(gid_t g, std::string n) : - id(g), - name(std::move(n)) -{ -} - -template<> -void -EntCache::fillCache() const -{ - Lock(lock); - setgrent(); - std::array buf {}; - idcache->clear(); - struct group grpbuf {}, * grp; - EntCache instance; - while (getgrent_r(&grpbuf, buf.data(), buf.size(), &grp) == 0) { - auto g = std::make_shared(grp->gr_gid, grp->gr_name); - for (auto member = grp->gr_mem; *member; member++) { - try { - g->members.insert(instance.getEntry((const name_t &)*member)->id); - } - catch (const NetFS::SystemError &) { - } - } - idcache->insert(std::move(g)); - } - endgrent(); - time(&fillTime); -} - -bool -Group::hasMember(uid_t u) const -{ - return (members.find(u) != members.end()); -} - template class EntCache; template class EntCache; diff --git a/netfs/lib/entCache.h b/netfs/lib/entCache.h index 9facdb3..e6a77dc 100644 --- a/netfs/lib/entCache.h +++ b/netfs/lib/entCache.h @@ -5,6 +5,7 @@ #include #include #include +#include class User { public: @@ -29,8 +30,11 @@ template class EntCache : public EntryResolver { public: EntCache(); + EntCache(entry_t fb); ~EntCache() override; + SPECIAL_MEMBERS_DEFAULT_MOVE_NO_COPY(EntCache); + using id_t = decltype(entry_t::id); using name_t = decltype(entry_t::name); using entry_ptr = std::shared_ptr; @@ -40,6 +44,9 @@ class EntCache : public EntryResolver entry_ptr getEntry(const key_t &) const; + void setFallback(entry_t fb); + void clearFallback(); + protected: void fillCache() const; template @@ -49,6 +56,7 @@ class EntCache : public EntryResolver idcache; mutable std::shared_mutex lock; mutable time_t fillTime {0}; + entry_ptr fallback; }; using UserEntCache = EntCache; diff --git a/netfs/lib/entCache.impl.h b/netfs/lib/entCache.impl.h new file mode 100644 index 0000000..e83fb49 --- /dev/null +++ b/netfs/lib/entCache.impl.h @@ -0,0 +1,167 @@ +#include "entCache.h" +#ifndef ENTCACHE_IMPL_H +#define ENTCACHE_IMPL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +template +class EntCache::Ids : public boost::multi_index::multi_index_container, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::tag, BOOST_MULTI_INDEX_MEMBER(entry_t, const id_t, id), std::less<>>, + boost::multi_index::ordered_unique< + boost::multi_index::tag, BOOST_MULTI_INDEX_MEMBER(entry_t, const std::string, name), std::less<>> + > > { + }; + +template +EntCache::EntCache() : + idcache(std::make_unique()) +{ +} + +template +EntCache::EntCache(entry_t fb) : + idcache(std::make_unique()) +{ + setFallback(std::move(fb)); +} + +template +EntCache::~EntCache() = default; + +template +void +EntCache::getID(const EntCache::name_t & u, EntCache::id_t * target) const +{ + auto e = getEntry(u); + *target = e->id; +} + +template +void +EntCache::getName(const EntCache::id_t & u, EntCache::name_t * target) const +{ + auto e = getEntry(u); + *target = e->name; +} + +template +template +typename EntCache::entry_ptr +EntCache::getEntry(const key_t & key) const +{ + if (fillTime + 60 > time(nullptr)) { + if (auto ent = getEntryNoFill(key)) { + return ent; + } + } + fillCache(); + if (auto ent = getEntryNoFill(key)) { + return ent; + } + if (fallback) { + return fallback; + } + throw NetFS::SystemError(EPERM); +} + +template +template +typename EntCache::entry_ptr +EntCache::getEntryNoFill(const key_t & key) const +{ + SharedLock(lock); + auto & collection = idcache->template get(); + auto i = collection.find(key); + if (i != collection.end()) { + return *i; + } + return nullptr; +} + +template +void +EntCache::setFallback(entry_t fb) +{ + fallback = std::make_shared(std::move(fb)); +} + +template +void +EntCache::clearFallback() +{ + fallback.reset(); +} + +User::User(uid_t u, std::string n, gid_t g) : + id(u), + name(std::move(n)), + group(g) +{ +} + +const int BUFLEN = 8196; + +template<> +void +EntCache::fillCache() const +{ + Lock(lock); + setpwent(); + idcache->clear(); + std::array buf {}; + struct passwd pwbuf {}, * pwp; + while (getpwent_r(&pwbuf, buf.data(), buf.size(), &pwp) == 0) { + idcache->insert(std::make_shared(pwp->pw_uid, pwp->pw_name, pwp->pw_gid)); + } + endpwent(); + time(&fillTime); +} + +Group::Group(gid_t g, std::string n) : + id(g), + name(std::move(n)) +{ +} + +template<> +void +EntCache::fillCache() const +{ + Lock(lock); + setgrent(); + std::array buf {}; + idcache->clear(); + struct group grpbuf {}, * grp; + EntCache instance; + while (getgrent_r(&grpbuf, buf.data(), buf.size(), &grp) == 0) { + auto g = std::make_shared(grp->gr_gid, grp->gr_name); + for (auto member = grp->gr_mem; *member; member++) { + try { + g->members.insert(instance.getEntry((const name_t &)*member)->id); + } + catch (const NetFS::SystemError &) { + } + } + idcache->insert(std::move(g)); + } + endgrent(); + time(&fillTime); +} + +bool +Group::hasMember(uid_t u) const +{ + return (members.find(u) != members.end()); +} + +#endif + diff --git a/netfs/unittests/Jamfile.jam b/netfs/unittests/Jamfile.jam index a6021f9..f1f3dba 100644 --- a/netfs/unittests/Jamfile.jam +++ b/netfs/unittests/Jamfile.jam @@ -64,6 +64,16 @@ run testEdgeCases.cpp testMocks : testEdgeCases ; +run testLib.cpp + : -- : + defaultDaemon.xml + defaultFuse.xml + : + BOOST_TEST_DYN_LINK + boost_utf + testMocks + ; + # explicit testFuse ; run testFuse.cpp : -- : diff --git a/netfs/unittests/testLib.cpp b/netfs/unittests/testLib.cpp new file mode 100644 index 0000000..91ca683 --- /dev/null +++ b/netfs/unittests/testLib.cpp @@ -0,0 +1,100 @@ +#define BOOST_TEST_MODULE TestNetFSLib +#include +#include +#include +#include + +struct TestEntry { + TestEntry(int i, std::string n) : + id(i), + name(std::move(n)) + { + } + + int id; + std::string name; +}; +using TestEntCache = EntCache; + +template<> +void +EntCache::fillCache() const +{ + Lock(lock); + idcache->insert(std::make_shared(1, "user1")); + idcache->insert(std::make_shared(2, "user2")); + idcache->insert(std::make_shared(3, "user3")); +} + +const auto GoodIds = boost::unit_test::data::make({ 1, 2, 3 }); +const auto BadIds = boost::unit_test::data::make({ 0, -1, 10 }); +const auto GoodNames = boost::unit_test::data::make({ "user1", "user2", "user3" }); +const auto BadNames = boost::unit_test::data::make({ "", "bad", "user4" }); + +BOOST_FIXTURE_TEST_SUITE(tec, TestEntCache); + +BOOST_DATA_TEST_CASE(notfoundid, BadIds, id) +{ + std::string outname; + BOOST_CHECK_THROW(getName(id, &outname), NetFS::SystemError); +} + +BOOST_DATA_TEST_CASE(notfoundname, BadNames, name) +{ + int outid; + BOOST_CHECK_THROW(getID(name, &outid), NetFS::SystemError); +} + +BOOST_DATA_TEST_CASE(foundid, GoodNames ^ GoodIds, name, id) +{ + std::string outname; + BOOST_REQUIRE_NO_THROW(getName(id, &outname)); + BOOST_CHECK_EQUAL(name, outname); +} + +BOOST_DATA_TEST_CASE(foundname, GoodNames ^ GoodIds, name, id) +{ + int outid; + BOOST_REQUIRE_NO_THROW(getID(name, &outid)); + BOOST_CHECK_EQUAL(id, outid); +} + +BOOST_AUTO_TEST_SUITE_END(); + +class TestEntCacheWithFallback : public TestEntCache { + public: + TestEntCacheWithFallback() : TestEntCache({ 4, "fallback" }) { } +}; + +BOOST_FIXTURE_TEST_SUITE(tecfb, TestEntCacheWithFallback); + +BOOST_DATA_TEST_CASE(notfoundid, BadIds, id) +{ + std::string outname; + BOOST_REQUIRE_NO_THROW(getName(id, &outname)); + BOOST_CHECK_EQUAL(outname, fallback->name); +} + +BOOST_DATA_TEST_CASE(notfoundname, BadNames, name) +{ + int outid; + BOOST_REQUIRE_NO_THROW(getID(name, &outid)); + BOOST_CHECK_EQUAL(outid, fallback->id); +} + +BOOST_DATA_TEST_CASE(foundid, GoodNames ^ GoodIds, name, id) +{ + std::string outname; + BOOST_REQUIRE_NO_THROW(getName(id, &outname)); + BOOST_CHECK_EQUAL(name, outname); +} + +BOOST_DATA_TEST_CASE(foundname, GoodNames ^ GoodIds, name, id) +{ + int outid; + BOOST_REQUIRE_NO_THROW(getID(name, &outid)); + BOOST_CHECK_EQUAL(id, outid); +} + +BOOST_AUTO_TEST_SUITE_END(); + -- cgit v1.2.3