blob: 66fa011b0ceb0ba1006d8ec48d709a9e4738102a [file] [log] [blame]
Tom Cherry16fad422017-08-04 15:59:03 -07001/*
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
27#include <android-base/file.h>
28#include <android-base/logging.h>
29#include <android-base/strings.h>
30#include <android-base/unique_fd.h>
31
32#include "util.h"
33
34using android::base::ReadFdToString;
35using android::base::StartsWith;
36using android::base::WriteStringToFd;
37using android::base::unique_fd;
38
39namespace android {
40namespace init {
41
42std::string persistent_property_filename = "/data/property/persistent_properties";
43
44namespace {
45
46constexpr const uint32_t kMagic = 0x8495E0B4;
47constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
48
49Result<std::vector<std::pair<std::string, std::string>>> LoadLegacyPersistentProperties() {
50 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
51 if (!dir) {
52 return ErrnoError() << "Unable to open persistent property directory \""
53 << kLegacyPersistentPropertyDir << "\"";
54 }
55
56 std::vector<std::pair<std::string, std::string>> persistent_properties;
57 dirent* entry;
58 while ((entry = readdir(dir.get())) != nullptr) {
59 if (!StartsWith(entry->d_name, "persist.")) {
60 continue;
61 }
62 if (entry->d_type != DT_REG) {
63 continue;
64 }
65
66 unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
67 if (fd == -1) {
68 PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
69 continue;
70 }
71
72 struct stat sb;
73 if (fstat(fd, &sb) == -1) {
74 PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
75 continue;
76 }
77
78 // File must not be accessible to others, be owned by root/root, and
79 // not be a hard link to any other file.
80 if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
81 sb.st_nlink != 1) {
82 PLOG(ERROR) << "skipping insecure property file " << entry->d_name
83 << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
84 << " mode=" << std::oct << sb.st_mode << ")";
85 continue;
86 }
87
88 std::string value;
89 if (ReadFdToString(fd, &value)) {
90 persistent_properties.emplace_back(entry->d_name, value);
91 } else {
92 PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
93 }
94 }
95 return persistent_properties;
96}
97
98void RemoveLegacyPersistentPropertyFiles() {
99 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
100 if (!dir) {
101 PLOG(ERROR) << "Unable to open persistent property directory \""
102 << kLegacyPersistentPropertyDir << "\"";
103 return;
104 }
105
106 dirent* entry;
107 while ((entry = readdir(dir.get())) != nullptr) {
108 if (!StartsWith(entry->d_name, "persist.")) {
109 continue;
110 }
111 if (entry->d_type != DT_REG) {
112 continue;
113 }
114 unlinkat(dirfd(dir.get()), entry->d_name, 0);
115 }
116}
117
118std::vector<std::pair<std::string, std::string>> LoadPersistentPropertiesFromMemory() {
119 std::vector<std::pair<std::string, std::string>> properties;
120 __system_property_foreach(
121 [](const prop_info* pi, void* cookie) {
122 __system_property_read_callback(
123 pi,
124 [](void* cookie, const char* name, const char* value, unsigned serial) {
125 if (StartsWith(name, "persist.")) {
126 auto properties =
127 reinterpret_cast<std::vector<std::pair<std::string, std::string>>*>(
128 cookie);
129 properties->emplace_back(name, value);
130 }
131 },
132 cookie);
133 },
134 &properties);
135 return properties;
136}
137
138class PersistentPropertyFileParser {
139 public:
140 PersistentPropertyFileParser(const std::string& contents) : contents_(contents), position_(0) {}
141 Result<std::vector<std::pair<std::string, std::string>>> Parse();
142
143 private:
144 Result<std::string> ReadString();
145 Result<uint32_t> ReadUint32();
146
147 const std::string& contents_;
148 size_t position_;
149};
150
151Result<std::vector<std::pair<std::string, std::string>>> PersistentPropertyFileParser::Parse() {
152 std::vector<std::pair<std::string, std::string>> result;
153
154 if (auto magic = ReadUint32(); magic) {
155 if (*magic != kMagic) {
156 return Error() << "Magic value '0x" << std::hex << *magic
157 << "' does not match expected value '0x" << kMagic << "'";
158 }
159 } else {
160 return Error() << "Could not read magic value: " << magic.error();
161 }
162
163 if (auto version = ReadUint32(); version) {
164 if (*version != 1) {
165 return Error() << "Version '" << *version
166 << "' does not match any compatible version: (1)";
167 }
168 } else {
169 return Error() << "Could not read version: " << version.error();
170 }
171
172 auto num_properties = ReadUint32();
173 if (!num_properties) {
174 return Error() << "Could not read num_properties: " << num_properties.error();
175 }
176
177 while (position_ < contents_.size()) {
178 auto key = ReadString();
179 if (!key) {
180 return Error() << "Could not read key: " << key.error();
181 }
182 if (!StartsWith(*key, "persist.")) {
183 return Error() << "Property '" << *key << "' does not starts with 'persist.'";
184 }
185 auto value = ReadString();
186 if (!value) {
187 return Error() << "Could not read value: " << value.error();
188 }
189 result.emplace_back(*key, *value);
190 }
191
192 if (result.size() != *num_properties) {
193 return Error() << "Mismatch of number of persistent properties read, " << result.size()
194 << " and number of persistent properties expected, " << *num_properties;
195 }
196
197 return result;
198}
199
200Result<std::string> PersistentPropertyFileParser::ReadString() {
201 auto string_length = ReadUint32();
202 if (!string_length) {
203 return Error() << "Could not read size for string";
204 }
205
206 if (position_ + *string_length > contents_.size()) {
207 return Error() << "String size would cause it to overflow the input buffer";
208 }
209 auto result = std::string(contents_, position_, *string_length);
210 position_ += *string_length;
211 return result;
212}
213
214Result<uint32_t> PersistentPropertyFileParser::ReadUint32() {
215 if (position_ + 3 > contents_.size()) {
216 return Error() << "Input buffer not large enough to read uint32_t";
217 }
218 uint32_t result = *reinterpret_cast<const uint32_t*>(&contents_[position_]);
219 position_ += sizeof(uint32_t);
220 return result;
221}
222
223} // namespace
224
225Result<std::vector<std::pair<std::string, std::string>>> LoadPersistentPropertyFile() {
226 const std::string temp_filename = persistent_property_filename + ".tmp";
227 if (access(temp_filename.c_str(), F_OK) == 0) {
228 LOG(INFO)
229 << "Found temporary property file while attempting to persistent system properties"
230 " a previous persistent property write may have failed";
231 unlink(temp_filename.c_str());
232 }
233 auto file_contents = ReadFile(persistent_property_filename);
234 if (!file_contents) {
235 return Error() << "Unable to read persistent property file: " << file_contents.error();
236 }
237 auto parsed_contents = PersistentPropertyFileParser(*file_contents).Parse();
238 if (!parsed_contents) {
239 // If the file cannot be parsed, then we don't have any recovery mechanisms, so we delete
240 // it to allow for future writes to take place successfully.
241 unlink(persistent_property_filename.c_str());
242 return Error() << "Unable to parse persistent property file: " << parsed_contents.error();
243 }
244 return parsed_contents;
245}
246
247std::string GenerateFileContents(
248 const std::vector<std::pair<std::string, std::string>>& persistent_properties) {
249 std::string result;
250
251 uint32_t magic = kMagic;
252 result.append(reinterpret_cast<char*>(&magic), sizeof(uint32_t));
253
254 uint32_t version = 1;
255 result.append(reinterpret_cast<char*>(&version), sizeof(uint32_t));
256
257 uint32_t num_properties = persistent_properties.size();
258 result.append(reinterpret_cast<char*>(&num_properties), sizeof(uint32_t));
259
260 for (const auto& [key, value] : persistent_properties) {
261 uint32_t key_length = key.length();
262 result.append(reinterpret_cast<char*>(&key_length), sizeof(uint32_t));
263 result.append(key);
264 uint32_t value_length = value.length();
265 result.append(reinterpret_cast<char*>(&value_length), sizeof(uint32_t));
266 result.append(value);
267 }
268 return result;
269}
270
271Result<Success> WritePersistentPropertyFile(
272 const std::vector<std::pair<std::string, std::string>>& persistent_properties) {
273 auto file_contents = GenerateFileContents(persistent_properties);
274
275 const std::string temp_filename = persistent_property_filename + ".tmp";
276 unique_fd fd(TEMP_FAILURE_RETRY(
277 open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
278 if (fd == -1) {
279 return ErrnoError() << "Could not open temporary properties file";
280 }
281 if (!WriteStringToFd(file_contents, fd)) {
282 return ErrnoError() << "Unable to write file contents";
283 }
284 fsync(fd);
285 fd.reset();
286
287 if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
288 int saved_errno = errno;
289 unlink(temp_filename.c_str());
290 return Error(saved_errno) << "Unable to rename persistent property file";
291 }
292 return Success();
293}
294
295// Persistent properties are not written often, so we rather not keep any data in memory and read
296// then rewrite the persistent property file for each update.
297void WritePersistentProperty(const std::string& name, const std::string& value) {
298 auto persistent_properties = LoadPersistentPropertyFile();
299 if (!persistent_properties) {
300 LOG(ERROR) << "Recovering persistent properties from memory: "
301 << persistent_properties.error();
302 persistent_properties = LoadPersistentPropertiesFromMemory();
303 }
304 auto it = std::find_if(persistent_properties->begin(), persistent_properties->end(),
305 [&name](const auto& entry) { return entry.first == name; });
306 if (it != persistent_properties->end()) {
307 *it = {name, value};
308 } else {
309 persistent_properties->emplace_back(name, value);
310 }
311
312 if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
313 LOG(ERROR) << "Could not store persistent property: " << result.error();
314 }
315}
316
317std::vector<std::pair<std::string, std::string>> LoadPersistentProperties() {
318 auto persistent_properties = LoadPersistentPropertyFile();
319
320 if (!persistent_properties) {
321 LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
322 << persistent_properties.error();
323 persistent_properties = LoadLegacyPersistentProperties();
324 if (!persistent_properties) {
325 LOG(ERROR) << "Unable to load legacy persistent properties: "
326 << persistent_properties.error();
327 return {};
328 }
329 if (auto result = WritePersistentPropertyFile(*persistent_properties); result) {
330 RemoveLegacyPersistentPropertyFiles();
331 } else {
332 LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
333 // Fall through so that we still set the properties that we've read.
334 }
335 }
336
337 return *persistent_properties;
338}
339
340} // namespace init
341} // namespace android