blob: 59b4f6118f34641240939fb5784cadafe4d66aef [file] [log] [blame]
Inseob Kim5f8f32c2018-08-24 11:10:44 +09001/*
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
Inseob Kim14e51872018-10-25 14:27:33 +090023#include <algorithm>
Inseob Kim5f8f32c2018-08-24 11:10:44 +090024#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
42namespace {
43
Inseob Kim14e51872018-10-25 14:27:33 +090044std::string GenerateDefaultPropName(const sysprop::Properties& props,
45 const sysprop::Property& prop);
Inseob Kim5f8f32c2018-08-24 11:10:44 +090046bool IsCorrectIdentifier(const std::string& name);
Inseob Kim5f8f32c2018-08-24 11:10:44 +090047bool ValidateProp(const sysprop::Properties& props,
48 const sysprop::Property& prop, std::string* err);
49bool ValidateProps(const sysprop::Properties& props, std::string* err);
50
Inseob Kim14e51872018-10-25 14:27:33 +090051std::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
Inseob Kim5f8f32c2018-08-24 11:10:44 +090073bool IsCorrectIdentifier(const std::string& name) {
74 if (name.empty()) return false;
75 if (std::isalpha(name[0]) == 0 && name[0] != '_') return false;
76
Inseob Kim14e51872018-10-25 14:27:33 +090077 return std::all_of(name.begin() + 1, name.end(), [](char ch) {
78 return std::isalnum(ch) != 0 || ch == '_';
79 });
Inseob Kim5f8f32c2018-08-24 11:10:44 +090080}
81
Inseob Kim14e51872018-10-25 14:27:33 +090082bool IsCorrectPropertyOrApiName(const std::string& name) {
Inseob Kim5f8f32c2018-08-24 11:10:44 +090083 if (name.empty()) return false;
84
Inseob Kim14e51872018-10-25 14:27:33 +090085 static std::unordered_set<char> allowed{'_', '-', '.'};
Inseob Kim5f8f32c2018-08-24 11:10:44 +090086
Inseob Kim14e51872018-10-25 14:27:33 +090087 return std::all_of(name.begin(), name.end(), [](char ch) {
88 return std::isalnum(ch) != 0 || allowed.count(ch) != 0;
89 });
Inseob Kim5f8f32c2018-08-24 11:10:44 +090090}
91
92bool ValidateProp(const sysprop::Properties& props,
93 const sysprop::Property& prop, std::string* err) {
Inseob Kim14e51872018-10-25 14:27:33 +090094 if (!IsCorrectPropertyOrApiName(prop.api_name())) {
95 if (err) *err = "Invalid API name \"" + prop.api_name() + "\"";
Inseob Kim5f8f32c2018-08-24 11:10:44 +090096 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()) {
Inseob Kim14e51872018-10-25 14:27:33 +0900103 if (err)
104 *err = "Enum values are empty for API \"" + prop.api_name() + "\"";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900105 return false;
106 }
107
108 for (const std::string& name : names) {
109 if (!IsCorrectIdentifier(name)) {
110 if (err)
Inseob Kim14e51872018-10-25 14:27:33 +0900111 *err = "Invalid enum value \"" + name + "\" for API \"" +
112 prop.api_name() + "\"";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900113 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(name).second) {
120 if (err)
Inseob Kim14e51872018-10-25 14:27:33 +0900121 *err = "Duplicated enum value \"" + name + "\" for API \"" +
122 prop.api_name() + "\"";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900123 return false;
124 }
125 }
126 }
127
Inseob Kim14e51872018-10-25 14:27:33 +0900128 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("([^.]+\\.)?vendor\\..+");
137 static const std::regex odm_regex("([^.]+\\.)?odm\\..+");
138
139 switch (props.owner()) {
140 case sysprop::Platform:
141 if (std::regex_match(prop_name, vendor_regex) ||
142 std::regex_match(prop_name, odm_regex)) {
143 if (err)
144 *err = "Prop \"" + prop_name +
145 "\" owned by platform cannot have vendor. or odm. namespace";
146 return false;
147 }
148 break;
149 case sysprop::Vendor:
150 if (!std::regex_match(prop_name, vendor_regex)) {
151 if (err)
152 *err = "Prop \"" + prop_name +
153 "\" owned by vendor should have vendor. namespace";
154 return false;
155 }
156 break;
157 case sysprop::Odm:
158 if (!std::regex_match(prop_name, odm_regex)) {
159 if (err)
160 *err = "Prop \"" + prop_name +
161 "\" owned by odm should have odm. namespace";
162 return false;
163 }
164 break;
165 }
166
167 switch (prop.access()) {
168 case sysprop::ReadWrite:
169 if (android::base::StartsWith(prop_name, "ro.")) {
170 if (err) {
171 *err = "Prop \"" + prop_name +
172 "\" is ReadWrite and also have prefix \"ro.\"";
173 }
174 return false;
175 }
176 break;
177 default:
178 if (!android::base::StartsWith(prop_name, "ro.")) {
179 if (err) {
180 *err = "Prop \"" + prop_name +
181 "\" isn't ReadWrite, but don't have prefix \"ro.\"";
182 }
183 return false;
184 }
185 break;
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900186 }
187
188 return true;
189}
190
191bool ValidateProps(const sysprop::Properties& props, std::string* err) {
192 std::vector<std::string> names = android::base::Split(props.module(), ".");
193 if (names.size() <= 1) {
194 if (err) *err = "Invalid module name \"" + props.module() + "\"";
195 return false;
196 }
197
198 for (const auto& name : names) {
199 if (!IsCorrectIdentifier(name)) {
200 if (err) *err = "Invalid name \"" + name + "\" in module";
201 return false;
202 }
203 }
204
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900205 if (props.prop_size() == 0) {
206 if (err) *err = "There is no defined property";
207 return false;
208 }
209
210 for (int i = 0; i < props.prop_size(); ++i) {
211 const auto& prop = props.prop(i);
212 if (!ValidateProp(props, prop, err)) return false;
213 }
214
215 std::unordered_set<std::string> prop_names;
216
217 for (int i = 0; i < props.prop_size(); ++i) {
218 const auto& prop = props.prop(i);
Inseob Kim14e51872018-10-25 14:27:33 +0900219 auto res = prop_names.insert(ApiNameToIdentifier(prop.api_name()));
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900220
221 if (!res.second) {
Inseob Kim14e51872018-10-25 14:27:33 +0900222 if (err) *err = "Duplicated API name \"" + prop.api_name() + "\"";
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900223 return false;
224 }
225 }
226
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900227 return true;
228}
229
230} // namespace
231
232// For directory functions, we could use <filesystem> of C++17 if supported..
233bool CreateDirectories(const std::string& path) {
234 struct stat st;
235
236 // If already exists..
237 if (stat(path.c_str(), &st) == 0) {
238 return false;
239 }
240
241 size_t last_slash = path.rfind('/');
242 if (last_slash > 0 && last_slash != std::string::npos) {
243 std::string parent = path.substr(0, last_slash);
244 if (!IsDirectory(parent) && !CreateDirectories(parent)) return false;
245 }
246
247 // It's very unlikely, but if path contains ".." or any symbolic links, it
248 // might already be created before this line.
249 return mkdir(path.c_str(), 0755) == 0 || IsDirectory(path);
250}
251
252bool IsDirectory(const std::string& path) {
253 struct stat st;
254
255 if (stat(path.c_str(), &st) == -1) return false;
256 return S_ISDIR(st.st_mode);
257}
258
259std::string GetModuleName(const sysprop::Properties& props) {
260 const std::string& module = props.module();
261 return module.substr(module.rfind('.') + 1);
262}
263
264bool ParseProps(const std::string& input_file_path, sysprop::Properties* props,
265 std::string* err) {
266 std::string file_contents;
267
268 if (!android::base::ReadFileToString(input_file_path, &file_contents, true)) {
269 *err = "Error reading file " + input_file_path + ": " + strerror(errno);
270 return false;
271 }
272
273 if (!google::protobuf::TextFormat::ParseFromString(file_contents, props)) {
274 *err = "Error parsing file " + input_file_path;
275 return false;
276 }
277
278 if (!ValidateProps(*props, err)) {
279 return false;
280 }
281
282 for (int i = 0; i < props->prop_size(); ++i) {
283 // set each optional field to its default value
284 sysprop::Property& prop = *props->mutable_prop(i);
Inseob Kim14e51872018-10-25 14:27:33 +0900285 if (prop.prop_name().empty())
286 prop.set_prop_name(GenerateDefaultPropName(*props, prop));
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900287 }
288
289 return true;
290}
291
Inseob Kim14e51872018-10-25 14:27:33 +0900292std::string ApiNameToIdentifier(const std::string& name) {
293 static const std::regex kRegexAllowed{"-|\\."};
294 return (isdigit(name[0]) ? "_" : "") +
295 std::regex_replace(name, kRegexAllowed, "_");
Inseob Kim5f8f32c2018-08-24 11:10:44 +0900296}