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