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 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 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 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 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 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 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 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. 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 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