Home | History | Annotate | Download | only in init
      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