blob: 1d17e3c106d2b8f50894d347172fb0a4fbb6bd79 [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>
Krzysztof KosiƄskieaf7d772024-03-15 06:43:54 +000026#include <unordered_map>
Tom Cherry16fad422017-08-04 15:59:03 -070027
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
Tom Cherry97437a72019-12-06 11:16:10 -080035using android::base::Dirname;
Tom Cherry16fad422017-08-04 15:59:03 -070036using android::base::ReadFdToString;
37using android::base::StartsWith;
Tom Cherry16fad422017-08-04 15:59:03 -070038using android::base::unique_fd;
Tom Cherry97437a72019-12-06 11:16:10 -080039using android::base::WriteStringToFd;
Tom Cherry16fad422017-08-04 15:59:03 -070040
41namespace android {
42namespace init {
43
44std::string persistent_property_filename = "/data/property/persistent_properties";
45
46namespace {
47
Tom Cherry16fad422017-08-04 15:59:03 -070048constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
49
Dennis Shen79283ef2023-10-24 17:30:12 +000050void 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
Tom Cherrya97faba2017-09-15 15:44:04 -070057Result<PersistentProperties> LoadLegacyPersistentProperties() {
Tom Cherry16fad422017-08-04 15:59:03 -070058 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
Tom Cherrya97faba2017-09-15 15:44:04 -070064 PersistentProperties persistent_properties;
Tom Cherry16fad422017-08-04 15:59:03 -070065 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
Tom Cherry247ffbf2019-07-08 15:09:36 -070074 unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Tom Cherry16fad422017-08-04 15:59:03 -070075 if (fd == -1) {
76 PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
77 continue;
78 }
79
80 struct stat sb;
Bart Van Asscheaee2ec82022-12-02 18:48:15 -080081 if (fstat(fd.get(), &sb) == -1) {
Tom Cherry16fad422017-08-04 15:59:03 -070082 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)) {
Tom Cherrya97faba2017-09-15 15:44:04 -070098 AddPersistentProperty(entry->d_name, value, &persistent_properties);
Tom Cherry16fad422017-08-04 15:59:03 -070099 } else {
100 PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
101 }
102 }
103 return persistent_properties;
104}
105
106void 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
Tom Cherrya97faba2017-09-15 15:44:04 -0700126Result<std::string> ReadPersistentPropertyFile() {
Tom Cherry16fad422017-08-04 15:59:03 -0700127 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);
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900135 if (!file_contents.ok()) {
Tom Cherry16fad422017-08-04 15:59:03 -0700136 return Error() << "Unable to read persistent property file: " << file_contents.error();
137 }
Tom Cherrya97faba2017-09-15 15:44:04 -0700138 return *file_contents;
139}
140
Paul Crowleyf7c74692022-08-25 11:38:27 -0700141Result<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()) {
Dennis Shen678d2682023-09-18 19:52:52 +0000147 if (!StartsWith(prop.name(), "persist.") && !StartsWith(prop.name(), "next_boot.")) {
Paul Crowleyf7c74692022-08-25 11:38:27 -0700148 return Error() << "Unable to load persistent property file: property '" << prop.name()
Dennis Shen678d2682023-09-18 19:52:52 +0000149 << "' doesn't start with 'persist.' or 'next_boot.'";
Paul Crowleyf7c74692022-08-25 11:38:27 -0700150 }
151 }
152 return persistent_properties;
153}
154
Tom Cherrya97faba2017-09-15 15:44:04 -0700155} // namespace
156
157Result<PersistentProperties> LoadPersistentPropertyFile() {
158 auto file_contents = ReadPersistentPropertyFile();
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900159 if (!file_contents.ok()) return file_contents.error();
Tom Cherrya97faba2017-09-15 15:44:04 -0700160
Paul Crowleyf7c74692022-08-25 11:38:27 -0700161 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;
Tom Cherry16fad422017-08-04 15:59:03 -0700168}
169
Tom Cherrybbcbc2f2019-06-10 11:08:01 -0700170Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
Tom Cherry16fad422017-08-04 15:59:03 -0700171 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 }
Tom Cherrya97faba2017-09-15 15:44:04 -0700177 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)) {
Tom Cherry16fad422017-08-04 15:59:03 -0700182 return ErrnoError() << "Unable to write file contents";
183 }
Bart Van Asscheaee2ec82022-12-02 18:48:15 -0800184 fsync(fd.get());
Tom Cherry16fad422017-08-04 15:59:03 -0700185 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 }
Tom Cherry97437a72019-12-06 11:16:10 -0800192
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);
Tom Cherryc99d60c2019-12-09 06:48:37 -0800198 auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};
Tom Cherry97437a72019-12-06 11:16:10 -0800199 if (dir_fd < 0) {
200 return ErrnoError() << "Unable to open persistent properties directory for fsync()";
201 }
Bart Van Asscheaee2ec82022-12-02 18:48:15 -0800202 fsync(dir_fd.get());
Tom Cherry97437a72019-12-06 11:16:10 -0800203
Tom Cherrybbcbc2f2019-06-10 11:08:01 -0700204 return {};
Tom Cherry16fad422017-08-04 15:59:03 -0700205}
206
Jiyong Parkc7230a12023-10-20 20:42:56 +0900207PersistentProperties 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
Tom Cherry16fad422017-08-04 15:59:03 -0700225// 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.
227void WritePersistentProperty(const std::string& name, const std::string& value) {
Tom Cherry9614e4d2017-09-19 10:31:27 -0700228 auto persistent_properties = LoadPersistentPropertyFile();
Tom Cherrya97faba2017-09-15 15:44:04 -0700229
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900230 if (!persistent_properties.ok()) {
Tom Cherry16fad422017-08-04 15:59:03 -0700231 LOG(ERROR) << "Recovering persistent properties from memory: "
Tom Cherry9614e4d2017-09-19 10:31:27 -0700232 << persistent_properties.error();
Tom Cherry16fad422017-08-04 15:59:03 -0700233 persistent_properties = LoadPersistentPropertiesFromMemory();
234 }
Tom Cherry9614e4d2017-09-19 10:31:27 -0700235 auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
236 persistent_properties->mutable_properties()->end(),
Tom Cherrya97faba2017-09-15 15:44:04 -0700237 [&name](const auto& record) { return record.name() == name; });
Tom Cherry9614e4d2017-09-19 10:31:27 -0700238 if (it != persistent_properties->mutable_properties()->end()) {
Jiyong Park2f8efe02024-04-04 22:14:27 +0900239 if (it->value() == value) {
240 return;
241 }
Tom Cherrya97faba2017-09-15 15:44:04 -0700242 it->set_name(name);
243 it->set_value(value);
Tom Cherry16fad422017-08-04 15:59:03 -0700244 } else {
Tom Cherry9614e4d2017-09-19 10:31:27 -0700245 AddPersistentProperty(name, value, &persistent_properties.value());
Tom Cherry16fad422017-08-04 15:59:03 -0700246 }
247
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900248 if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) {
Tom Cherry16fad422017-08-04 15:59:03 -0700249 LOG(ERROR) << "Could not store persistent property: " << result.error();
250 }
251}
252
Tom Cherrya97faba2017-09-15 15:44:04 -0700253PersistentProperties LoadPersistentProperties() {
Tom Cherry16fad422017-08-04 15:59:03 -0700254 auto persistent_properties = LoadPersistentPropertyFile();
255
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900256 if (!persistent_properties.ok()) {
Tom Cherry16fad422017-08-04 15:59:03 -0700257 LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
258 << persistent_properties.error();
259 persistent_properties = LoadLegacyPersistentProperties();
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900260 if (!persistent_properties.ok()) {
Tom Cherry16fad422017-08-04 15:59:03 -0700261 LOG(ERROR) << "Unable to load legacy persistent properties: "
262 << persistent_properties.error();
263 return {};
264 }
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900265 if (auto result = WritePersistentPropertyFile(*persistent_properties); result.ok()) {
Tom Cherry16fad422017-08-04 15:59:03 -0700266 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
Dennis Shen79283ef2023-10-24 17:30:12 +0000273 // 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;
Tom Cherry16fad422017-08-04 15:59:03 -0700321}
322
Dennis Shen79283ef2023-10-24 17:30:12 +0000323
324
Tom Cherry16fad422017-08-04 15:59:03 -0700325} // namespace init
326} // namespace android