// // Copyright (C) 2017 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "property_info_parser/property_info_parser.h" #include #include #include #include #include #include namespace android { namespace properties { namespace { // Binary search to find index of element in an array compared via f(search). template int Find(uint32_t array_length, F&& f) { int bottom = 0; int top = array_length - 1; while (top >= bottom) { int search = (top + bottom) / 2; auto cmp = f(search); if (cmp == 0) return search; if (cmp < 0) bottom = search + 1; if (cmp > 0) top = search - 1; } return -1; } } // namespace // Binary search the list of contexts to find the index of a given context string. // Only should be used for TrieSerializer to construct the Trie. int PropertyInfoArea::FindContextIndex(const char* context) const { return Find(num_contexts(), [this, context](auto array_offset) { auto string_offset = uint32_array(contexts_array_offset())[array_offset]; return strcmp(c_string(string_offset), context); }); } // Binary search the list of types to find the index of a given type string. // Only should be used for TrieSerializer to construct the Trie. int PropertyInfoArea::FindTypeIndex(const char* type) const { return Find(num_types(), [this, type](auto array_offset) { auto string_offset = uint32_array(types_array_offset())[array_offset]; return strcmp(c_string(string_offset), type); }); } // Binary search the list of children nodes to find a TrieNode for a given property piece. // Used to traverse the Trie in GetPropertyInfoIndexes(). bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const { auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) { const char* child_name = child_node(array_offset).name(); int cmp = strncmp(child_name, name, namelen); if (cmp == 0 && child_name[namelen] != '\0') { // We use strncmp() since name isn't null terminated, but we don't want to match only a // prefix of a child node's name, so we check here if we did only match a prefix and // return 1, to indicate to the binary search to search earlier in the array for the real // match. return 1; } return cmp; }); if (node_index == -1) { return false; } *child = child_node(node_index); return true; } void PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node, uint32_t* context_index, uint32_t* type_index) const { const uint32_t remaining_name_size = strlen(remaining_name); for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) { auto prefix_len = trie_node.prefix(i)->namelen; if (prefix_len > remaining_name_size) continue; if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) { if (trie_node.prefix(i)->context_index != ~0u) { *context_index = trie_node.prefix(i)->context_index; } if (trie_node.prefix(i)->type_index != ~0u) { *type_index = trie_node.prefix(i)->type_index; } return; } } } void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index, uint32_t* type_index) const { uint32_t return_context_index = ~0u; uint32_t return_type_index = ~0u; const char* remaining_name = name; auto trie_node = root_node(); while (true) { const char* sep = strchr(remaining_name, '.'); // Apply prefix match for prefix deliminated with '.' if (trie_node.context_index() != ~0u) { return_context_index = trie_node.context_index(); } if (trie_node.type_index() != ~0u) { return_type_index = trie_node.type_index(); } // Check prefixes at this node. This comes after the node check since these prefixes are by // definition longer than the node itself. CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index); if (sep == nullptr) { break; } const uint32_t substr_size = sep - remaining_name; TrieNode child_node; if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) { break; } trie_node = child_node; remaining_name = sep + 1; } // We've made it to a leaf node, so check contents and return appropriately. // Check exact matches for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) { if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) { if (context_index != nullptr) { if (trie_node.exact_match(i)->context_index != ~0u) { *context_index = trie_node.exact_match(i)->context_index; } else { *context_index = return_context_index; } } if (type_index != nullptr) { if (trie_node.exact_match(i)->type_index != ~0u) { *type_index = trie_node.exact_match(i)->type_index; } else { *type_index = return_type_index; } } return; } } // Check prefix matches for prefixes not deliminated with '.' CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index); // Return previously found prefix match. if (context_index != nullptr) *context_index = return_context_index; if (type_index != nullptr) *type_index = return_type_index; return; } void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context, const char** type) const { uint32_t context_index; uint32_t type_index; GetPropertyInfoIndexes(property, &context_index, &type_index); if (context != nullptr) { if (context_index == ~0u) { *context = nullptr; } else { *context = this->context(context_index); } } if (type != nullptr) { if (type_index == ~0u) { *type = nullptr; } else { *type = this->type(type_index); } } } bool PropertyInfoAreaFile::LoadDefaultPath() { return LoadPath("/dev/__properties__/property_info"); } bool PropertyInfoAreaFile::LoadPath(const char* filename) { int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); struct stat fd_stat; if (fstat(fd, &fd_stat) < 0) { close(fd); return false; } if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) || (fd_stat.st_size < static_cast(sizeof(PropertyInfoArea)))) { close(fd); return false; } auto mmap_size = fd_stat.st_size; void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0); if (map_result == MAP_FAILED) { close(fd); return false; } auto property_info_area = reinterpret_cast(map_result); if (property_info_area->minimum_supported_version() > 1 || property_info_area->size() != mmap_size) { munmap(map_result, mmap_size); close(fd); return false; } close(fd); mmap_base_ = map_result; mmap_size_ = mmap_size; return true; } void PropertyInfoAreaFile::Reset() { if (mmap_size_ > 0) { munmap(mmap_base_, mmap_size_); } mmap_base_ = nullptr; mmap_size_ = 0; } } // namespace properties } // namespace android