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
34using android::base::ReadFdToString;
35using android::base::StartsWith;
36using android::base::WriteStringToFd;
37using android::base::unique_fd;
38
39namespace android {
40namespace init {
41
42std::string persistent_property_filename = "/data/property/persistent_properties";
43
44namespace {
45
46constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
47
48void 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
55Result<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
104void 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
124PersistentProperties 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
142Result<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
159Result<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
172Result<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.
199void 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
222PersistentProperties 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