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

Commit 7cf627ba authored by David Anderson's avatar David Anderson Committed by Gerrit Code Review
Browse files

Merge changes Ib744d763,I74278bb5,I3d240d6e,I1b41d233

* changes:
  fastboot: Move some helpers into util.h/.cpp.
  fastboot: Allow using LOG().
  fastboot: Use RAII for sparse_file objects.
  liblp: Add a helper class for building sparse-compatible super image layouts.
parents 4c96a167 aa87dc5a
Loading
Loading
Loading
Loading
+45 −59
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@

#include <android-base/endian.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/parsenetaddress.h>
@@ -113,7 +114,7 @@ enum fb_buffer_type {

struct fastboot_buffer {
    enum fb_buffer_type type;
    void* data;
    std::vector<SparsePtr> files;
    int64_t sz;
    unique_fd fd;
    int64_t image_size;
@@ -246,14 +247,6 @@ static void InfoMessage(const std::string& info) {
    fprintf(stderr, "(bootloader) %s\n", info.c_str());
}

static int64_t get_file_size(borrowed_fd fd) {
    struct stat sb;
    if (fstat(fd.get(), &sb) == -1) {
        die("could not get file size");
    }
    return sb.st_size;
}

bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
    out->clear();

@@ -824,27 +817,32 @@ static void DumpInfo() {
    fprintf(stderr, "--------------------------------------------\n");
}

static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
    struct sparse_file* s = sparse_file_import_auto(fd, false, true);
    if (!s) die("cannot sparse read file");

static std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size) {
    if (max_size <= 0 || max_size > std::numeric_limits<uint32_t>::max()) {
        die("invalid max size %" PRId64, max_size);
    }

    int files = sparse_file_resparse(s, max_size, nullptr, 0);
    const int files = sparse_file_resparse(s, max_size, nullptr, 0);
    if (files < 0) die("Failed to resparse");

    sparse_file** out_s =
            reinterpret_cast<sparse_file**>(calloc(sizeof(struct sparse_file*), files + 1));
    if (!out_s) die("Failed to allocate sparse file array");

    files = sparse_file_resparse(s, max_size, out_s, files);
    if (files < 0) die("Failed to resparse");
    auto temp = std::make_unique<sparse_file*[]>(files);
    const int rv = sparse_file_resparse(s, max_size, temp.get(), files);
    if (rv < 0) die("Failed to resparse");

    std::vector<SparsePtr> out_s;
    for (int i = 0; i < files; i++) {
        out_s.emplace_back(temp[i], sparse_file_destroy);
    }
    return out_s;
}

static std::vector<SparsePtr> load_sparse_files(int fd, int64_t max_size) {
    SparsePtr s(sparse_file_import_auto(fd, false, true), sparse_file_destroy);
    if (!s) die("cannot sparse read file");

    return resparse_file(s.get(), max_size);
}

static uint64_t get_uint_var(const char* var_name) {
    std::string value_str;
    if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {
@@ -903,15 +901,13 @@ static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf) {
    int64_t limit = get_sparse_limit(sz);
    buf->fd = std::move(fd);
    if (limit) {
        sparse_file** s = load_sparse_files(buf->fd.get(), limit);
        if (s == nullptr) {
        buf->files = load_sparse_files(buf->fd.get(), limit);
        if (buf->files.empty()) {
            return false;
        }
        buf->type = FB_BUFFER_SPARSE;
        buf->data = s;
    } else {
        buf->type = FB_BUFFER_FD;
        buf->data = nullptr;
        buf->sz = sz;
    }

@@ -996,6 +992,8 @@ static bool has_vbmeta_partition() {
           fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
}

// Note: this only works in userspace fastboot. In the bootloader, use
// should_flash_in_userspace().
static bool is_logical(const std::string& partition) {
    std::string value;
    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
@@ -1078,9 +1076,16 @@ static void copy_avb_footer(const std::string& partition, struct fastboot_buffer
    lseek(buf->fd.get(), 0, SEEK_SET);
}

static void flash_buf(const std::string& partition, struct fastboot_buffer* buf) {
    sparse_file** s;
static void flash_partition_files(const std::string& partition,
                                  const std::vector<SparsePtr>& files) {
    for (size_t i = 0; i < files.size(); i++) {
        sparse_file* s = files[i].get();
        int64_t sz = sparse_file_len(s, true, false);
        fb->FlashPartition(partition, s, sz, i + 1, files.size());
    }
}

static void flash_buf(const std::string& partition, struct fastboot_buffer* buf) {
    if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
        partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b" ||
        partition == "recovery" || partition == "recovery_a" || partition == "recovery_b") {
@@ -1103,18 +1108,7 @@ static void flash_buf(const std::string& partition, struct fastboot_buffer* buf)

    switch (buf->type) {
        case FB_BUFFER_SPARSE: {
            std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
            s = reinterpret_cast<sparse_file**>(buf->data);
            while (*s) {
                int64_t sz = sparse_file_len(*s, true, false);
                sparse_files.emplace_back(*s, sz);
                ++s;
            }

            for (size_t i = 0; i < sparse_files.size(); ++i) {
                const auto& pair = sparse_files[i];
                fb->FlashPartition(partition, pair.first, pair.second, i + 1, sparse_files.size());
            }
            flash_partition_files(partition, buf->files);
            break;
        }
        case FB_BUFFER_FD:
@@ -1402,13 +1396,6 @@ static void CancelSnapshotIfNeeded() {
    }
}

class ImageSource {
  public:
    virtual ~ImageSource(){};
    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
    virtual unique_fd OpenFile(const std::string& name) const = 0;
};

class FlashAllTool {
  public:
    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
@@ -1782,20 +1769,7 @@ static bool should_flash_in_userspace(const std::string& partition_name) {
    if (!metadata) {
        return false;
    }
    for (const auto& partition : metadata->partitions) {
        auto candidate = android::fs_mgr::GetPartitionName(partition);
        if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
            // On retrofit devices, we don't know if, or whether, the A or B
            // slot has been flashed for dynamic partitions. Instead we add
            // both names to the list as a conservative guess.
            if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
                return true;
            }
        } else if (candidate == partition_name) {
            return true;
        }
    }
    return false;
    return should_flash_in_userspace(*metadata.get(), partition_name);
}

static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
@@ -1868,7 +1842,19 @@ static void do_wipe_super(const std::string& image, const std::string& slot_over
    }
}

static void FastbootLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
                           const char* /* tag */, const char* /* file */, unsigned int /* line */,
                           const char* message) {
    verbose("%s", message);
}

static void FastbootAborter(const char* message) {
    die("%s", message);
}

int FastBootTool::Main(int argc, char* argv[]) {
    android::base::InitLogging(argv, FastbootLogger, FastbootAborter);

    bool wants_wipe = false;
    bool wants_reboot = false;
    bool wants_reboot_bootloader = false;
+34 −1
Original line number Diff line number Diff line
@@ -30,11 +30,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/stat.h>
#include <sys/time.h>

#include "util.h"

using android::base::borrowed_fd;

static bool g_verbose = false;

double now() {
@@ -73,3 +75,34 @@ void verbose(const char* fmt, ...) {
    }
    fprintf(stderr, "\n");
}

bool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,
                               const std::string& partition_name) {
    for (const auto& partition : metadata.partitions) {
        auto candidate = android::fs_mgr::GetPartitionName(partition);
        if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
            // On retrofit devices, we don't know if, or whether, the A or B
            // slot has been flashed for dynamic partitions. Instead we add
            // both names to the list as a conservative guess.
            if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
                return true;
            }
        } else if (candidate == partition_name) {
            return true;
        }
    }
    return false;
}

bool is_sparse_file(borrowed_fd fd) {
    SparsePtr s(sparse_file_import(fd.get(), false, false), sparse_file_destroy);
    return !!s;
}

int64_t get_file_size(borrowed_fd fd) {
    struct stat sb;
    if (fstat(fd.get(), &sb) == -1) {
        die("could not get file size");
    }
    return sb.st_size;
}
+18 −0
Original line number Diff line number Diff line
@@ -4,8 +4,14 @@
#include <stdlib.h>

#include <string>
#include <vector>

#include <android-base/unique_fd.h>
#include <bootimg.h>
#include <liblp/liblp.h>
#include <sparse/sparse.h>

using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;

/* util stuff */
double now();
@@ -19,3 +25,15 @@ __attribute__((__format__(__printf__, 1, 2)));
void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));

void die(const std::string& str) __attribute__((__noreturn__));

bool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,
                               const std::string& partition_name);
bool is_sparse_file(android::base::borrowed_fd fd);
int64_t get_file_size(android::base::borrowed_fd fd);

class ImageSource {
  public:
    virtual ~ImageSource(){};
    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
    virtual android::base::unique_fd OpenFile(const std::string& name) const = 0;
};
+21 −14
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ cc_library {
    ],
    srcs: [
        "builder.cpp",
        "super_layout_builder.cpp",
        "images.cpp",
        "partition_opener.cpp",
        "property_fetcher.cpp",
@@ -62,17 +63,6 @@ cc_library {
    export_include_dirs: ["include"],
}

filegroup {
    name: "liblp_test_srcs",
    srcs: [
        "builder_test.cpp",
        "device_test.cpp",
        "io_test.cpp",
        "test_partition_opener.cpp",
        "utility_test.cpp",
    ],
}

cc_defaults {
    name: "liblp_test_defaults",
    defaults: ["fs_mgr_defaults"],
@@ -82,22 +72,39 @@ cc_defaults {
    static_libs: [
        "libcutils",
        "libgmock",
        "libfs_mgr",
        "liblp",
        "libcrypto_static",
    ] + liblp_lib_deps,
    header_libs: [
        "libstorage_literals_headers",
    ],
    target: {
        android: {
            srcs: [
                "device_test.cpp",
                "io_test.cpp",
            ],
            static_libs: [
                "libfs_mgr",
            ],
        }
    },
    stl: "libc++_static",
    srcs: [":liblp_test_srcs"],
    srcs: [
        "builder_test.cpp",
        "super_layout_builder_test.cpp",
        "test_partition_opener.cpp",
        "utility_test.cpp",
    ],
}

cc_test {
    name: "liblp_test",
    defaults: ["liblp_test_defaults"],
    test_config: "liblp_test.xml",
    test_suites: ["device-tests"],
    auto_gen_config: true,
    require_root: true,
    host_supported: true
}

cc_test {
+104 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2023 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.
//
#pragma once

#include <stdint.h>

#include <memory>
#include <ostream>
#include <string>
#include <unordered_map>
#include <utility>

#include <android-base/unique_fd.h>
#include <liblp/builder.h>

namespace android {
namespace fs_mgr {

struct SuperImageExtent {
    enum class Type { INVALID, DATA, PARTITION, ZERO, DONTCARE };

    SuperImageExtent(const SuperImageExtent& other) = default;
    SuperImageExtent(SuperImageExtent&& other) = default;
    SuperImageExtent(uint64_t offset, uint64_t size, Type type)
        : offset(offset), size(size), type(type) {}

    SuperImageExtent(uint64_t offset, std::shared_ptr<std::string> blob)
        : SuperImageExtent(offset, blob->size(), Type::DATA) {
        this->blob = blob;
    }

    SuperImageExtent(uint64_t offset, uint64_t size, const std::string& image_name,
                     uint64_t image_offset)
        : SuperImageExtent(offset, size, Type::PARTITION) {
        this->image_name = image_name;
        this->image_offset = image_offset;
    }

    SuperImageExtent& operator=(const SuperImageExtent& other) = default;
    SuperImageExtent& operator=(SuperImageExtent&& other) = default;

    bool operator<(const SuperImageExtent& other) const { return offset < other.offset; }
    bool operator==(const SuperImageExtent& other) const;

    // Location, size, and type of the extent.
    uint64_t offset = 0;
    uint64_t size = 0;
    Type type = Type::INVALID;

    // If type == DATA, this contains the bytes to write.
    std::shared_ptr<std::string> blob;
    // If type == PARTITION, this contains the partition image name and
    // offset within that file.
    std::string image_name;
    uint64_t image_offset = 0;
};

// The SuperLayoutBuilder allows building a sparse view of a super image. This
// is useful for efficient flashing, eg to bypass fastbootd and directly flash
// super without physically building and storing the image.
class SuperLayoutBuilder final {
  public:
    // Open a super_empty.img, return false on failure. This must be called to
    // initialize the tool. If it returns false, either the image failed to
    // parse, or the tool is not compatible with how the device is configured
    // (in which case fastbootd should be preferred).
    [[nodiscard]] bool Open(android::base::borrowed_fd fd);
    [[nodiscard]] bool Open(const void* data, size_t bytes);
    [[nodiscard]] bool Open(const LpMetadata& metadata);

    // Add a partition's image and size to the work list. If false is returned,
    // there was either a duplicate partition or not enough space in super.
    bool AddPartition(const std::string& partition_name, const std::string& image_name,
                      uint64_t partition_size);

    // Return the list of extents describing the super image. If this list is
    // empty, then there was an unrecoverable error in building the list.
    std::vector<SuperImageExtent> GetImageLayout();

    // Return the current metadata.
    std::unique_ptr<LpMetadata> Export() const { return builder_->Export(); }

  private:
    std::unique_ptr<MetadataBuilder> builder_;
    std::unordered_map<std::string, std::string> image_map_;
};

std::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent);

}  // namespace fs_mgr
}  // namespace android
Loading