1 /*
2 * Copyright (C) 2017 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 "persistent_properties.h"
18
19 #include <errno.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <vector>
25
26 #include <android-base/file.h>
27 #include <gtest/gtest.h>
28
29 #include "util.h"
30
31 using namespace std::string_literals;
32
33 namespace android {
34 namespace init {
35
VectorToPersistentProperties(const std::vector<std::pair<std::string,std::string>> & input_properties)36 PersistentProperties VectorToPersistentProperties(
37 const std::vector<std::pair<std::string, std::string>>& input_properties) {
38 PersistentProperties persistent_properties;
39
40 for (const auto& [name, value] : input_properties) {
41 auto persistent_property_record = persistent_properties.add_properties();
42 persistent_property_record->set_name(name);
43 persistent_property_record->set_value(value);
44 }
45
46 return persistent_properties;
47 }
48
CheckPropertiesEqual(std::vector<std::pair<std::string,std::string>> expected,const PersistentProperties & persistent_properties)49 void CheckPropertiesEqual(std::vector<std::pair<std::string, std::string>> expected,
50 const PersistentProperties& persistent_properties) {
51 for (const auto& persistent_property_record : persistent_properties.properties()) {
52 auto it = std::find_if(expected.begin(), expected.end(),
53 [persistent_property_record](const auto& entry) {
54 return entry.first == persistent_property_record.name() &&
55 entry.second == persistent_property_record.value();
56 });
57 ASSERT_TRUE(it != expected.end())
58 << "Found unexpected property (" << persistent_property_record.name() << ", "
59 << persistent_property_record.value() << ")";
60 expected.erase(it);
61 }
62 auto joiner = [](const std::vector<std::pair<std::string, std::string>>& vector) {
63 std::string result;
64 for (const auto& [name, value] : vector) {
65 result += " (" + name + ", " + value + ")";
66 }
67 return result;
68 };
69 EXPECT_TRUE(expected.empty()) << "Did not find expected properties:" << joiner(expected);
70 }
71
TEST(persistent_properties,EndToEnd)72 TEST(persistent_properties, EndToEnd) {
73 TemporaryFile tf;
74 ASSERT_TRUE(tf.fd != -1);
75 persistent_property_filename = tf.path;
76
77 std::vector<std::pair<std::string, std::string>> persistent_properties = {
78 {"persist.sys.locale", "en-US"},
79 {"persist.sys.timezone", "America/Los_Angeles"},
80 {"persist.test.empty.value", ""},
81 {"persist.test.new.line", "abc\n\n\nabc"},
82 {"persist.test.numbers", "1234567890"},
83 {"persist.test.non.ascii", "\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F"},
84 // We don't currently allow for non-ascii names for system properties, but this is a policy
85 // decision, not a technical limitation.
86 {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-name"},
87 };
88
89 ASSERT_RESULT_OK(
90 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
91
92 auto read_back_properties = LoadPersistentProperties();
93 CheckPropertiesEqual(persistent_properties, read_back_properties);
94 }
95
TEST(persistent_properties,AddProperty)96 TEST(persistent_properties, AddProperty) {
97 TemporaryFile tf;
98 ASSERT_TRUE(tf.fd != -1);
99 persistent_property_filename = tf.path;
100
101 std::vector<std::pair<std::string, std::string>> persistent_properties = {
102 {"persist.sys.timezone", "America/Los_Angeles"},
103 };
104 ASSERT_RESULT_OK(
105 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
106
107 WritePersistentProperty("persist.sys.locale", "pt-BR");
108
109 std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
110 {"persist.sys.timezone", "America/Los_Angeles"},
111 {"persist.sys.locale", "pt-BR"},
112 };
113
114 auto read_back_properties = LoadPersistentProperties();
115 CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
116 }
117
TEST(persistent_properties,UpdateProperty)118 TEST(persistent_properties, UpdateProperty) {
119 TemporaryFile tf;
120 ASSERT_TRUE(tf.fd != -1);
121 persistent_property_filename = tf.path;
122
123 std::vector<std::pair<std::string, std::string>> persistent_properties = {
124 {"persist.sys.locale", "en-US"},
125 {"persist.sys.timezone", "America/Los_Angeles"},
126 };
127 ASSERT_RESULT_OK(
128 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
129
130 WritePersistentProperty("persist.sys.locale", "pt-BR");
131
132 std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
133 {"persist.sys.locale", "pt-BR"},
134 {"persist.sys.timezone", "America/Los_Angeles"},
135 };
136
137 auto read_back_properties = LoadPersistentProperties();
138 CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
139 }
140
TEST(persistent_properties,UpdatePropertyBadParse)141 TEST(persistent_properties, UpdatePropertyBadParse) {
142 TemporaryFile tf;
143 ASSERT_TRUE(tf.fd != -1);
144 persistent_property_filename = tf.path;
145
146 ASSERT_RESULT_OK(WriteFile(tf.path, "ab"));
147
148 WritePersistentProperty("persist.sys.locale", "pt-BR");
149
150 auto read_back_properties = LoadPersistentProperties();
151 EXPECT_GT(read_back_properties.properties().size(), 0);
152
153 auto it =
154 std::find_if(read_back_properties.properties().begin(),
155 read_back_properties.properties().end(), [](const auto& entry) {
156 return entry.name() == "persist.sys.locale" && entry.value() == "pt-BR";
157 });
158 EXPECT_FALSE(it == read_back_properties.properties().end());
159 }
160
TEST(persistent_properties,NopUpdateDoesntWriteFile)161 TEST(persistent_properties, NopUpdateDoesntWriteFile) {
162 TemporaryFile tf;
163 ASSERT_TRUE(tf.fd != -1);
164 persistent_property_filename = tf.path;
165
166 auto last_modified = [&tf]() -> time_t {
167 struct stat buf;
168 EXPECT_EQ(fstat(tf.fd, &buf), 0);
169 return buf.st_mtime;
170 };
171
172 std::vector<std::pair<std::string, std::string>> persistent_properties = {
173 {"persist.sys.locale", "en-US"},
174 {"persist.sys.timezone", "America/Los_Angeles"},
175 };
176 ASSERT_RESULT_OK(
177 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
178
179 time_t t = last_modified();
180 sleep(2);
181 WritePersistentProperty("persist.sys.locale", "en-US");
182 // Ensure that the file was not modified
183 ASSERT_EQ(last_modified(), t);
184 }
185
TEST(persistent_properties,RejectNonPersistProperty)186 TEST(persistent_properties, RejectNonPersistProperty) {
187 TemporaryFile tf;
188 ASSERT_TRUE(tf.fd != -1);
189 persistent_property_filename = tf.path;
190
191 WritePersistentProperty("notpersist.sys.locale", "pt-BR");
192
193 auto read_back_properties = LoadPersistentProperties();
194 EXPECT_EQ(read_back_properties.properties().size(), 0);
195
196 WritePersistentProperty("persist.sys.locale", "pt-BR");
197
198 read_back_properties = LoadPersistentProperties();
199 EXPECT_GT(read_back_properties.properties().size(), 0);
200
201 auto it = std::find_if(read_back_properties.properties().begin(),
202 read_back_properties.properties().end(), [](const auto& entry) {
203 return entry.name() == "persist.sys.locale" &&
204 entry.value() == "pt-BR";
205 });
206 EXPECT_FALSE(it == read_back_properties.properties().end());
207 }
208
TEST(persistent_properties,StagedPersistProperty)209 TEST(persistent_properties, StagedPersistProperty) {
210 TemporaryFile tf;
211 ASSERT_TRUE(tf.fd != -1);
212 persistent_property_filename = tf.path;
213
214 std::vector<std::pair<std::string, std::string>> persistent_properties = {
215 {"persist.sys.locale", "en-US"},
216 {"next_boot.persist.test.numbers", "54321"},
217 {"persist.sys.timezone", "America/Los_Angeles"},
218 {"persist.test.numbers", "12345"},
219 {"next_boot.persist.test.extra", "abc"},
220 };
221
222 ASSERT_RESULT_OK(
223 WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
224
225 std::vector<std::pair<std::string, std::string>> expected_persistent_properties = {
226 {"persist.sys.locale", "en-US"},
227 {"persist.sys.timezone", "America/Los_Angeles"},
228 {"persist.test.numbers", "54321"},
229 {"persist.test.extra", "abc"},
230 };
231
232 // lock down that staged props are applied
233 auto first_read_back_properties = LoadPersistentProperties();
234 CheckPropertiesEqual(expected_persistent_properties, first_read_back_properties);
235
236 // lock down that other props are not overwritten
237 auto second_read_back_properties = LoadPersistentProperties();
238 CheckPropertiesEqual(expected_persistent_properties, second_read_back_properties);
239 }
240
241 } // namespace init
242 } // namespace android
243