Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a97faba6 authored by Tom Cherry's avatar Tom Cherry
Browse files

init: use protobuf for serialization of persistent properties

I probably should have done this from the start...  There's a shim to
convert my manually serialized format to protobuf, and since that has
not yet shipped, it'll be reverted in a short period of time.

Test: init unit tests
Test: upgrade from legacy and intermediate property formats successfully
Change-Id: Iad25f6c30d0b44d294230a53dd6876222d1c785b
parent 2fb5fa33
Loading
Loading
Loading
Loading
+7 −1
Original line number Original line Diff line number Diff line
@@ -73,6 +73,7 @@ cc_library_static {
        "log.cpp",
        "log.cpp",
        "parser.cpp",
        "parser.cpp",
        "persistent_properties.cpp",
        "persistent_properties.cpp",
        "persistent_properties.proto",
        "property_service.cpp",
        "property_service.cpp",
        "security.cpp",
        "security.cpp",
        "selinux.cpp",
        "selinux.cpp",
@@ -90,11 +91,15 @@ cc_library_static {
        "liblog",
        "liblog",
        "libprocessgroup",
        "libprocessgroup",
        "libfs_mgr",
        "libfs_mgr",
        "libprotobuf-cpp-lite",
    ],
    ],
    include_dirs: [
    include_dirs: [
        "system/core/mkbootimg",
        "system/core/mkbootimg",
    ],
    ],

    proto: {
        type: "lite",
        export_proto_headers: true,
    },
}
}


/*
/*
@@ -179,6 +184,7 @@ cc_test {
        "libinit",
        "libinit",
        "libselinux",
        "libselinux",
        "libcrypto",
        "libcrypto",
        "libprotobuf-cpp-lite",
    ],
    ],
}
}


+1 −0
Original line number Original line Diff line number Diff line
@@ -82,6 +82,7 @@ LOCAL_STATIC_LIBRARIES := \
    libprocessgroup \
    libprocessgroup \
    libavb \
    libavb \
    libkeyutils \
    libkeyutils \
    libprotobuf-cpp-lite \


LOCAL_REQUIRED_MODULES := \
LOCAL_REQUIRED_MODULES := \
    e2fsdroid \
    e2fsdroid \
+67 −70
Original line number Original line Diff line number Diff line
@@ -46,14 +46,21 @@ namespace {
constexpr const uint32_t kMagic = 0x8495E0B4;
constexpr const uint32_t kMagic = 0x8495E0B4;
constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";


Result<std::vector<std::pair<std::string, std::string>>> LoadLegacyPersistentProperties() {
void AddPersistentProperty(const std::string& name, const std::string& value,
                           PersistentProperties* persistent_properties) {
    auto persistent_property_record = persistent_properties->add_properties();
    persistent_property_record->set_name(name);
    persistent_property_record->set_value(value);
}

Result<PersistentProperties> LoadLegacyPersistentProperties() {
    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
    if (!dir) {
    if (!dir) {
        return ErrnoError() << "Unable to open persistent property directory \""
        return ErrnoError() << "Unable to open persistent property directory \""
                            << kLegacyPersistentPropertyDir << "\"";
                            << kLegacyPersistentPropertyDir << "\"";
    }
    }


    std::vector<std::pair<std::string, std::string>> persistent_properties;
    PersistentProperties persistent_properties;
    dirent* entry;
    dirent* entry;
    while ((entry = readdir(dir.get())) != nullptr) {
    while ((entry = readdir(dir.get())) != nullptr) {
        if (!StartsWith(entry->d_name, "persist.")) {
        if (!StartsWith(entry->d_name, "persist.")) {
@@ -87,7 +94,7 @@ Result<std::vector<std::pair<std::string, std::string>>> LoadLegacyPersistentPro


        std::string value;
        std::string value;
        if (ReadFdToString(fd, &value)) {
        if (ReadFdToString(fd, &value)) {
            persistent_properties.emplace_back(entry->d_name, value);
            AddPersistentProperty(entry->d_name, value, &persistent_properties);
        } else {
        } else {
            PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
            PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
        }
        }
@@ -115,30 +122,28 @@ void RemoveLegacyPersistentPropertyFiles() {
    }
    }
}
}


std::vector<std::pair<std::string, std::string>> LoadPersistentPropertiesFromMemory() {
PersistentProperties LoadPersistentPropertiesFromMemory() {
    std::vector<std::pair<std::string, std::string>> properties;
    PersistentProperties persistent_properties;
    __system_property_foreach(
    __system_property_foreach(
        [](const prop_info* pi, void* cookie) {
        [](const prop_info* pi, void* cookie) {
            __system_property_read_callback(
            __system_property_read_callback(
                pi,
                pi,
                [](void* cookie, const char* name, const char* value, unsigned serial) {
                [](void* cookie, const char* name, const char* value, unsigned serial) {
                    if (StartsWith(name, "persist.")) {
                    if (StartsWith(name, "persist.")) {
                        auto properties =
                        auto properties = reinterpret_cast<PersistentProperties*>(cookie);
                            reinterpret_cast<std::vector<std::pair<std::string, std::string>>*>(
                        AddPersistentProperty(name, value, properties);
                                cookie);
                        properties->emplace_back(name, value);
                    }
                    }
                },
                },
                cookie);
                cookie);
        },
        },
        &properties);
        &persistent_properties);
    return properties;
    return persistent_properties;
}
}


class PersistentPropertyFileParser {
class PersistentPropertyFileParser {
  public:
  public:
    PersistentPropertyFileParser(const std::string& contents) : contents_(contents), position_(0) {}
    PersistentPropertyFileParser(const std::string& contents) : contents_(contents), position_(0) {}
    Result<std::vector<std::pair<std::string, std::string>>> Parse();
    Result<PersistentProperties> Parse();


  private:
  private:
    Result<std::string> ReadString();
    Result<std::string> ReadString();
@@ -148,9 +153,7 @@ class PersistentPropertyFileParser {
    size_t position_;
    size_t position_;
};
};


Result<std::vector<std::pair<std::string, std::string>>> PersistentPropertyFileParser::Parse() {
Result<PersistentProperties> PersistentPropertyFileParser::Parse() {
    std::vector<std::pair<std::string, std::string>> result;

    if (auto magic = ReadUint32(); magic) {
    if (auto magic = ReadUint32(); magic) {
        if (*magic != kMagic) {
        if (*magic != kMagic) {
            return Error() << "Magic value '0x" << std::hex << *magic
            return Error() << "Magic value '0x" << std::hex << *magic
@@ -174,24 +177,20 @@ Result<std::vector<std::pair<std::string, std::string>>> PersistentPropertyFileP
        return Error() << "Could not read num_properties: " << num_properties.error();
        return Error() << "Could not read num_properties: " << num_properties.error();
    }
    }


    PersistentProperties result;
    while (position_ < contents_.size()) {
    while (position_ < contents_.size()) {
        auto key = ReadString();
        auto name = ReadString();
        if (!key) {
        if (!name) {
            return Error() << "Could not read key: " << key.error();
            return Error() << "Could not read name: " << name.error();
        }
        }
        if (!StartsWith(*key, "persist.")) {
        if (!StartsWith(*name, "persist.")) {
            return Error() << "Property '" << *key << "' does not starts with 'persist.'";
            return Error() << "Property '" << *name << "' does not starts with 'persist.'";
        }
        }
        auto value = ReadString();
        auto value = ReadString();
        if (!value) {
        if (!value) {
            return Error() << "Could not read value: " << value.error();
            return Error() << "Could not read value: " << value.error();
        }
        }
        result.emplace_back(*key, *value);
        AddPersistentProperty(*name, *value, &result);
    }

    if (result.size() != *num_properties) {
        return Error() << "Mismatch of number of persistent properties read, " << result.size()
                       << " and number of persistent properties expected, " << *num_properties;
    }
    }


    return result;
    return result;
@@ -220,9 +219,7 @@ Result<uint32_t> PersistentPropertyFileParser::ReadUint32() {
    return result;
    return result;
}
}


}  // namespace
Result<std::string> ReadPersistentPropertyFile() {

Result<std::vector<std::pair<std::string, std::string>>> LoadPersistentPropertyFile() {
    const std::string temp_filename = persistent_property_filename + ".tmp";
    const std::string temp_filename = persistent_property_filename + ".tmp";
    if (access(temp_filename.c_str(), F_OK) == 0) {
    if (access(temp_filename.c_str(), F_OK) == 0) {
        LOG(INFO)
        LOG(INFO)
@@ -234,51 +231,47 @@ Result<std::vector<std::pair<std::string, std::string>>> LoadPersistentPropertyF
    if (!file_contents) {
    if (!file_contents) {
        return Error() << "Unable to read persistent property file: " << file_contents.error();
        return Error() << "Unable to read persistent property file: " << file_contents.error();
    }
    }
    auto parsed_contents = PersistentPropertyFileParser(*file_contents).Parse();
    return *file_contents;
    if (!parsed_contents) {
        // If the file cannot be parsed, then we don't have any recovery mechanisms, so we delete
        // it to allow for future writes to take place successfully.
        unlink(persistent_property_filename.c_str());
        return Error() << "Unable to parse persistent property file: " << parsed_contents.error();
}
}
    return parsed_contents;
}

std::string GenerateFileContents(
    const std::vector<std::pair<std::string, std::string>>& persistent_properties) {
    std::string result;


    uint32_t magic = kMagic;
}  // namespace
    result.append(reinterpret_cast<char*>(&magic), sizeof(uint32_t));


    uint32_t version = 1;
Result<PersistentProperties> LoadPersistentPropertyFile() {
    result.append(reinterpret_cast<char*>(&version), sizeof(uint32_t));
    auto file_contents = ReadPersistentPropertyFile();
    if (!file_contents) return file_contents.error();


    uint32_t num_properties = persistent_properties.size();
    // Check the intermediate "I should have used protobufs from the start" format.
    result.append(reinterpret_cast<char*>(&num_properties), sizeof(uint32_t));
    // TODO: Remove this.
    auto parsed_contents = PersistentPropertyFileParser(*file_contents).Parse();
    if (parsed_contents) {
        LOG(INFO) << "Intermediate format persistent property file found, converting to protobuf";


    for (const auto& [key, value] : persistent_properties) {
        // Update to the protobuf format
        uint32_t key_length = key.length();
        WritePersistentPropertyFile(*parsed_contents);
        result.append(reinterpret_cast<char*>(&key_length), sizeof(uint32_t));
        return parsed_contents;
        result.append(key);
        uint32_t value_length = value.length();
        result.append(reinterpret_cast<char*>(&value_length), sizeof(uint32_t));
        result.append(value);
    }
    return result;
    }
    }


Result<Success> WritePersistentPropertyFile(
    PersistentProperties persistent_properties;
    const std::vector<std::pair<std::string, std::string>>& persistent_properties) {
    if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
    auto file_contents = GenerateFileContents(persistent_properties);

    // If the file cannot be parsed in either format, then we don't have any recovery
    // mechanisms, so we delete it to allow for future writes to take place successfully.
    unlink(persistent_property_filename.c_str());
    return Error() << "Unable to parse persistent property file: " << parsed_contents.error();
}


Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
    const std::string temp_filename = persistent_property_filename + ".tmp";
    const std::string temp_filename = persistent_property_filename + ".tmp";
    unique_fd fd(TEMP_FAILURE_RETRY(
    unique_fd fd(TEMP_FAILURE_RETRY(
        open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
        open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
    if (fd == -1) {
    if (fd == -1) {
        return ErrnoError() << "Could not open temporary properties file";
        return ErrnoError() << "Could not open temporary properties file";
    }
    }
    if (!WriteStringToFd(file_contents, fd)) {
    std::string serialized_string;
    if (!persistent_properties.SerializeToString(&serialized_string)) {
        return Error() << "Unable to serialize properties";
    }
    if (!WriteStringToFd(serialized_string, fd)) {
        return ErrnoError() << "Unable to write file contents";
        return ErrnoError() << "Unable to write file contents";
    }
    }
    fsync(fd);
    fsync(fd);
@@ -295,26 +288,30 @@ Result<Success> WritePersistentPropertyFile(
// Persistent properties are not written often, so we rather not keep any data in memory and read
// Persistent properties are not written often, so we rather not keep any data in memory and read
// then rewrite the persistent property file for each update.
// then rewrite the persistent property file for each update.
void WritePersistentProperty(const std::string& name, const std::string& value) {
void WritePersistentProperty(const std::string& name, const std::string& value) {
    auto persistent_properties = LoadPersistentPropertyFile();
    auto file_contents = ReadPersistentPropertyFile();
    if (!persistent_properties) {
    PersistentProperties persistent_properties;

    if (!file_contents || !persistent_properties.ParseFromString(*file_contents)) {
        LOG(ERROR) << "Recovering persistent properties from memory: "
        LOG(ERROR) << "Recovering persistent properties from memory: "
                   << persistent_properties.error();
                   << (!file_contents ? file_contents.error_string() : "Could not parse protobuf");
        persistent_properties = LoadPersistentPropertiesFromMemory();
        persistent_properties = LoadPersistentPropertiesFromMemory();
    }
    }
    auto it = std::find_if(persistent_properties->begin(), persistent_properties->end(),
    auto it = std::find_if(persistent_properties.mutable_properties()->begin(),
                           [&name](const auto& entry) { return entry.first == name; });
                           persistent_properties.mutable_properties()->end(),
    if (it != persistent_properties->end()) {
                           [&name](const auto& record) { return record.name() == name; });
        *it = {name, value};
    if (it != persistent_properties.mutable_properties()->end()) {
        it->set_name(name);
        it->set_value(value);
    } else {
    } else {
        persistent_properties->emplace_back(name, value);
        AddPersistentProperty(name, value, &persistent_properties);
    }
    }


    if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
    if (auto result = WritePersistentPropertyFile(persistent_properties); !result) {
        LOG(ERROR) << "Could not store persistent property: " << result.error();
        LOG(ERROR) << "Could not store persistent property: " << result.error();
    }
    }
}
}


std::vector<std::pair<std::string, std::string>> LoadPersistentProperties() {
PersistentProperties LoadPersistentProperties() {
    auto persistent_properties = LoadPersistentPropertyFile();
    auto persistent_properties = LoadPersistentPropertyFile();


    if (!persistent_properties) {
    if (!persistent_properties) {
+4 −7
Original line number Original line Diff line number Diff line
@@ -18,22 +18,19 @@
#define _INIT_PERSISTENT_PROPERTIES_H
#define _INIT_PERSISTENT_PROPERTIES_H


#include <string>
#include <string>
#include <vector>


#include "result.h"
#include "result.h"
#include "system/core/init/persistent_properties.pb.h"


namespace android {
namespace android {
namespace init {
namespace init {


std::vector<std::pair<std::string, std::string>> LoadPersistentProperties();
PersistentProperties LoadPersistentProperties();
void WritePersistentProperty(const std::string& name, const std::string& value);
void WritePersistentProperty(const std::string& name, const std::string& value);


// Exposed only for testing
// Exposed only for testing
Result<std::vector<std::pair<std::string, std::string>>> LoadPersistentPropertyFile();
Result<PersistentProperties> LoadPersistentPropertyFile();
std::string GenerateFileContents(
Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
    const std::vector<std::pair<std::string, std::string>>& persistent_properties);
Result<Success> WritePersistentPropertyFile(
    const std::vector<std::pair<std::string, std::string>>& persistent_properties);
extern std::string persistent_property_filename;
extern std::string persistent_property_filename;


}  // namespace init
}  // namespace init
+27 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

syntax = "proto2";
option optimize_for = LITE_RUNTIME;

message PersistentProperties {
    message PersistentPropertyRecord {
        optional string name = 1;
        optional string value = 2;
    }

    repeated PersistentPropertyRecord properties = 1;
}
Loading