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 <dirent.h>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <sys/system_properties.h>
23 #include <sys/types.h>
24
25 #include <memory>
26 #include <unordered_map>
27
28 #include <android-base/file.h>
29 #include <android-base/logging.h>
30 #include <android-base/strings.h>
31 #include <android-base/unique_fd.h>
32
33 #include "util.h"
34
35 using android::base::Dirname;
36 using android::base::ReadFdToString;
37 using android::base::StartsWith;
38 using android::base::unique_fd;
39 using android::base::WriteStringToFd;
40
41 namespace android {
42 namespace init {
43
44 std::string persistent_property_filename = "/data/property/persistent_properties";
45
46 namespace {
47
48 constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
49
AddPersistentProperty(const std::string & name,const std::string & value,PersistentProperties * persistent_properties)50 void AddPersistentProperty(const std::string& name, const std::string& value,
51 PersistentProperties* persistent_properties) {
52 auto persistent_property_record = persistent_properties->add_properties();
53 persistent_property_record->set_name(name);
54 persistent_property_record->set_value(value);
55 }
56
LoadLegacyPersistentProperties()57 Result<PersistentProperties> LoadLegacyPersistentProperties() {
58 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
59 if (!dir) {
60 return ErrnoError() << "Unable to open persistent property directory \""
61 << kLegacyPersistentPropertyDir << "\"";
62 }
63
64 PersistentProperties persistent_properties;
65 dirent* entry;
66 while ((entry = readdir(dir.get())) != nullptr) {
67 if (!StartsWith(entry->d_name, "persist.")) {
68 continue;
69 }
70 if (entry->d_type != DT_REG) {
71 continue;
72 }
73
74 unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
75 if (fd == -1) {
76 PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
77 continue;
78 }
79
80 struct stat sb;
81 if (fstat(fd.get(), &sb) == -1) {
82 PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
83 continue;
84 }
85
86 // File must not be accessible to others, be owned by root/root, and
87 // not be a hard link to any other file.
88 if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
89 sb.st_nlink != 1) {
90 PLOG(ERROR) << "skipping insecure property file " << entry->d_name
91 << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
92 << " mode=" << std::oct << sb.st_mode << ")";
93 continue;
94 }
95
96 std::string value;
97 if (ReadFdToString(fd, &value)) {
98 AddPersistentProperty(entry->d_name, value, &persistent_properties);
99 } else {
100 PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
101 }
102 }
103 return persistent_properties;
104 }
105
RemoveLegacyPersistentPropertyFiles()106 void RemoveLegacyPersistentPropertyFiles() {
107 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
108 if (!dir) {
109 PLOG(ERROR) << "Unable to open persistent property directory \""
110 << kLegacyPersistentPropertyDir << "\"";
111 return;
112 }
113
114 dirent* entry;
115 while ((entry = readdir(dir.get())) != nullptr) {
116 if (!StartsWith(entry->d_name, "persist.")) {
117 continue;
118 }
119 if (entry->d_type != DT_REG) {
120 continue;
121 }
122 unlinkat(dirfd(dir.get()), entry->d_name, 0);
123 }
124 }
125
ReadPersistentPropertyFile()126 Result<std::string> ReadPersistentPropertyFile() {
127 const std::string temp_filename = persistent_property_filename + ".tmp";
128 if (access(temp_filename.c_str(), F_OK) == 0) {
129 LOG(INFO)
130 << "Found temporary property file while attempting to persistent system properties"
131 " a previous persistent property write may have failed";
132 unlink(temp_filename.c_str());
133 }
134 auto file_contents = ReadFile(persistent_property_filename);
135 if (!file_contents.ok()) {
136 return Error() << "Unable to read persistent property file: " << file_contents.error();
137 }
138 return *file_contents;
139 }
140
ParsePersistentPropertyFile(const std::string & file_contents)141 Result<PersistentProperties> ParsePersistentPropertyFile(const std::string& file_contents) {
142 PersistentProperties persistent_properties;
143 if (!persistent_properties.ParseFromString(file_contents)) {
144 return Error() << "Unable to parse persistent property file: Could not parse protobuf";
145 }
146 for (auto& prop : persistent_properties.properties()) {
147 if (!StartsWith(prop.name(), "persist.") && !StartsWith(prop.name(), "next_boot.")) {
148 return Error() << "Unable to load persistent property file: property '" << prop.name()
149 << "' doesn't start with 'persist.' or 'next_boot.'";
150 }
151 }
152 return persistent_properties;
153 }
154
155 } // namespace
156
LoadPersistentPropertyFile()157 Result<PersistentProperties> LoadPersistentPropertyFile() {
158 auto file_contents = ReadPersistentPropertyFile();
159 if (!file_contents.ok()) return file_contents.error();
160
161 auto persistent_properties = ParsePersistentPropertyFile(*file_contents);
162 if (!persistent_properties.ok()) {
163 // If the file cannot be parsed in either format, then we don't have any recovery
164 // mechanisms, so we delete it to allow for future writes to take place successfully.
165 unlink(persistent_property_filename.c_str());
166 }
167 return persistent_properties;
168 }
169
WritePersistentPropertyFile(const PersistentProperties & persistent_properties)170 Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
171 const std::string temp_filename = persistent_property_filename + ".tmp";
172 unique_fd fd(TEMP_FAILURE_RETRY(
173 open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
174 if (fd == -1) {
175 return ErrnoError() << "Could not open temporary properties file";
176 }
177 std::string serialized_string;
178 if (!persistent_properties.SerializeToString(&serialized_string)) {
179 return Error() << "Unable to serialize properties";
180 }
181 if (!WriteStringToFd(serialized_string, fd)) {
182 return ErrnoError() << "Unable to write file contents";
183 }
184 fsync(fd.get());
185 fd.reset();
186
187 if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
188 int saved_errno = errno;
189 unlink(temp_filename.c_str());
190 return Error(saved_errno) << "Unable to rename persistent property file";
191 }
192
193 // rename() is atomic with regards to the kernel's filesystem buffers, but the parent
194 // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage.
195 // Note in this case, that the source and destination directories are the same, so only one
196 // fsync() is required.
197 auto dir = Dirname(persistent_property_filename);
198 auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};
199 if (dir_fd < 0) {
200 return ErrnoError() << "Unable to open persistent properties directory for fsync()";
201 }
202 fsync(dir_fd.get());
203
204 return {};
205 }
206
LoadPersistentPropertiesFromMemory()207 PersistentProperties LoadPersistentPropertiesFromMemory() {
208 PersistentProperties persistent_properties;
209 __system_property_foreach(
210 [](const prop_info* pi, void* cookie) {
211 __system_property_read_callback(
212 pi,
213 [](void* cookie, const char* name, const char* value, unsigned serial) {
214 if (StartsWith(name, "persist.")) {
215 auto properties = reinterpret_cast<PersistentProperties*>(cookie);
216 AddPersistentProperty(name, value, properties);
217 }
218 },
219 cookie);
220 },
221 &persistent_properties);
222 return persistent_properties;
223 }
224
225 // Persistent properties are not written often, so we rather not keep any data in memory and read
226 // then rewrite the persistent property file for each update.
WritePersistentProperty(const std::string & name,const std::string & value)227 void WritePersistentProperty(const std::string& name, const std::string& value) {
228 auto persistent_properties = LoadPersistentPropertyFile();
229
230 if (!persistent_properties.ok()) {
231 LOG(ERROR) << "Recovering persistent properties from memory: "
232 << persistent_properties.error();
233 persistent_properties = LoadPersistentPropertiesFromMemory();
234 }
235 auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
236 persistent_properties->mutable_properties()->end(),
237 [&name](const auto& record) { return record.name() == name; });
238 if (it != persistent_properties->mutable_properties()->end()) {
239 if (it->value() == value) {
240 return;
241 }
242 it->set_name(name);
243 it->set_value(value);
244 } else {
245 AddPersistentProperty(name, value, &persistent_properties.value());
246 }
247
248 if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) {
249 LOG(ERROR) << "Could not store persistent property: " << result.error();
250 }
251 }
252
LoadPersistentProperties()253 PersistentProperties LoadPersistentProperties() {
254 auto persistent_properties = LoadPersistentPropertyFile();
255
256 if (!persistent_properties.ok()) {
257 LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
258 << persistent_properties.error();
259 persistent_properties = LoadLegacyPersistentProperties();
260 if (!persistent_properties.ok()) {
261 LOG(ERROR) << "Unable to load legacy persistent properties: "
262 << persistent_properties.error();
263 return {};
264 }
265 if (auto result = WritePersistentPropertyFile(*persistent_properties); result.ok()) {
266 RemoveLegacyPersistentPropertyFiles();
267 } else {
268 LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
269 // Fall through so that we still set the properties that we've read.
270 }
271 }
272
273 // loop over to find all staged props
274 auto const staged_prefix = std::string_view("next_boot.");
275 auto staged_props = std::unordered_map<std::string, std::string>();
276 for (const auto& property_record : persistent_properties->properties()) {
277 auto const& prop_name = property_record.name();
278 auto const& prop_value = property_record.value();
279 if (StartsWith(prop_name, staged_prefix)) {
280 auto actual_prop_name = prop_name.substr(staged_prefix.size());
281 staged_props[actual_prop_name] = prop_value;
282 }
283 }
284
285 if (staged_props.empty()) {
286 return *persistent_properties;
287 }
288
289 // if has staging, apply staging and perserve the original prop order
290 PersistentProperties updated_persistent_properties;
291 for (const auto& property_record : persistent_properties->properties()) {
292 auto const& prop_name = property_record.name();
293 auto const& prop_value = property_record.value();
294
295 // don't include staged props anymore
296 if (StartsWith(prop_name, staged_prefix)) {
297 continue;
298 }
299
300 auto iter = staged_props.find(prop_name);
301 if (iter != staged_props.end()) {
302 AddPersistentProperty(prop_name, iter->second, &updated_persistent_properties);
303 staged_props.erase(iter);
304 } else {
305 AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
306 }
307 }
308
309 // add any additional staged props
310 for (auto const& [prop_name, prop_value] : staged_props) {
311 AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
312 }
313
314 // write current updated persist prop file
315 auto result = WritePersistentPropertyFile(updated_persistent_properties);
316 if (!result.ok()) {
317 LOG(ERROR) << "Could not store persistent property: " << result.error();
318 }
319
320 return updated_persistent_properties;
321 }
322
323
324
325 } // namespace init
326 } // namespace android
327