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
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
34 using android::base::ReadFdToString;
35 using android::base::StartsWith;
36 using android::base::WriteStringToFd;
37 using android::base::unique_fd;
38
39 namespace android {
40 namespace init {
41
42 std::string persistent_property_filename = "/data/property/persistent_properties";
43
44 namespace {
45
46 constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
47
AddPersistentProperty(const std::string & name,const std::string & value,PersistentProperties * persistent_properties)48 void 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
LoadLegacyPersistentProperties()55 Result<PersistentProperties> LoadLegacyPersistentProperties() {
56 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
62 PersistentProperties persistent_properties;
63 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)) {
96 AddPersistentProperty(entry->d_name, value, &persistent_properties);
97 } else {
98 PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
99 }
100 }
101 return persistent_properties;
102 }
103
RemoveLegacyPersistentPropertyFiles()104 void 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
LoadPersistentPropertiesFromMemory()124 PersistentProperties LoadPersistentPropertiesFromMemory() {
125 PersistentProperties persistent_properties;
126 __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.")) {
132 auto properties = reinterpret_cast<PersistentProperties*>(cookie);
133 AddPersistentProperty(name, value, properties);
134 }
135 },
136 cookie);
137 },
138 &persistent_properties);
139 return persistent_properties;
140 }
141
ReadPersistentPropertyFile()142 Result<std::string> ReadPersistentPropertyFile() {
143 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 }
154 return *file_contents;
155 }
156
157 } // namespace
158
LoadPersistentPropertyFile()159 Result<PersistentProperties> LoadPersistentPropertyFile() {
160 auto file_contents = ReadPersistentPropertyFile();
161 if (!file_contents) return file_contents.error();
162
163 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());
169 return Error() << "Unable to parse persistent property file: Could not parse protobuf";
170 }
171
WritePersistentPropertyFile(const PersistentProperties & persistent_properties)172 Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
173 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 }
179 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)) {
184 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.
WritePersistentProperty(const std::string & name,const std::string & value)199 void WritePersistentProperty(const std::string& name, const std::string& value) {
200 auto persistent_properties = LoadPersistentPropertyFile();
201
202 if (!persistent_properties) {
203 LOG(ERROR) << "Recovering persistent properties from memory: "
204 << persistent_properties.error();
205 persistent_properties = LoadPersistentPropertiesFromMemory();
206 }
207 auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
208 persistent_properties->mutable_properties()->end(),
209 [&name](const auto& record) { return record.name() == name; });
210 if (it != persistent_properties->mutable_properties()->end()) {
211 it->set_name(name);
212 it->set_value(value);
213 } else {
214 AddPersistentProperty(name, value, &persistent_properties.value());
215 }
216
217 if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
218 LOG(ERROR) << "Could not store persistent property: " << result.error();
219 }
220 }
221
LoadPersistentProperties()222 PersistentProperties LoadPersistentProperties() {
223 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
247