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