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