| // | |
| // 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 <fcntl.h> | |
| #include <string.h> | |
| #include <sys/mman.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| namespace android { | |
| namespace properties { | |
| namespace { | |
| // Binary search to find index of element in an array compared via f(search). | |
| template <typename F> | |
| 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<off_t>(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<PropertyInfoArea*>(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 |