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::Dirname;
35  using android::base::ReadFdToString;
36  using android::base::StartsWith;
37  using android::base::unique_fd;
38  using android::base::WriteStringToFd;
39  
40  namespace android {
41  namespace init {
42  
43  std::string persistent_property_filename = "/data/property/persistent_properties";
44  
45  namespace {
46  
47  constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
48  
AddPersistentProperty(const std::string & name,const std::string & value,PersistentProperties * persistent_properties)49  void AddPersistentProperty(const std::string& name, const std::string& value,
50                             PersistentProperties* persistent_properties) {
51      auto persistent_property_record = persistent_properties->add_properties();
52      persistent_property_record->set_name(name);
53      persistent_property_record->set_value(value);
54  }
55  
LoadLegacyPersistentProperties()56  Result<PersistentProperties> LoadLegacyPersistentProperties() {
57      std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
58      if (!dir) {
59          return ErrnoError() << "Unable to open persistent property directory \""
60                              << kLegacyPersistentPropertyDir << "\"";
61      }
62  
63      PersistentProperties persistent_properties;
64      dirent* entry;
65      while ((entry = readdir(dir.get())) != nullptr) {
66          if (!StartsWith(entry->d_name, "persist.")) {
67              continue;
68          }
69          if (entry->d_type != DT_REG) {
70              continue;
71          }
72  
73          unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
74          if (fd == -1) {
75              PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
76              continue;
77          }
78  
79          struct stat sb;
80          if (fstat(fd.get(), &sb) == -1) {
81              PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
82              continue;
83          }
84  
85          // File must not be accessible to others, be owned by root/root, and
86          // not be a hard link to any other file.
87          if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
88              sb.st_nlink != 1) {
89              PLOG(ERROR) << "skipping insecure property file " << entry->d_name
90                          << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
91                          << " mode=" << std::oct << sb.st_mode << ")";
92              continue;
93          }
94  
95          std::string value;
96          if (ReadFdToString(fd, &value)) {
97              AddPersistentProperty(entry->d_name, value, &persistent_properties);
98          } else {
99              PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
100          }
101      }
102      return persistent_properties;
103  }
104  
RemoveLegacyPersistentPropertyFiles()105  void RemoveLegacyPersistentPropertyFiles() {
106      std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
107      if (!dir) {
108          PLOG(ERROR) << "Unable to open persistent property directory \""
109                      << kLegacyPersistentPropertyDir << "\"";
110          return;
111      }
112  
113      dirent* entry;
114      while ((entry = readdir(dir.get())) != nullptr) {
115          if (!StartsWith(entry->d_name, "persist.")) {
116              continue;
117          }
118          if (entry->d_type != DT_REG) {
119              continue;
120          }
121          unlinkat(dirfd(dir.get()), entry->d_name, 0);
122      }
123  }
124  
LoadPersistentPropertiesFromMemory()125  PersistentProperties LoadPersistentPropertiesFromMemory() {
126      PersistentProperties persistent_properties;
127      __system_property_foreach(
128          [](const prop_info* pi, void* cookie) {
129              __system_property_read_callback(
130                  pi,
131                  [](void* cookie, const char* name, const char* value, unsigned serial) {
132                      if (StartsWith(name, "persist.")) {
133                          auto properties = reinterpret_cast<PersistentProperties*>(cookie);
134                          AddPersistentProperty(name, value, properties);
135                      }
136                  },
137                  cookie);
138          },
139          &persistent_properties);
140      return persistent_properties;
141  }
142  
ReadPersistentPropertyFile()143  Result<std::string> ReadPersistentPropertyFile() {
144      const std::string temp_filename = persistent_property_filename + ".tmp";
145      if (access(temp_filename.c_str(), F_OK) == 0) {
146          LOG(INFO)
147              << "Found temporary property file while attempting to persistent system properties"
148                 " a previous persistent property write may have failed";
149          unlink(temp_filename.c_str());
150      }
151      auto file_contents = ReadFile(persistent_property_filename);
152      if (!file_contents.ok()) {
153          return Error() << "Unable to read persistent property file: " << file_contents.error();
154      }
155      return *file_contents;
156  }
157  
ParsePersistentPropertyFile(const std::string & file_contents)158  Result<PersistentProperties> ParsePersistentPropertyFile(const std::string& file_contents) {
159      PersistentProperties persistent_properties;
160      if (!persistent_properties.ParseFromString(file_contents)) {
161          return Error() << "Unable to parse persistent property file: Could not parse protobuf";
162      }
163      for (auto& prop : persistent_properties.properties()) {
164          if (!StartsWith(prop.name(), "persist.")) {
165              return Error() << "Unable to load persistent property file: property '" << prop.name()
166                             << "' doesn't start with 'persist.'";
167          }
168      }
169      return persistent_properties;
170  }
171  
172  }  // namespace
173  
LoadPersistentPropertyFile()174  Result<PersistentProperties> LoadPersistentPropertyFile() {
175      auto file_contents = ReadPersistentPropertyFile();
176      if (!file_contents.ok()) return file_contents.error();
177  
178      auto persistent_properties = ParsePersistentPropertyFile(*file_contents);
179      if (!persistent_properties.ok()) {
180          // If the file cannot be parsed in either format, then we don't have any recovery
181          // mechanisms, so we delete it to allow for future writes to take place successfully.
182          unlink(persistent_property_filename.c_str());
183      }
184      return persistent_properties;
185  }
186  
WritePersistentPropertyFile(const PersistentProperties & persistent_properties)187  Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
188      const std::string temp_filename = persistent_property_filename + ".tmp";
189      unique_fd fd(TEMP_FAILURE_RETRY(
190          open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
191      if (fd == -1) {
192          return ErrnoError() << "Could not open temporary properties file";
193      }
194      std::string serialized_string;
195      if (!persistent_properties.SerializeToString(&serialized_string)) {
196          return Error() << "Unable to serialize properties";
197      }
198      if (!WriteStringToFd(serialized_string, fd)) {
199          return ErrnoError() << "Unable to write file contents";
200      }
201      fsync(fd.get());
202      fd.reset();
203  
204      if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
205          int saved_errno = errno;
206          unlink(temp_filename.c_str());
207          return Error(saved_errno) << "Unable to rename persistent property file";
208      }
209  
210      // rename() is atomic with regards to the kernel's filesystem buffers, but the parent
211      // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage.
212      // Note in this case, that the source and destination directories are the same, so only one
213      // fsync() is required.
214      auto dir = Dirname(persistent_property_filename);
215      auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};
216      if (dir_fd < 0) {
217          return ErrnoError() << "Unable to open persistent properties directory for fsync()";
218      }
219      fsync(dir_fd.get());
220  
221      return {};
222  }
223  
224  // Persistent properties are not written often, so we rather not keep any data in memory and read
225  // then rewrite the persistent property file for each update.
WritePersistentProperty(const std::string & name,const std::string & value)226  void WritePersistentProperty(const std::string& name, const std::string& value) {
227      auto persistent_properties = LoadPersistentPropertyFile();
228  
229      if (!persistent_properties.ok()) {
230          LOG(ERROR) << "Recovering persistent properties from memory: "
231                     << persistent_properties.error();
232          persistent_properties = LoadPersistentPropertiesFromMemory();
233      }
234      auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
235                             persistent_properties->mutable_properties()->end(),
236                             [&name](const auto& record) { return record.name() == name; });
237      if (it != persistent_properties->mutable_properties()->end()) {
238          it->set_name(name);
239          it->set_value(value);
240      } else {
241          AddPersistentProperty(name, value, &persistent_properties.value());
242      }
243  
244      if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) {
245          LOG(ERROR) << "Could not store persistent property: " << result.error();
246      }
247  }
248  
LoadPersistentProperties()249  PersistentProperties LoadPersistentProperties() {
250      auto persistent_properties = LoadPersistentPropertyFile();
251  
252      if (!persistent_properties.ok()) {
253          LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
254                     << persistent_properties.error();
255          persistent_properties = LoadLegacyPersistentProperties();
256          if (!persistent_properties.ok()) {
257              LOG(ERROR) << "Unable to load legacy persistent properties: "
258                         << persistent_properties.error();
259              return {};
260          }
261          if (auto result = WritePersistentPropertyFile(*persistent_properties); result.ok()) {
262              RemoveLegacyPersistentPropertyFiles();
263          } else {
264              LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
265              // Fall through so that we still set the properties that we've read.
266          }
267      }
268  
269      return *persistent_properties;
270  }
271  
272  }  // namespace init
273  }  // namespace android
274