xref: /aosp_15_r20/system/core/init/persistent_properties.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
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