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