• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 "Common.h"
18 
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 
23 #include <algorithm>
24 #include <cctype>
25 #include <cerrno>
26 #include <cmath>
27 #include <cstdlib>
28 #include <cstring>
29 #include <initializer_list>
30 #include <memory>
31 #include <regex>
32 #include <string>
33 #include <unordered_set>
34 #include <vector>
35 
36 #include <android-base/file.h>
37 #include <android-base/logging.h>
38 #include <android-base/strings.h>
39 #include <google/protobuf/text_format.h>
40 
41 #include "sysprop.pb.h"
42 
43 using android::base::Result;
44 
45 namespace {
46 
47 std::string GenerateDefaultPropName(const sysprop::Properties& props,
48                                     const sysprop::Property& prop);
49 bool IsCorrectIdentifier(const std::string& name);
50 Result<void> ValidateProp(const sysprop::Properties& props,
51                           const sysprop::Property& prop);
52 Result<void> ValidateProps(const sysprop::Properties& props);
53 
GenerateDefaultPropName(const sysprop::Properties & props,const sysprop::Property & prop)54 std::string GenerateDefaultPropName(const sysprop::Properties& props,
55                                     const sysprop::Property& prop) {
56   std::string ret;
57 
58   if (prop.access() != sysprop::ReadWrite) ret = "ro.";
59 
60   switch (props.owner()) {
61     case sysprop::Vendor:
62       ret += "vendor.";
63       break;
64     case sysprop::Odm:
65       ret += "odm.";
66       break;
67     default:
68       break;
69   }
70 
71   ret += prop.api_name();
72 
73   return ret;
74 }
75 
IsCorrectIdentifier(const std::string & name)76 bool IsCorrectIdentifier(const std::string& name) {
77   if (name.empty()) return false;
78   if (std::isalpha(name[0]) == 0 && name[0] != '_') return false;
79 
80   return std::all_of(name.begin() + 1, name.end(), [](char ch) {
81     return std::isalnum(ch) != 0 || ch == '_';
82   });
83 }
84 
IsCorrectName(const std::string & name,const std::unordered_set<char> & allowed_chars)85 bool IsCorrectName(const std::string& name,
86                    const std::unordered_set<char>& allowed_chars) {
87   if (name.empty()) return false;
88   if (!std::isalpha(*name.begin())) return false;
89 
90   return std::all_of(name.begin(), name.end(), [allowed_chars](char ch) {
91     return std::isalnum(ch) != 0 || allowed_chars.count(ch) != 0;
92   });
93 }
94 
IsCorrectPropertyName(const std::string & name)95 bool IsCorrectPropertyName(const std::string& name) {
96   std::unordered_set<char> allowed{'_', '-', '.'};
97   if (android::base::StartsWith(name, "ctl.")) {
98     allowed.emplace('$');
99   }
100   return IsCorrectName(name, allowed);
101 }
102 
IsCorrectApiName(const std::string & name)103 bool IsCorrectApiName(const std::string& name) {
104   static std::unordered_set<char> allowed{'_', '-'};
105   return IsCorrectName(name, allowed);
106 }
107 
ValidateProp(const sysprop::Properties & props,const sysprop::Property & prop)108 Result<void> ValidateProp(const sysprop::Properties& props,
109                           const sysprop::Property& prop) {
110   if (!IsCorrectApiName(prop.api_name())) {
111     return Errorf("Invalid API name \"{}\"", prop.api_name());
112   }
113 
114   if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
115     std::vector<std::string> names =
116         android::base::Split(prop.enum_values(), "|");
117     if (names.empty()) {
118       return Errorf("Enum values are empty for API \"{}\"", prop.api_name());
119     }
120 
121     for (const std::string& name : names) {
122       if (!IsCorrectIdentifier(name)) {
123         return Errorf("Invalid enum value \"{}\" for API \"{}\"", name,
124                       prop.api_name());
125       }
126     }
127 
128     std::unordered_set<std::string> name_set;
129     for (const std::string& name : names) {
130       if (!name_set.insert(ToUpper(name)).second) {
131         return Errorf("Duplicated enum value \"{}\" for API \"{}\"", name,
132                       prop.api_name());
133       }
134     }
135   }
136 
137   std::string prop_name = prop.prop_name();
138   if (prop_name.empty()) prop_name = GenerateDefaultPropName(props, prop);
139 
140   if (!IsCorrectPropertyName(prop_name)) {
141     return Errorf("Invalid prop name \"{}\"", prop.prop_name());
142   }
143 
144   static const std::regex vendor_regex(
145       "(init\\.svc\\.|ro\\.|persist\\.)?vendor\\..+|ro\\.hardware\\..+");
146   static const std::regex odm_regex(
147       "(init\\.svc\\.|ro\\.|persist\\.)?odm\\..+|ro\\.hardware\\..+");
148 
149   switch (props.owner()) {
150     case sysprop::Platform:
151       if (std::regex_match(prop_name, vendor_regex) ||
152           std::regex_match(prop_name, odm_regex)) {
153         return Errorf(
154             "Prop \"{}\" owned by platform cannot have vendor. or odm. "
155             "namespace",
156             prop_name);
157       }
158       break;
159     case sysprop::Vendor:
160       if (!std::regex_match(prop_name, vendor_regex)) {
161         return Errorf(
162             "Prop \"{}\" owned by vendor should have vendor. namespace",
163             prop_name);
164       }
165       break;
166     case sysprop::Odm:
167       if (!std::regex_match(prop_name, odm_regex)) {
168         return Errorf("Prop \"{}\" owned by odm should have odm. namespace",
169                       prop_name);
170       }
171       break;
172     default:
173       break;
174   }
175 
176   switch (prop.access()) {
177     case sysprop::ReadWrite:
178       if (android::base::StartsWith(prop_name, "ro.")) {
179         return Errorf("Prop \"{}\" is ReadWrite and also have prefix \"ro.\"",
180                       prop_name);
181       }
182       break;
183     default:
184       /*
185        * TODO: Some properties don't have prefix "ro." but not written in any
186        * Java or C++ codes. They might be misnamed and should be readonly. Will
187        * uncomment this check after fixing them all / or making a whitelist for
188        * them
189       if (!android::base::StartsWith(prop_name, "ro.")) {
190         return Errorf("Prop \"{}\" isn't ReadWrite, but don't have prefix
191       \"ro.\"", prop_name);
192       }
193       */
194       break;
195   }
196 
197   if (prop.integer_as_bool() && !(prop.type() == sysprop::Boolean ||
198                                   prop.type() == sysprop::BooleanList)) {
199     return Errorf("Prop \"{}\" has integer_as_bool: true, but not a boolean",
200                   prop_name);
201   }
202 
203   return {};
204 }
205 
ValidateProps(const sysprop::Properties & props)206 Result<void> ValidateProps(const sysprop::Properties& props) {
207   std::vector<std::string> names = android::base::Split(props.module(), ".");
208   if (names.size() <= 1) {
209     return Errorf("Invalid module name \"{}\"", props.module());
210   }
211 
212   for (const auto& name : names) {
213     if (!IsCorrectIdentifier(name)) {
214       return Errorf("Invalid name \"{}\" in module", name);
215     }
216   }
217 
218   if (props.prop_size() == 0) {
219     return Errorf("There is no defined property");
220   }
221 
222   for (int i = 0; i < props.prop_size(); ++i) {
223     const auto& prop = props.prop(i);
224     if (auto res = ValidateProp(props, prop); !res.ok()) return res;
225   }
226 
227   std::unordered_set<std::string> prop_names;
228 
229   for (int i = 0; i < props.prop_size(); ++i) {
230     const auto& prop = props.prop(i);
231     auto res = prop_names.insert(ApiNameToIdentifier(prop.api_name()));
232 
233     if (!res.second) {
234       return Errorf("Duplicated API name \"{}\"", prop.api_name());
235     }
236   }
237 
238   return {};
239 }
240 
SetDefaultValues(sysprop::Properties * props)241 void SetDefaultValues(sysprop::Properties* props) {
242   for (int i = 0; i < props->prop_size(); ++i) {
243     // set each optional field to its default value
244     sysprop::Property& prop = *props->mutable_prop(i);
245     if (prop.prop_name().empty())
246       prop.set_prop_name(GenerateDefaultPropName(*props, prop));
247     if (prop.scope() == sysprop::Scope::System) {
248       LOG(WARNING) << "Sysprop API " << prop.api_name()
249                    << ": System scope is deprecated."
250                    << " Please use Public scope instead.";
251       prop.set_scope(sysprop::Scope::Public);
252     }
253   }
254 }
255 
256 }  // namespace
257 
IsListProp(const sysprop::Property & prop)258 bool IsListProp(const sysprop::Property& prop) {
259   switch (prop.type()) {
260     case sysprop::BooleanList:
261     case sysprop::IntegerList:
262     case sysprop::LongList:
263     case sysprop::DoubleList:
264     case sysprop::StringList:
265     case sysprop::EnumList:
266       return true;
267     default:
268       return false;
269   }
270 }
271 
GetModuleName(const sysprop::Properties & props)272 std::string GetModuleName(const sysprop::Properties& props) {
273   const std::string& module = props.module();
274   return module.substr(module.rfind('.') + 1);
275 }
276 
ParseProps(const std::string & input_file_path)277 Result<sysprop::Properties> ParseProps(const std::string& input_file_path) {
278   sysprop::Properties ret;
279   std::string file_contents;
280 
281   if (!android::base::ReadFileToString(input_file_path, &file_contents, true)) {
282     return ErrnoErrorf("Error reading file {}", input_file_path);
283   }
284 
285   if (!google::protobuf::TextFormat::ParseFromString(file_contents, &ret)) {
286     return Errorf("Error parsing file {}", input_file_path);
287   }
288 
289   if (auto res = ValidateProps(ret); !res.ok()) {
290     return res.error();
291   }
292 
293   SetDefaultValues(&ret);
294 
295   return ret;
296 }
297 
ParseApiFile(const std::string & input_file_path)298 Result<sysprop::SyspropLibraryApis> ParseApiFile(
299     const std::string& input_file_path) {
300   sysprop::SyspropLibraryApis ret;
301   std::string file_contents;
302 
303   if (!android::base::ReadFileToString(input_file_path, &file_contents, true)) {
304     return ErrnoErrorf("Error reading file {}", input_file_path);
305   }
306 
307   if (!google::protobuf::TextFormat::ParseFromString(file_contents, &ret)) {
308     return Errorf("Error parsing file {}", input_file_path);
309   }
310 
311   std::unordered_set<std::string> modules;
312 
313   for (int i = 0; i < ret.props_size(); ++i) {
314     sysprop::Properties* props = ret.mutable_props(i);
315 
316     if (!modules.insert(props->module()).second) {
317       return Errorf("Error parsing file {}: duplicated module {}",
318                     input_file_path, props->module());
319     }
320 
321     if (auto res = ValidateProps(*props); !res.ok()) {
322       return res.error();
323     }
324 
325     SetDefaultValues(props);
326   }
327 
328   return ret;
329 }
330 
ToUpper(std::string str)331 std::string ToUpper(std::string str) {
332   for (char& ch : str) {
333     ch = toupper(ch);
334   }
335   return str;
336 }
337 
ApiNameToIdentifier(const std::string & name)338 std::string ApiNameToIdentifier(const std::string& name) {
339   static const std::regex kRegexAllowed{"-|\\."};
340   return (isdigit(name[0]) ? "_" : "") +
341          std::regex_replace(name, kRegexAllowed, "_");
342 }
343