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