diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2020-04-05 12:00:07 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2020-04-05 12:00:07 +0100 |
commit | 0ca1705d3ad7d5ee8b3cf57cb6638c3e43d31a2d (patch) | |
tree | e71438af9b89c97f57273c9ca42293f0a69e3985 | |
parent | Fix templateness of EntryResolver (diff) | |
download | netfs-0ca1705d3ad7d5ee8b3cf57cb6638c3e43d31a2d.tar.bz2 netfs-0ca1705d3ad7d5ee8b3cf57cb6638c3e43d31a2d.tar.xz netfs-0ca1705d3ad7d5ee8b3cf57cb6638c3e43d31a2d.zip |
Support a fallback value in EntCache
Includes minor refactor for testing.
-rw-r--r-- | netfs/lib/entCache.cpp | 139 | ||||
-rw-r--r-- | netfs/lib/entCache.h | 8 | ||||
-rw-r--r-- | netfs/lib/entCache.impl.h | 167 | ||||
-rw-r--r-- | netfs/unittests/Jamfile.jam | 10 | ||||
-rw-r--r-- | netfs/unittests/testLib.cpp | 100 |
5 files changed, 286 insertions, 138 deletions
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 <exceptions.h> -#include <lockHelpers.h> -#include <pwd.h> -#include <grp.h> -#include <type_traits> -#include <boost/multi_index_container.hpp> -#include <boost/multi_index/member.hpp> -#include <boost/multi_index/ordered_index.hpp> +#include "entCache.impl.h" static_assert(std::is_nothrow_move_constructible_v<User>); static_assert(std::is_nothrow_move_assignable_v<User>); static_assert(std::is_nothrow_move_constructible_v<Group>); static_assert(std::is_nothrow_move_assignable_v<Group>); - -template<class entry_t> -class EntCache<entry_t>::Ids : public boost::multi_index::multi_index_container<std::shared_ptr<entry_t>, - boost::multi_index::indexed_by< - boost::multi_index::ordered_unique< - boost::multi_index::tag<id_t>, BOOST_MULTI_INDEX_MEMBER(entry_t, const id_t, id), std::less<>>, - boost::multi_index::ordered_unique< - boost::multi_index::tag<std::string>, BOOST_MULTI_INDEX_MEMBER(entry_t, const std::string, name), std::less<>> - > > { - }; - -template<class entry_t> -EntCache<entry_t>::EntCache() : - idcache(std::make_unique<Ids>()) -{ -} - -template<class entry_t> -EntCache<entry_t>::~EntCache() = default; - -template<class entry_t> -void -EntCache<entry_t>::getID(const EntCache<entry_t>::name_t & u, EntCache<entry_t>::id_t * target) const -{ - auto e = getEntry(u); - *target = e->id; -} - -template<class entry_t> -void -EntCache<entry_t>::getName(const EntCache<entry_t>::id_t & u, EntCache<entry_t>::name_t * target) const -{ - auto e = getEntry(u); - *target = e->name; -} - -template<class entry_t> -template<class key_t> -typename EntCache<entry_t>::entry_ptr -EntCache<entry_t>::getEntry(const key_t & key) const -{ - if (fillTime + 60 > time(nullptr)) { - if (auto ent = getEntryNoFill<key_t>(key)) { - return ent; - } - } - fillCache(); - if (auto ent = getEntryNoFill<key_t>(key)) { - return ent; - } - throw NetFS::SystemError(EPERM); -} - -template<class entry_t> -template<class key_t> -typename EntCache<entry_t>::entry_ptr -EntCache<entry_t>::getEntryNoFill(const key_t & key) const -{ - SharedLock(lock); - auto & collection = idcache->template get<key_t>(); - 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<User>::fillCache() const -{ - Lock(lock); - setpwent(); - idcache->clear(); - std::array<char, BUFLEN> buf {}; - struct passwd pwbuf {}, * pwp; - while (getpwent_r(&pwbuf, buf.data(), buf.size(), &pwp) == 0) { - idcache->insert(std::make_shared<User>(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<Group>::fillCache() const -{ - Lock(lock); - setgrent(); - std::array<char, BUFLEN> buf {}; - idcache->clear(); - struct group grpbuf {}, * grp; - EntCache<User> instance; - while (getgrent_r(&grpbuf, buf.data(), buf.size(), &grp) == 0) { - auto g = std::make_shared<Group>(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<User>; template class EntCache<Group>; 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 <set> #include <shared_mutex> #include <entryResolver.h> +#include <c++11Helpers.h> class User { public: @@ -29,8 +30,11 @@ template<class entry_t> class EntCache : public EntryResolver<decltype(entry_t::id), decltype(entry_t::name)> { 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<entry_t>; @@ -40,6 +44,9 @@ class EntCache : public EntryResolver<decltype(entry_t::id), decltype(entry_t::n template<class key_t> entry_ptr getEntry(const key_t &) const; + void setFallback(entry_t fb); + void clearFallback(); + protected: void fillCache() const; template<class key_t> @@ -49,6 +56,7 @@ class EntCache : public EntryResolver<decltype(entry_t::id), decltype(entry_t::n std::unique_ptr<Ids> idcache; mutable std::shared_mutex lock; mutable time_t fillTime {0}; + entry_ptr fallback; }; using UserEntCache = EntCache<User>; 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 <exceptions.h> +#include <lockHelpers.h> +#include <pwd.h> +#include <grp.h> +#include <type_traits> +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/member.hpp> +#include <boost/multi_index/ordered_index.hpp> + +template<class entry_t> +class EntCache<entry_t>::Ids : public boost::multi_index::multi_index_container<std::shared_ptr<entry_t>, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::tag<id_t>, BOOST_MULTI_INDEX_MEMBER(entry_t, const id_t, id), std::less<>>, + boost::multi_index::ordered_unique< + boost::multi_index::tag<std::string>, BOOST_MULTI_INDEX_MEMBER(entry_t, const std::string, name), std::less<>> + > > { + }; + +template<class entry_t> +EntCache<entry_t>::EntCache() : + idcache(std::make_unique<Ids>()) +{ +} + +template<class entry_t> +EntCache<entry_t>::EntCache(entry_t fb) : + idcache(std::make_unique<Ids>()) +{ + setFallback(std::move(fb)); +} + +template<class entry_t> +EntCache<entry_t>::~EntCache() = default; + +template<class entry_t> +void +EntCache<entry_t>::getID(const EntCache<entry_t>::name_t & u, EntCache<entry_t>::id_t * target) const +{ + auto e = getEntry(u); + *target = e->id; +} + +template<class entry_t> +void +EntCache<entry_t>::getName(const EntCache<entry_t>::id_t & u, EntCache<entry_t>::name_t * target) const +{ + auto e = getEntry(u); + *target = e->name; +} + +template<class entry_t> +template<class key_t> +typename EntCache<entry_t>::entry_ptr +EntCache<entry_t>::getEntry(const key_t & key) const +{ + if (fillTime + 60 > time(nullptr)) { + if (auto ent = getEntryNoFill<key_t>(key)) { + return ent; + } + } + fillCache(); + if (auto ent = getEntryNoFill<key_t>(key)) { + return ent; + } + if (fallback) { + return fallback; + } + throw NetFS::SystemError(EPERM); +} + +template<class entry_t> +template<class key_t> +typename EntCache<entry_t>::entry_ptr +EntCache<entry_t>::getEntryNoFill(const key_t & key) const +{ + SharedLock(lock); + auto & collection = idcache->template get<key_t>(); + auto i = collection.find(key); + if (i != collection.end()) { + return *i; + } + return nullptr; +} + +template<typename entry_t> +void +EntCache<entry_t>::setFallback(entry_t fb) +{ + fallback = std::make_shared<entry_t>(std::move(fb)); +} + +template<typename entry_t> +void +EntCache<entry_t>::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<User>::fillCache() const +{ + Lock(lock); + setpwent(); + idcache->clear(); + std::array<char, BUFLEN> buf {}; + struct passwd pwbuf {}, * pwp; + while (getpwent_r(&pwbuf, buf.data(), buf.size(), &pwp) == 0) { + idcache->insert(std::make_shared<User>(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<Group>::fillCache() const +{ + Lock(lock); + setgrent(); + std::array<char, BUFLEN> buf {}; + idcache->clear(); + struct group grpbuf {}, * grp; + EntCache<User> instance; + while (getgrent_r(&grpbuf, buf.data(), buf.size(), &grp) == 0) { + auto g = std::make_shared<Group>(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 <library>testMocks : testEdgeCases ; +run testLib.cpp + : -- : + defaultDaemon.xml + defaultFuse.xml + : + <define>BOOST_TEST_DYN_LINK + <library>boost_utf + <library>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 <boost/test/unit_test.hpp> +#include <boost/test/data/test_case.hpp> +#include <entCache.impl.h> +#include <lockHelpers.h> + +struct TestEntry { + TestEntry(int i, std::string n) : + id(i), + name(std::move(n)) + { + } + + int id; + std::string name; +}; +using TestEntCache = EntCache<TestEntry>; + +template<> +void +EntCache<TestEntry>::fillCache() const +{ + Lock(lock); + idcache->insert(std::make_shared<TestEntry>(1, "user1")); + idcache->insert(std::make_shared<TestEntry>(2, "user2")); + idcache->insert(std::make_shared<TestEntry>(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(); + |