2 * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #include "include/fb_generated.h"
17 #include "print_content.hpp"
18 #include "serialized_convert.hpp"
19 #include "serializer.hpp"
20 #include "storage_backend_serialized.hpp"
21 #include "transaction_guard.hpp"
23 #include <boost/tokenizer.hpp>
29 #include <sys/types.h>
33 using ldp_xml_parser::MatchItemSend;
35 namespace ldp_serialized {
37 // Set max size of serialized file to prevent mmap with unexpected memory size.
39 #define MAX_SFILE_SIZE (1024 * 1024)
41 typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
46 class StorageBackendSerialized::StorageBackendSerializedImpl {
48 uint8_t *mem{static_cast<decltype(mem)>(MAP_FAILED)};
50 const FB::File *file{nullptr};
52 std::unique_ptr<ldp_xml_parser::Serializer> serializer;
57 typedef std::pair<const ItemSend *, unsigned> ItemWithScore; // a pair: Item, index of Item
58 typedef std::vector<ItemWithScore> ItemsWithScore; // items
59 typedef std::pair<unsigned, ItemsWithScore> HighestScoreWithItems; // value for index map: highest index for items, items
60 typedef std::map<boost::string_ref, HighestScoreWithItems> MapIndex;
61 typedef std::vector<ItemWithScore> SendPrefixIndex;
63 MapIndex sendIndex; // context default
64 SendPrefixIndex sendPrefixIndex; // context default prefix rules
65 std::map<uid_t, MapIndex> userSendIndex;
66 std::map<uid_t, SendPrefixIndex> userSendPrefixIndex;
69 bool init(const char *filename, bool verify);
70 bool init(const FB::File *f);
71 bool initFromXML(const char *config_name);
74 void createSendIndex();
75 ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const MapIndex &index, const SendPrefixIndex &prefixIndex);
76 ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item);
77 ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid);
79 void printContent(const bool xml_format = false) const;
81 template <typename T, typename M = typename type_helper<T>::policy_set_type>
82 const M *getPolicySet();
85 inline boost::string_ref s(const flatbuffers::String *str) {
86 return boost::string_ref(str->c_str(), str->size());
89 template <typename T, typename I>
90 bool match(const T &match, const I *i) {
91 return match.match(makeMessageType(i->type()),
97 makeDecision(i->decision()->decision()));
100 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item,
101 const MapIndex &index, const SendPrefixIndex &prefixIndex) {
102 ldp_xml_parser::DecisionItem decision(ldp_xml_parser::Decision::ANY);
104 ItemWithScore currentBest(nullptr, 0);
106 auto updateCurrentBest = [¤tBest, &item, &index](boost::string_ref name) {
107 // check if there are any rules for the name
108 auto fit = index.find(name);
109 if (fit == index.end())
112 // check if there's any chance to get better score
113 // fit->second.first is the highest score from vector fit->second.second
114 if (fit->second.first <= currentBest.second)
117 // look for better score
118 for (const auto &it: fit->second.second) {
119 if (it.second > currentBest.second && match(item, it.first))
124 // iterate over names
125 for (const auto &name: item.getNames()) {
126 // find and check the no-prefix rules
127 updateCurrentBest(name);
129 // check the prefix rules
130 for (const auto &prefixItem: prefixIndex) {
131 if (prefixItem.second > currentBest.second && match(item, prefixItem.first))
132 currentBest = prefixItem;
136 // check no-name rules
137 updateCurrentBest("");
139 if (currentBest.first)
140 decision = makeDecisionItem(currentBest.first->decision());
145 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item) {
146 return getDecisionFromSendIndex(item, sendIndex, sendPrefixIndex);
149 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item, uid_t uid) {
150 return getDecisionFromSendIndex(item, userSendIndex[uid], userSendPrefixIndex[uid]);
153 void StorageBackendSerialized::StorageBackendSerializedImpl::createSendIndex() {
154 // helper for adding items to indexes
155 auto add = [](MapIndex &index, SendPrefixIndex &prefixIndex, boost::string_ref key, ItemWithScore value) {
156 if (value.first->is_name_prefix()) {
157 prefixIndex.push_back(value);
159 index[key].second.push_back(value);
160 // we insert items in increasing score order, so we just update the highest score on each add
161 index[key].first = value.second;
165 // context default index
166 const auto *policy = file->m_send_set()->context_default();
169 const auto *v = policy->items();
170 for (auto it = v->begin(); it != v->end(); ++it)
171 add(sendIndex, sendPrefixIndex,
172 boost::string_ref(it->name()->c_str(), it->name()->size()),
173 ItemWithScore(*it, cnt++));
176 for (const auto &uidPolicy: *file->m_send_set()->user()) {
178 for (const auto it: *uidPolicy->policy()->items()) {
179 add(userSendIndex[uidPolicy->id()],
180 userSendPrefixIndex[uidPolicy->id()],
181 boost::string_ref(it->name()->c_str(), it->name()->size()),
182 ItemWithScore(it, cnt++));
187 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseMMap() {
188 assert(MAP_FAILED != mem);
191 if (munmap(mem, length) != 0)
192 tslog::log_error("munmap(): ", tslog::print_errno(errno), "\n");
194 mem = static_cast<decltype(mem)>(MAP_FAILED);
198 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseFD() {
202 tslog::log_error("close(): ", tslog::print_errno(errno), "\n");
207 void StorageBackendSerialized::StorageBackendSerializedImpl::release() {
208 if (-1 != fd) { // we need to check it, because we may have initialized the storage directly from File *
213 if (nullptr != serializer.get()) {
214 serializer.reset(nullptr);
220 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const char *filename, bool verify) {
221 assert(nullptr == file);
223 auto err = [filename] (const char *what) {
224 tslog::log_error("Can't ", what, " ", filename, ": ", tslog::print_errno(errno), "\n");
228 fd = open(filename, O_RDONLY);
232 auto openGuard = transaction_guard::makeGuard([&] () { releaseFD(); });
236 if (fstat(fd, &buf) == -1)
239 length = buf.st_size;
240 if (length > MAX_SFILE_SIZE) {
241 tslog::log_error("Serialized file size(", length, ") is too large. (>", MAX_SFILE_SIZE, ") bytes.\n");
245 mem = reinterpret_cast<uint8_t*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0));
246 if (MAP_FAILED == mem)
249 auto mmapGuard = transaction_guard::makeGuard([&] () { releaseMMap(); });
252 auto verifier = flatbuffers::Verifier(mem, length);
253 if (!FB::VerifyFileBuffer(verifier) || !FB::FileBufferHasIdentifier(mem)) {
254 tslog::log_error("verification of serialized data: failed\n");
263 bool res = file != nullptr;
269 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const FB::File *f) {
270 assert(nullptr == file);
277 bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromXML(const char *config_name) {
278 assert(nullptr == file);
279 assert(nullptr == serializer.get());
281 serializer.reset(new ldp_xml_parser::Serializer());
283 size_t serialized_size;
284 uint8_t *data = serializer->serialize(config_name, serialized_size);
288 return init(FB::GetFile(data));
291 void StorageBackendSerialized::StorageBackendSerializedImpl::printContent(const bool xml_format) const {
292 print_content::use_xml_format(xml_format);
296 /*************************************************/
298 #define TYPE_HELPER(T, t) \
300 struct type_helper<ldp_xml_parser::MatchItem##T> { \
301 typedef FB::T##Set policy_set_type; \
302 typedef FB::Policy##T policy_type; \
305 auto StorageBackendSerialized::StorageBackendSerializedImpl::getPolicySet<ldp_xml_parser::MatchItem##T>() \
306 -> const typename type_helper<ldp_xml_parser::MatchItem##T>::policy_set_type * { \
308 return file->m_##t##_set(); \
311 TYPE_HELPER(Own, own)
312 TYPE_HELPER(Send, send)
313 TYPE_HELPER(Receive, receive)
314 TYPE_HELPER(Access, access)
316 bool StorageBackendSerialized::init(const char *filename, bool verify) {
317 return pimpl->init(filename, verify);
320 bool StorageBackendSerialized::init(const FB::File *f) {
321 return pimpl->init(f);
324 bool StorageBackendSerialized::initFromXML(const char *config_name) {
325 return pimpl->initFromXML(config_name);
328 void StorageBackendSerialized::release() {
332 void StorageBackendSerialized::printContent(const bool xml_format) const {
333 pimpl->printContent(xml_format);
336 template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
337 return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
340 template <typename T, typename P = typename type_helper<T>::policy_type>
341 ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
342 const auto *v = policy->items();
343 for (auto it = v->rbegin(); it != v->rend(); ++it) {
344 if (match(item, *it))
345 return makeDecisionItem((*it)->decision());
347 return ldp_xml_parser::Decision::ANY;
350 const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
352 tokenizer::iterator &iterator) {
353 if (iterator == tokens.end()) {
354 if (node->decision_item()->decision() != Decision_ANY)
355 return node->decision_item();
357 return node->prefix_decision_item();
360 auto child = node->children()->LookupByKey(iterator->c_str());
361 if (nullptr == child)
362 return node->prefix_decision_item();
365 const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
366 if (child_decision->decision() == Decision_ANY)
367 return node->prefix_decision_item();
369 return child_decision;
372 template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
373 const PolicyOwn *policy) {
374 if (item.getName().length() == 0)
375 return ldp_xml_parser::Decision::DENY;
377 boost::char_separator<char> separator(".");
378 tokenizer tokens(item.getName(), separator);
380 auto node = policy->tree();
382 return ldp_xml_parser::Decision::ANY;
384 auto iterator = tokens.begin();
386 return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
389 template <typename T, typename P = typename type_helper<T>::policy_type>
390 ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
391 if (nullptr == policyPair)
392 return ldp_xml_parser::Decision::ANY;
393 return getDecisionItem(item, policyPair->policy());
396 template <typename T>
397 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const T &item) const {
398 return getDecisionItem(item, pimpl->getPolicySet<T>()->context_mandatory());
402 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
403 return pimpl->getDecisionFromSendIndex(item);
407 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const MatchItemSend &item) const {
408 auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
409 if (nullptr == policyPair)
410 return ldp_xml_parser::Decision::ANY;
411 return pimpl->getDecisionFromSendIndex(item, uid);
414 template <typename T>
415 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const T &item) const {
416 return getDecisionItem(item, pimpl->getPolicySet<T>()->context_default());
419 template <typename T>
420 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const T &item) const {
421 return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->user()->LookupByKey(uid));
424 template <typename T>
425 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const T &item) const {
426 return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->group()->LookupByKey(gid));
429 template <typename T>
430 bool StorageBackendSerialized::existsPolicyForGroup(gid_t gid) const {
431 return pimpl->getPolicySet<T>()->group()->LookupByKey(gid) != nullptr;
434 #define T_INSTANTIATION(T) \
435 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const ldp_xml_parser::MatchItem##T &item) const; \
436 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const ldp_xml_parser::MatchItem##T &item) const;
439 T_INSTANTIATION(Send)
440 T_INSTANTIATION(Receive)
441 T_INSTANTIATION(Access)
443 #undef T_INSTANTIATION
445 #define T_INSTANTIATION2(T) \
446 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const ldp_xml_parser::MatchItem##T &item) const; \
447 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const ldp_xml_parser::MatchItem##T &item) const; \
448 template bool StorageBackendSerialized::existsPolicyForGroup<ldp_xml_parser::MatchItem##T>(gid_t) const;
450 T_INSTANTIATION2(Own)
451 T_INSTANTIATION2(Send)
452 T_INSTANTIATION2(Receive)
454 #undef T_INSTANTIATION2
456 StorageBackendSerialized::StorageBackendSerialized()
457 : pimpl{new StorageBackendSerializedImpl} {
460 StorageBackendSerialized::~StorageBackendSerialized() {