• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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