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>
30 #include <sys/types.h>
34 using ldp_xml_parser::MatchItemSend;
36 namespace ldp_serialized {
38 // Set max size of serialized file to prevent mmap with unexpected memory size.
40 #define MAX_SFILE_SIZE (1024 * 1024)
42 typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
49 inline boost::string_ref s(const flatbuffers::String *str) {
50 return boost::string_ref(str->c_str(), str->size());
53 template <typename T, typename I>
54 bool match(const T &match, const I *i) {
55 return match.match(makeMessageType(i->type()),
61 makeDecision(i->decision()->decision()));
64 template <> bool match(const ldp_xml_parser::MatchItemAccess &match, const FB::ItemAccess *item) {
65 return match.match(makeBusAccessType(item->type()), item->uid(), item->gid());
68 template <typename T, typename P = typename type_helper<T>::policy_type>
69 ldp_xml_parser::DecisionItem getDecisionItem(const T &item, const P *policy) {
70 const auto *v = policy->items();
71 for (auto it = v->rbegin(); it != v->rend(); ++it) {
73 return makeDecisionItem((*it)->decision());
75 return ldp_xml_parser::Decision::ANY;
78 const DecisionItem *getDecisionItemFromTree(const FB::PolicyOwnNode *node,
80 tokenizer::iterator &iterator) {
81 if (iterator == tokens.end()) {
82 if (node->decision_item()->decision() != Decision_ANY)
83 return node->decision_item();
85 return node->prefix_decision_item();
88 auto child = node->children()->LookupByKey(iterator->c_str());
90 return node->prefix_decision_item();
93 const DecisionItem *child_decision = getDecisionItemFromTree(child, tokens, iterator);
94 if (child_decision->decision() == Decision_ANY)
95 return node->prefix_decision_item();
97 return child_decision;
100 template <> ldp_xml_parser::DecisionItem getDecisionItem(const ldp_xml_parser::MatchItemOwn &item,
101 const PolicyOwn *policy) {
102 if (item.getName().length() == 0)
103 return ldp_xml_parser::Decision::DENY;
105 boost::char_separator<char> separator(".");
106 tokenizer tokens(item.getName(), separator);
108 auto node = policy->tree();
110 return ldp_xml_parser::Decision::ANY;
112 auto iterator = tokens.begin();
114 return makeDecisionItem(getDecisionItemFromTree(node, tokens, iterator));
117 template <typename T, typename P = typename type_helper<T>::policy_type>
118 ldp_xml_parser::DecisionItem getDecisionItemMaybeNull(const T &item, const P *policyPair) {
119 if (nullptr == policyPair)
120 return ldp_xml_parser::Decision::ANY;
121 return getDecisionItem(item, policyPair->policy());
124 } // anonymous namespace
126 class StorageBackendSerialized::StorageBackendSerializedImpl {
128 uint8_t *mem{static_cast<decltype(mem)>(MAP_FAILED)};
130 const FB::File *file{nullptr};
132 std::unique_ptr<ldp_serializer::Serializer> serializer;
138 bool init(const char *filename, bool verify);
139 bool initFromData(const uint8_t *mem, bool verify);
140 bool initFromXML(const char *config_name);
143 ldp_xml_parser::DecisionItem getDecisionFromSendIndex(const MatchItemSend &item, const FB::PolicySend *policy);
145 void printContent(const bool xml_format = false) const;
147 template <typename T, typename M = typename type_helper<T>::policy_set_type>
148 const M *getPolicySet();
151 ldp_xml_parser::DecisionItem StorageBackendSerialized::StorageBackendSerializedImpl::getDecisionFromSendIndex(const MatchItemSend &item,
152 const FB::PolicySend *policy) {
154 const auto *index = policy->index();
156 if (!index || index->size() == 0) // this indicates version earlier than LDP2
157 return getDecisionItem(item, policy); // make it old way for old databases
159 std::pair<const ItemSend*, uint32_t> currentBest(nullptr, 0);
161 auto updateCurrentBest = [¤tBest, &item, &policy](const flatbuffers::Vector<uint32_t> *vec) {
162 // look from the back, the rule is the same as for the full database
163 // we now only check among less elements, because the database is indexed to small lists
164 // item_scores are in increasing order in the index, and they serve also as ids of the policy rules
165 for (auto item_score_it = vec->rbegin(); item_score_it != vec->rend(); item_score_it++) {
166 const auto *fb_item = policy->items()->Get(*item_score_it - 1); // rules are indexed/scored from 1
167 if (*item_score_it > currentBest.second && match(item, fb_item)) {
168 currentBest.first = fb_item;
169 currentBest.second = *item_score_it;
171 } else if (*item_score_it <= currentBest.second) {
172 // there is no need to look further as we can't improve the score anymore
178 auto searchAndUpdateCurrentBest = [¤tBest, &index, &updateCurrentBest](boost::string_ref name_ref) {
179 // we need to create C-string for flatbuffers lookups
180 // boost::string_ref gives us correct start, but possibly NUL-terminated in a wrong place, as it does not modify
181 // input string and keeps only the length
182 std::string name(name_ref.data(), name_ref.size());
184 // check if there are any rules for the name
185 const auto *fit = index->LookupByKey(name.c_str());
189 // check if there's any chance to get better score
190 if (fit->best_score() <= currentBest.second)
193 // look for better score
194 updateCurrentBest(fit->item_refs());
197 const auto *prefixIndex = policy->prefix_index();
200 // iterate over names
201 for (const auto &name: item.getNames()) {
202 // find and check the no-prefix rules
203 searchAndUpdateCurrentBest(name);
205 // check the prefix rules
206 updateCurrentBest(prefixIndex);
209 // check no-name rules
210 searchAndUpdateCurrentBest("");
212 // if a matching rule was found, return the decision
213 if (currentBest.first)
214 return makeDecisionItem(currentBest.first->decision());
216 // or if no matching rule was not found, return the default decision
217 return ldp_xml_parser::DecisionItem(ldp_xml_parser::Decision::ANY);
220 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseMMap() {
221 assert(MAP_FAILED != mem);
224 if (munmap(mem, length) != 0)
225 tslog::log_error("munmap(): ", tslog::print_errno(errno), "\n");
227 mem = static_cast<decltype(mem)>(MAP_FAILED);
231 void StorageBackendSerialized::StorageBackendSerializedImpl::releaseFD() {
235 tslog::log_error("close(): ", tslog::print_errno(errno), "\n");
240 void StorageBackendSerialized::StorageBackendSerializedImpl::release() {
241 if (-1 != fd) { // we need to check it, because we may have initialized the storage directly from File *
246 if (nullptr != serializer.get()) {
247 serializer.reset(nullptr);
253 bool StorageBackendSerialized::StorageBackendSerializedImpl::init(const char *filename, bool verify) {
254 assert(nullptr == file);
256 auto err = [filename] (const char *what) {
257 tslog::log_error("Can't ", what, " ", filename, ": ", tslog::print_errno(errno), "\n");
261 fd = open(filename, O_RDONLY);
265 auto openGuard = transaction_guard::makeGuard([&] () { releaseFD(); });
269 if (fstat(fd, &buf) == -1)
272 length = buf.st_size;
273 if (length > MAX_SFILE_SIZE) {
274 tslog::log_error("Serialized file size(", length, ") is too large. (>", MAX_SFILE_SIZE, ") bytes.\n");
278 mem = reinterpret_cast<uint8_t*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0));
279 if (MAP_FAILED == mem)
282 auto mmapGuard = transaction_guard::makeGuard([&] () { releaseMMap(); });
284 if (!initFromData(mem, verify))
293 bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromData(const uint8_t *mem, bool verify) {
294 assert(nullptr == file);
297 auto verifier = flatbuffers::Verifier(mem, length);
298 if (!FB::VerifyFileBuffer(verifier) || !FB::FileBufferHasIdentifier(mem)) {
299 char fid[FB::FB_ID_SIZE + 1] = {0, };
300 strncpy(fid, (const char *)(mem + FB::FB_ID_OFFSET), FB::FB_ID_SIZE);
302 if (strcmp(fid, "LDP1") == 0) {
303 tslog::log_error("verification of serialized data: not available\n");
304 tslog::log_error("header ID : ", FB::FileIdentifier(), "\n");
305 tslog::log_error("serialized data ID : ", fid, "\n");
307 tslog::log_error("verification of serialized data: failed\n");
314 return file != nullptr;
317 bool StorageBackendSerialized::StorageBackendSerializedImpl::initFromXML(const char *config_name) {
318 assert(nullptr == file);
319 assert(nullptr == serializer.get());
321 serializer.reset(new ldp_serializer::Serializer());
323 size_t serialized_size;
324 uint8_t *data = serializer->serialize(config_name, serialized_size);
328 return initFromData(data, false);
331 void StorageBackendSerialized::StorageBackendSerializedImpl::printContent(const bool xml_format) const {
332 print_content::use_xml_format(xml_format);
336 /*************************************************/
338 #define TYPE_HELPER(T, t) \
340 struct type_helper<ldp_xml_parser::MatchItem##T> { \
341 typedef FB::T##Set policy_set_type; \
342 typedef FB::Policy##T policy_type; \
345 auto StorageBackendSerialized::StorageBackendSerializedImpl::getPolicySet<ldp_xml_parser::MatchItem##T>() \
346 -> const typename type_helper<ldp_xml_parser::MatchItem##T>::policy_set_type * { \
348 return file->m_##t##_set(); \
351 TYPE_HELPER(Own, own)
352 TYPE_HELPER(Send, send)
353 TYPE_HELPER(Receive, receive)
354 TYPE_HELPER(Access, access)
356 bool StorageBackendSerialized::init(const char *filename, bool verify) {
357 return pimpl->init(filename, verify);
360 bool StorageBackendSerialized::initFromData(const uint8_t *serialized_data, bool verify) {
361 return pimpl->initFromData(serialized_data, verify);
364 bool StorageBackendSerialized::initFromXML(const char *config_name) {
365 return pimpl->initFromXML(config_name);
368 void StorageBackendSerialized::release() {
372 void StorageBackendSerialized::printContent(const bool xml_format) const {
373 pimpl->printContent(xml_format);
376 template <typename T>
377 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const T &item) const {
378 return getDecisionItem(item, pimpl->getPolicySet<T>()->context_mandatory());
382 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const MatchItemSend &item) const {
383 return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_mandatory());
387 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const MatchItemSend &item) const {
388 return pimpl->getDecisionFromSendIndex(item, pimpl->getPolicySet<MatchItemSend>()->context_default());
392 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const MatchItemSend &item) const {
393 auto *policyPair = pimpl->getPolicySet<MatchItemSend>()->user()->LookupByKey(uid);
394 if (nullptr == policyPair)
395 return ldp_xml_parser::Decision::ANY;
396 return pimpl->getDecisionFromSendIndex(item, policyPair->policy());
399 template <typename T>
400 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const T &item) const {
401 return getDecisionItem(item, pimpl->getPolicySet<T>()->context_default());
404 template <typename T>
405 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const T &item) const {
406 return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->user()->LookupByKey(uid));
409 template <typename T>
410 ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const T &item) const {
411 return getDecisionItemMaybeNull(item, pimpl->getPolicySet<T>()->group()->LookupByKey(gid));
414 template <typename T>
415 bool StorageBackendSerialized::existsPolicyForGroup(gid_t gid) const {
416 return pimpl->getPolicySet<T>()->group()->LookupByKey(gid) != nullptr;
419 #define T_INSTANTIATION(T) \
420 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextMandatory(const ldp_xml_parser::MatchItem##T &item) const; \
421 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemContextDefault(const ldp_xml_parser::MatchItem##T &item) const;
424 T_INSTANTIATION(Send)
425 T_INSTANTIATION(Receive)
426 T_INSTANTIATION(Access)
428 #undef T_INSTANTIATION
430 #define T_INSTANTIATION2(T) \
431 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemUser(uid_t uid, const ldp_xml_parser::MatchItem##T &item) const; \
432 template ldp_xml_parser::DecisionItem StorageBackendSerialized::getDecisionItemGroup(gid_t gid, const ldp_xml_parser::MatchItem##T &item) const; \
433 template bool StorageBackendSerialized::existsPolicyForGroup<ldp_xml_parser::MatchItem##T>(gid_t) const;
435 T_INSTANTIATION2(Own)
436 T_INSTANTIATION2(Send)
437 T_INSTANTIATION2(Receive)
439 #undef T_INSTANTIATION2
441 StorageBackendSerialized::StorageBackendSerialized()
442 : pimpl{new StorageBackendSerializedImpl} {
445 StorageBackendSerialized::~StorageBackendSerialized() {