summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2020-04-05 12:00:07 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2020-04-05 12:00:07 +0100
commit0ca1705d3ad7d5ee8b3cf57cb6638c3e43d31a2d (patch)
treee71438af9b89c97f57273c9ca42293f0a69e3985
parentFix templateness of EntryResolver (diff)
downloadnetfs-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.cpp139
-rw-r--r--netfs/lib/entCache.h8
-rw-r--r--netfs/lib/entCache.impl.h167
-rw-r--r--netfs/unittests/Jamfile.jam10
-rw-r--r--netfs/unittests/testLib.cpp100
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();
+