blob: 21adce914ba33536bc1c74a52312efbc44301c27 [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
Tom Cherry16fad422017-08-04 15:59:03 -070046constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
47
Tom Cherrya97faba2017-09-15 15:44:04 -070048void AddPersistentProperty(const std::string& name, const std::string& value,
49 PersistentProperties* persistent_properties) {
50 auto persistent_property_record = persistent_properties->add_properties();
51 persistent_property_record->set_name(name);
52 persistent_property_record->set_value(value);
53}
54
55Result<PersistentProperties> LoadLegacyPersistentProperties() {
Tom Cherry16fad422017-08-04 15:59:03 -070056 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
57 if (!dir) {
58 return ErrnoError() << "Unable to open persistent property directory \""
59 << kLegacyPersistentPropertyDir << "\"";
60 }
61
Tom Cherrya97faba2017-09-15 15:44:04 -070062 PersistentProperties persistent_properties;
Tom Cherry16fad422017-08-04 15:59:03 -070063 dirent* entry;
64 while ((entry = readdir(dir.get())) != nullptr) {
65 if (!StartsWith(entry->d_name, "persist.")) {
66 continue;
67 }
68 if (entry->d_type != DT_REG) {
69 continue;
70 }
71
72 unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
73 if (fd == -1) {
74 PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
75 continue;
76 }
77
78 struct stat sb;
79 if (fstat(fd, &sb) == -1) {
80 PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
81 continue;
82 }
83
84 // File must not be accessible to others, be owned by root/root, and
85 // not be a hard link to any other file.
86 if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
87 sb.st_nlink != 1) {
88 PLOG(ERROR) << "skipping insecure property file " << entry->d_name
89 << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
90 << " mode=" << std::oct << sb.st_mode << ")";
91 continue;
92 }
93
94 std::string value;
95 if (ReadFdToString(fd, &value)) {
Tom Cherrya97faba2017-09-15 15:44:04 -070096 AddPersistentProperty(entry->d_name, value, &persistent_properties);
Tom Cherry16fad422017-08-04 15:59:03 -070097 } else {
98 PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
99 }
100 }
101 return persistent_properties;
102}
103
104void RemoveLegacyPersistentPropertyFiles() {
105 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
106 if (!dir) {
107 PLOG(ERROR) << "Unable to open persistent property directory \""
108 << kLegacyPersistentPropertyDir << "\"";
109 return;
110 }
111
112 dirent* entry;
113 while ((entry = readdir(dir.get())) != nullptr) {
114 if (!StartsWith(entry->d_name, "persist.")) {
115 continue;
116 }
117 if (entry->d_type != DT_REG) {
118 continue;
119 }
120 unlinkat(dirfd(dir.get()), entry->d_name, 0);
121 }
122}
123
Tom Cherrya97faba2017-09-15 15:44:04 -0700124PersistentProperties LoadPersistentPropertiesFromMemory() {
125 PersistentProperties persistent_properties;
Tom Cherry16fad422017-08-04 15:59:03 -0700126 __system_property_foreach(
127 [](const prop_info* pi, void* cookie) {
128 __system_property_read_callback(
129 pi,
130 [](void* cookie, const char* name, const char* value, unsigned serial) {
131 if (StartsWith(name, "persist.")) {
Tom Cherrya97faba2017-09-15 15:44:04 -0700132 auto properties = reinterpret_cast<PersistentProperties*>(cookie);
133 AddPersistentProperty(name, value, properties);
Tom Cherry16fad422017-08-04 15:59:03 -0700134 }
135 },
136 cookie);
137 },
Tom Cherrya97faba2017-09-15 15:44:04 -0700138 &persistent_properties);
139 return persistent_properties;
Tom Cherry16fad422017-08-04 15:59:03 -0700140}
141
Tom Cherrya97faba2017-09-15 15:44:04 -0700142Result<std::string> ReadPersistentPropertyFile() {
Tom Cherry16fad422017-08-04 15:59:03 -0700143 const std::string temp_filename = persistent_property_filename + ".tmp";
144 if (access(temp_filename.c_str(), F_OK) == 0) {
145 LOG(INFO)
146 << "Found temporary property file while attempting to persistent system properties"
147 " a previous persistent property write may have failed";
148 unlink(temp_filename.c_str());
149 }
150 auto file_contents = ReadFile(persistent_property_filename);
151 if (!file_contents) {
152 return Error() << "Unable to read persistent property file: " << file_contents.error();
153 }
Tom Cherrya97faba2017-09-15 15:44:04 -0700154 return *file_contents;
155}
156
157} // namespace
158
159Result<PersistentProperties> LoadPersistentPropertyFile() {
160 auto file_contents = ReadPersistentPropertyFile();
161 if (!file_contents) return file_contents.error();
162
Tom Cherrya97faba2017-09-15 15:44:04 -0700163 PersistentProperties persistent_properties;
164 if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
165
166 // If the file cannot be parsed in either format, then we don't have any recovery
167 // mechanisms, so we delete it to allow for future writes to take place successfully.
168 unlink(persistent_property_filename.c_str());
Tom Cherry9614e4d2017-09-19 10:31:27 -0700169 return Error() << "Unable to parse persistent property file: Could not parse protobuf";
Tom Cherry16fad422017-08-04 15:59:03 -0700170}
171
Tom Cherrya97faba2017-09-15 15:44:04 -0700172Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
Tom Cherry16fad422017-08-04 15:59:03 -0700173 const std::string temp_filename = persistent_property_filename + ".tmp";
174 unique_fd fd(TEMP_FAILURE_RETRY(
175 open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
176 if (fd == -1) {
177 return ErrnoError() << "Could not open temporary properties file";
178 }
Tom Cherrya97faba2017-09-15 15:44:04 -0700179 std::string serialized_string;
180 if (!persistent_properties.SerializeToString(&serialized_string)) {
181 return Error() << "Unable to serialize properties";
182 }
183 if (!WriteStringToFd(serialized_string, fd)) {
Tom Cherry16fad422017-08-04 15:59:03 -0700184 return ErrnoError() << "Unable to write file contents";
185 }
186 fsync(fd);
187 fd.reset();
188
189 if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
190 int saved_errno = errno;
191 unlink(temp_filename.c_str());
192 return Error(saved_errno) << "Unable to rename persistent property file";
193 }
194 return Success();
195}
196
197// Persistent properties are not written often, so we rather not keep any data in memory and read
198// then rewrite the persistent property file for each update.
199void WritePersistentProperty(const std::string& name, const std::string& value) {
Tom Cherry9614e4d2017-09-19 10:31:27 -0700200 auto persistent_properties = LoadPersistentPropertyFile();
Tom Cherrya97faba2017-09-15 15:44:04 -0700201
Tom Cherry9614e4d2017-09-19 10:31:27 -0700202 if (!persistent_properties) {
Tom Cherry16fad422017-08-04 15:59:03 -0700203 LOG(ERROR) << "Recovering persistent properties from memory: "
Tom Cherry9614e4d2017-09-19 10:31:27 -0700204 << persistent_properties.error();
Tom Cherry16fad422017-08-04 15:59:03 -0700205 persistent_properties = LoadPersistentPropertiesFromMemory();
206 }
Tom Cherry9614e4d2017-09-19 10:31:27 -0700207 auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
208 persistent_properties->mutable_properties()->end(),
Tom Cherrya97faba2017-09-15 15:44:04 -0700209 [&name](const auto& record) { return record.name() == name; });
Tom Cherry9614e4d2017-09-19 10:31:27 -0700210 if (it != persistent_properties->mutable_properties()->end()) {
Tom Cherrya97faba2017-09-15 15:44:04 -0700211 it->set_name(name);
212 it->set_value(value);
Tom Cherry16fad422017-08-04 15:59:03 -0700213 } else {
Tom Cherry9614e4d2017-09-19 10:31:27 -0700214 AddPersistentProperty(name, value, &persistent_properties.value());
Tom Cherry16fad422017-08-04 15:59:03 -0700215 }
216
Tom Cherry9614e4d2017-09-19 10:31:27 -0700217 if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
Tom Cherry16fad422017-08-04 15:59:03 -0700218 LOG(ERROR) << "Could not store persistent property: " << result.error();
219 }
220}
221
Tom Cherrya97faba2017-09-15 15:44:04 -0700222PersistentProperties LoadPersistentProperties() {
Tom Cherry16fad422017-08-04 15:59:03 -0700223 auto persistent_properties = LoadPersistentPropertyFile();
224
225 if (!persistent_properties) {
226 LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
227 << persistent_properties.error();
228 persistent_properties = LoadLegacyPersistentProperties();
229 if (!persistent_properties) {
230 LOG(ERROR) << "Unable to load legacy persistent properties: "
231 << persistent_properties.error();
232 return {};
233 }
234 if (auto result = WritePersistentPropertyFile(*persistent_properties); result) {
235 RemoveLegacyPersistentPropertyFiles();
236 } else {
237 LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
238 // Fall through so that we still set the properties that we've read.
239 }
240 }
241
242 return *persistent_properties;
243}
244
245} // namespace init
246} // namespace android