1 // 2 // Copyright (C) 2017 The Android Open Source Project 3 // 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 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 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. 15 // 16 17 #include "property_info_parser/property_info_parser.h" 18 19 #include <fcntl.h> 20 #include <string.h> 21 #include <sys/mman.h> 22 #include <sys/stat.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 26 namespace android { 27 namespace properties { 28 29 namespace { 30 31 // Binary search to find index of element in an array compared via f(search). 32 template <typename F> Find(uint32_t array_length,F && f)33 int Find(uint32_t array_length, F&& f) { 34 int bottom = 0; 35 int top = array_length - 1; 36 while (top >= bottom) { 37 int search = (top + bottom) / 2; 38 39 auto cmp = f(search); 40 41 if (cmp == 0) return search; 42 if (cmp < 0) bottom = search + 1; 43 if (cmp > 0) top = search - 1; 44 } 45 return -1; 46 } 47 48 } // namespace 49 50 // Binary search the list of contexts to find the index of a given context string. 51 // Only should be used for TrieSerializer to construct the Trie. FindContextIndex(const char * context) const52 int PropertyInfoArea::FindContextIndex(const char* context) const { 53 return Find(num_contexts(), [this, context](auto array_offset) { 54 auto string_offset = uint32_array(contexts_array_offset())[array_offset]; 55 return strcmp(c_string(string_offset), context); 56 }); 57 } 58 59 // Binary search the list of types to find the index of a given type string. 60 // Only should be used for TrieSerializer to construct the Trie. FindTypeIndex(const char * type) const61 int PropertyInfoArea::FindTypeIndex(const char* type) const { 62 return Find(num_types(), [this, type](auto array_offset) { 63 auto string_offset = uint32_array(types_array_offset())[array_offset]; 64 return strcmp(c_string(string_offset), type); 65 }); 66 } 67 68 // Binary search the list of children nodes to find a TrieNode for a given property piece. 69 // Used to traverse the Trie in GetPropertyInfoIndexes(). FindChildForString(const char * name,uint32_t namelen,TrieNode * child) const70 bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const { 71 auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) { 72 const char* child_name = child_node(array_offset).name(); 73 int cmp = strncmp(child_name, name, namelen); 74 if (cmp == 0 && child_name[namelen] != '\0') { 75 // We use strncmp() since name isn't null terminated, but we don't want to match only a 76 // prefix of a child node's name, so we check here if we did only match a prefix and 77 // return 1, to indicate to the binary search to search earlier in the array for the real 78 // match. 79 return 1; 80 } 81 return cmp; 82 }); 83 84 if (node_index == -1) { 85 return false; 86 } 87 *child = child_node(node_index); 88 return true; 89 } 90 CheckPrefixMatch(const char * remaining_name,const TrieNode & trie_node,uint32_t * context_index,uint32_t * type_index) const91 void PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node, 92 uint32_t* context_index, uint32_t* type_index) const { 93 const uint32_t remaining_name_size = strlen(remaining_name); 94 for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) { 95 auto prefix_len = trie_node.prefix(i)->namelen; 96 if (prefix_len > remaining_name_size) continue; 97 98 if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) { 99 if (trie_node.prefix(i)->context_index != ~0u) { 100 *context_index = trie_node.prefix(i)->context_index; 101 } 102 if (trie_node.prefix(i)->type_index != ~0u) { 103 *type_index = trie_node.prefix(i)->type_index; 104 } 105 return; 106 } 107 } 108 } 109 GetPropertyInfoIndexes(const char * name,uint32_t * context_index,uint32_t * type_index) const110 void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index, 111 uint32_t* type_index) const { 112 uint32_t return_context_index = ~0u; 113 uint32_t return_type_index = ~0u; 114 const char* remaining_name = name; 115 auto trie_node = root_node(); 116 while (true) { 117 const char* sep = strchr(remaining_name, '.'); 118 119 // Apply prefix match for prefix deliminated with '.' 120 if (trie_node.context_index() != ~0u) { 121 return_context_index = trie_node.context_index(); 122 } 123 if (trie_node.type_index() != ~0u) { 124 return_type_index = trie_node.type_index(); 125 } 126 127 // Check prefixes at this node. This comes after the node check since these prefixes are by 128 // definition longer than the node itself. 129 CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index); 130 131 if (sep == nullptr) { 132 break; 133 } 134 135 const uint32_t substr_size = sep - remaining_name; 136 TrieNode child_node; 137 if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) { 138 break; 139 } 140 141 trie_node = child_node; 142 remaining_name = sep + 1; 143 } 144 145 // We've made it to a leaf node, so check contents and return appropriately. 146 // Check exact matches 147 for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) { 148 if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) { 149 if (context_index != nullptr) { 150 if (trie_node.exact_match(i)->context_index != ~0u) { 151 *context_index = trie_node.exact_match(i)->context_index; 152 } else { 153 *context_index = return_context_index; 154 } 155 } 156 if (type_index != nullptr) { 157 if (trie_node.exact_match(i)->type_index != ~0u) { 158 *type_index = trie_node.exact_match(i)->type_index; 159 } else { 160 *type_index = return_type_index; 161 } 162 } 163 return; 164 } 165 } 166 // Check prefix matches for prefixes not deliminated with '.' 167 CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index); 168 // Return previously found prefix match. 169 if (context_index != nullptr) *context_index = return_context_index; 170 if (type_index != nullptr) *type_index = return_type_index; 171 return; 172 } 173 GetPropertyInfo(const char * property,const char ** context,const char ** type) const174 void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context, 175 const char** type) const { 176 uint32_t context_index; 177 uint32_t type_index; 178 GetPropertyInfoIndexes(property, &context_index, &type_index); 179 if (context != nullptr) { 180 if (context_index == ~0u) { 181 *context = nullptr; 182 } else { 183 *context = this->context(context_index); 184 } 185 } 186 if (type != nullptr) { 187 if (type_index == ~0u) { 188 *type = nullptr; 189 } else { 190 *type = this->type(type_index); 191 } 192 } 193 } 194 LoadDefaultPath()195 bool PropertyInfoAreaFile::LoadDefaultPath() { 196 return LoadPath("/dev/__properties__/property_info"); 197 } 198 LoadPath(const char * filename)199 bool PropertyInfoAreaFile::LoadPath(const char* filename) { 200 int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); 201 202 struct stat fd_stat; 203 if (fstat(fd, &fd_stat) < 0) { 204 close(fd); 205 return false; 206 } 207 208 if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) || 209 ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) || 210 (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) { 211 close(fd); 212 return false; 213 } 214 215 auto mmap_size = fd_stat.st_size; 216 217 void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0); 218 if (map_result == MAP_FAILED) { 219 close(fd); 220 return false; 221 } 222 223 auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result); 224 if (property_info_area->minimum_supported_version() > 1 || 225 property_info_area->size() != mmap_size) { 226 munmap(map_result, mmap_size); 227 close(fd); 228 return false; 229 } 230 231 close(fd); 232 mmap_base_ = map_result; 233 mmap_size_ = mmap_size; 234 return true; 235 } 236 Reset()237 void PropertyInfoAreaFile::Reset() { 238 if (mmap_size_ > 0) { 239 munmap(mmap_base_, mmap_size_); 240 } 241 mmap_base_ = nullptr; 242 mmap_size_ = 0; 243 } 244 245 } // namespace properties 246 } // namespace android 247