1 /*
2 * Copyright (C) 2019 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 #pragma once
17
18 #include <cstring>
19 #include <regex>
20
21 #include "gtest/gtest.h"
22 #include "modules.h"
23
24 namespace {
25 constexpr const char* kSectionNameRegex = "\\[\\s*(\\w+)\\s*\\]";
26 constexpr const char* kDirRegex = "dir\\.(\\w+)\\s*=\\s*([\\w_\\-/]+)";
27 constexpr const char* kNamespaceBaseRegex =
28 "namespace\\.(\\w+)\\.([^\\s=]+)\\s*(=|\\+=)\\s*([^\\s]+)";
29 constexpr const char* kAdditionalNamespacesRegex =
30 "additional\\.namespaces\\s*=\\s*((?:[\\w]+)(?:,[\\w]+)*)";
31
32 // Functions to parse configuration string and verify syntax
33
ParseDirPath(const std::string & line,Configuration & conf)34 inline void ParseDirPath(const std::string& line, Configuration& conf) {
35 static std::regex dir_regex(kDirRegex);
36 std::smatch match;
37
38 ASSERT_TRUE(std::regex_match(line, match, dir_regex)) << line;
39 ASSERT_EQ(3u, match.size()) << line;
40 std::string section_name = match[1];
41 std::string dir_path = match[2];
42
43 if (!MapContainsKey(conf.sections, section_name)) {
44 conf.sections[section_name].name = section_name;
45 conf.sections[section_name].namespaces["default"].name = "default";
46 }
47
48 conf.sections[section_name].dirs.push_back(dir_path);
49 }
50
ParseAdditionalNamespaces(const std::smatch & match,Section & current_section)51 inline void ParseAdditionalNamespaces(const std::smatch& match,
52 Section& current_section) {
53 // additional.namespace = a,b,c,e,d
54 ASSERT_EQ(2u, match.size());
55 std::stringstream namespaces(match[1]);
56 for (std::string namespace_name;
57 std::getline(namespaces, namespace_name, ',');) {
58 EXPECT_FALSE(MapContainsKey(current_section.namespaces, namespace_name))
59 << "Namespace " << namespace_name << " already exists";
60 Namespace new_namespace;
61 new_namespace.name = namespace_name;
62 current_section.namespaces[namespace_name] = new_namespace;
63 }
64 }
65
ParseNamespacePath(const std::vector<std::string> & property_descs,const bool is_additional,const std::string & path,Namespace & current_namespace,const std::string & line)66 inline void ParseNamespacePath(const std::vector<std::string>& property_descs,
67 const bool is_additional, const std::string& path,
68 Namespace& current_namespace,
69 const std::string& line) {
70 // namespace.test.(asan.)search|permitted.path =|+= /path/to/${LIB}/dir
71 ASSERT_EQ(property_descs[0] == "asan" ? 3u : 2u, property_descs.size());
72
73 std::vector<std::string>* target_path = nullptr;
74 if (property_descs[0] == "search") {
75 target_path = ¤t_namespace.search_path;
76 } else if (property_descs[0] == "permitted") {
77 target_path = ¤t_namespace.permitted_path;
78 } else if (property_descs[0] == "asan" && property_descs[1] == "search") {
79 target_path = ¤t_namespace.asan_search_path;
80 } else if (property_descs[0] == "asan" && property_descs[1] == "permitted") {
81 target_path = ¤t_namespace.asan_permitted_path;
82 }
83
84 ASSERT_NE(nullptr, target_path) << line;
85 EXPECT_EQ(is_additional, target_path->size() != 0)
86 << "Path should be marked as = if and only if it is mentioned first : "
87 << line;
88
89 target_path->push_back(path);
90 }
91
ParseLinkList(const std::vector<std::string> & property_descs,const std::string & target_namespaces,Namespace & current_namespace,Section & current_section,const std::string & line)92 inline void ParseLinkList(const std::vector<std::string>& property_descs,
93 const std::string& target_namespaces,
94 Namespace& current_namespace,
95 Section& current_section, const std::string& line) {
96 // namespace.test.links = a,b,c,d,e
97 EXPECT_EQ(1u, property_descs.size());
98 std::stringstream namespaces(target_namespaces);
99 for (std::string namespace_to; std::getline(namespaces, namespace_to, ',');) {
100 EXPECT_FALSE(MapContainsKey(current_namespace.links, namespace_to))
101 << "Link to " << namespace_to << " is already defined : " << line;
102 EXPECT_TRUE(MapContainsKey(current_section.namespaces, namespace_to))
103 << "Target namespace " << namespace_to
104 << " is not defined in section : " << line;
105
106 current_namespace.links[namespace_to].from = ¤t_namespace;
107 current_namespace.links[namespace_to].to =
108 ¤t_section.namespaces[namespace_to];
109 current_namespace.links[namespace_to].allow_all_shared = false;
110 }
111 }
112
ParseLink(const std::vector<std::string> & property_descs,const bool is_additional,const std::string & value,Namespace & current_namespace,Section & current_section,const std::string & line)113 inline void ParseLink(const std::vector<std::string>& property_descs,
114 const bool is_additional, const std::string& value,
115 Namespace& current_namespace, Section& current_section,
116 const std::string& line) {
117 // namespace.from.link.to.shared_libs = a.so
118 // namespace.from.link.to.allow_all_shared_libs = true
119 ASSERT_EQ(3u, property_descs.size()) << line;
120 ASSERT_TRUE(property_descs[2] == "shared_libs" ||
121 property_descs[2] == "allow_all_shared_libs")
122 << line;
123 std::string namespace_to = property_descs[1];
124
125 ASSERT_TRUE(MapContainsKey(current_section.namespaces, namespace_to))
126 << "To namespace does not exist in section " << current_section.name
127 << " : " << line;
128
129 if (property_descs[2] == "shared_libs") {
130 EXPECT_EQ(is_additional,
131 current_namespace.links[namespace_to].shared_libs.size() != 0)
132 << "Link should be defined with = if and only if it is first link "
133 "between two namespaces : "
134 << line;
135
136 current_namespace.links[namespace_to].shared_libs.push_back(value);
137 } else {
138 EXPECT_EQ("true", value) << line;
139 current_namespace.links[namespace_to].allow_all_shared = true;
140 }
141 }
142
ParseNamespaceCommand(const std::string & namespace_name,const std::string & property_desc,const bool is_additional_property,const std::string & value,Section & current_section,const std::string & line)143 inline void ParseNamespaceCommand(const std::string& namespace_name,
144 const std::string& property_desc,
145 const bool is_additional_property,
146 const std::string& value,
147 Section& current_section,
148 const std::string& line) {
149 ASSERT_TRUE(MapContainsKey(current_section.namespaces, namespace_name))
150 << "Namespace " << namespace_name << " does not exist in section "
151 << current_section.name << " : " << line;
152 Namespace& current_namespace = current_section.namespaces[namespace_name];
153
154 std::vector<std::string> property_descs;
155 std::stringstream property_desc_stream(property_desc);
156 for (std::string property;
157 std::getline(property_desc_stream, property, '.');) {
158 property_descs.push_back(property);
159 }
160
161 ASSERT_TRUE(property_descs.size() > 0)
162 << "There should be at least one property description after namespace."
163 << namespace_name << " : " << line;
164
165 if (property_descs[0].compare("isolated") == 0) {
166 // namespace.test.isolated = true
167 EXPECT_EQ(1u, property_descs.size()) << line;
168 EXPECT_TRUE(value == "true" || value == "false") << line;
169 current_namespace.is_isolated = value == "true";
170 } else if (property_descs[0].compare("visible") == 0) {
171 // namespace.test.visible = true
172 EXPECT_EQ(1u, property_descs.size()) << line;
173 EXPECT_TRUE(value == "true" || value == "false") << line;
174 current_namespace.is_visible = value == "true";
175 } else if (property_descs[property_descs.size() - 1] == "paths") {
176 // namespace.test.search.path += /system/lib
177 ParseNamespacePath(
178 property_descs, is_additional_property, value, current_namespace, line);
179 } else if (property_descs[0] == "links") {
180 // namespace.test.links = a,b,c
181 ParseLinkList(
182 property_descs, value, current_namespace, current_section, line);
183 } else if (property_descs[0] == "link") {
184 // namespace.test.link.a = libc.so
185 ParseLink(property_descs,
186 is_additional_property,
187 value,
188 current_namespace,
189 current_section,
190 line);
191 } else if (property_descs[0] == "allowed_libs") {
192 EXPECT_EQ(1u, property_descs.size()) << line;
193 current_namespace.allowed_libs.push_back(value);
194 } else {
195 EXPECT_TRUE(false) << "Failed to parse line : " << line;
196 }
197 }
198 } // namespace
199
ParseConfiguration(const std::string & configuration_str,Configuration & conf)200 inline void ParseConfiguration(const std::string& configuration_str,
201 Configuration& conf) {
202 Section* current_section = nullptr;
203
204 static std::regex section_name_regex(kSectionNameRegex);
205 static std::regex additional_namespaces_regex(kAdditionalNamespacesRegex);
206 static std::regex namespace_base_regex(kNamespaceBaseRegex);
207
208 std::smatch match;
209
210 std::stringstream configuration_stream(configuration_str);
211
212 for (std::string line; std::getline(configuration_stream, line);) {
213 // Skip empty line
214 if (line.empty()) {
215 continue;
216 }
217
218 if (std::regex_match(line, match, section_name_regex)) {
219 // [section_name]
220 ASSERT_EQ(2u, match.size()) << line;
221 std::string section_name = match[1];
222 ASSERT_TRUE(MapContainsKey(conf.sections, section_name)) << line;
223 current_section = &conf.sections[section_name];
224
225 continue;
226 }
227
228 if (current_section == nullptr) {
229 ParseDirPath(line, conf);
230 } else {
231 if (std::regex_match(line, match, additional_namespaces_regex)) {
232 ParseAdditionalNamespaces(match, *current_section);
233 } else {
234 EXPECT_TRUE(std::regex_match(line, match, namespace_base_regex)) << line;
235 ASSERT_EQ(5u, match.size()) << line;
236 std::string namespace_name = match[1];
237 std::string property_desc = match[2];
238 bool is_additional_property = match[3] == "+=";
239 std::string content = match[4];
240 ParseNamespaceCommand(namespace_name,
241 property_desc,
242 is_additional_property,
243 content,
244 *current_section,
245 line);
246 }
247 }
248 }
249 }