Merge "Snap for 9560500 from 15d7230ed614a6a379c7aa78fcdb21197885ebaf to sdk-release" into sdk-release
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9c1b136..a8d7de6 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
#include <string>
#include <thread>
+#include <android/dlext.h>
#include <android/fdsan.h>
#include <android/set_abort_message.h>
#include <bionic/malloc.h>
@@ -1909,7 +1910,7 @@
ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
}
-static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+static std::string GetTestLibraryPath() {
std::string test_lib(testing::internal::GetArgvs()[0]);
auto const value = test_lib.find_last_of('/');
if (value == std::string::npos) {
@@ -1917,7 +1918,62 @@
} else {
test_lib = test_lib.substr(0, value + 1) + "./";
}
- test_lib += "libcrash_test.so";
+ return test_lib + "libcrash_test.so";
+}
+
+static void CreateEmbeddedLibrary(int out_fd) {
+ std::string test_lib(GetTestLibraryPath());
+ android::base::unique_fd fd(open(test_lib.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_NE(fd.get(), -1);
+ off_t file_size = lseek(fd, 0, SEEK_END);
+ ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
+ std::vector<uint8_t> contents(file_size);
+ ASSERT_TRUE(android::base::ReadFully(fd, contents.data(), contents.size()));
+
+ // Put the shared library data at a pagesize() offset.
+ ASSERT_EQ(lseek(out_fd, 4 * getpagesize(), SEEK_CUR), 4 * getpagesize());
+ ASSERT_EQ(static_cast<size_t>(write(out_fd, contents.data(), contents.size())), contents.size());
+}
+
+TEST_F(CrasherTest, non_zero_offset_in_library) {
+ int intercept_result;
+ unique_fd output_fd;
+ TemporaryFile tf;
+ CreateEmbeddedLibrary(tf.fd);
+ StartProcess([&tf]() {
+ android_dlextinfo extinfo{};
+ extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
+ extinfo.library_fd = tf.fd;
+ extinfo.library_fd_offset = 4 * getpagesize();
+ void* handle = android_dlopen_ext(tf.path, RTLD_NOW, &extinfo);
+ if (handle == nullptr) {
+ _exit(1);
+ }
+ void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, "crash"));
+ if (crash_func == nullptr) {
+ _exit(1);
+ }
+ crash_func();
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ // Verify the crash includes an offset value in the backtrace.
+ std::string match_str = android::base::StringPrintf("%s\\!libcrash_test.so \\(offset 0x%x\\)",
+ tf.path, 4 * getpagesize());
+ ASSERT_MATCH(result, match_str);
+}
+
+static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string test_lib(GetTestLibraryPath());
*tmp_so_name = std::string(tmp_dir) + "/libcrash_test.so";
std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index a05bcec..e4d68f8 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -176,8 +176,14 @@
build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
}
- CB(should_log, " #%02d pc %0*" PRIx64 " %s%s%s", index++, pointer_width(tombstone) * 2,
- frame.rel_pc(), frame.file_name().c_str(), function.c_str(), build_id.c_str());
+ std::string line =
+ StringPrintf(" #%02d pc %0*" PRIx64 " %s", index++, pointer_width(tombstone) * 2,
+ frame.rel_pc(), frame.file_name().c_str());
+ if (frame.file_map_offset() != 0) {
+ line += StringPrintf(" (offset 0x%" PRIx64 ")", frame.file_map_offset());
+ }
+ line += function + build_id;
+ CB(should_log, "%s", line.c_str());
}
}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index a801900..9512e7e 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -286,6 +286,7 @@
"fastboot.cpp",
"fs.cpp",
"socket.cpp",
+ "super_flash_helper.cpp",
"tcp.cpp",
"udp.cpp",
"util.cpp",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 9676f87..8f7cced 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -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>
@@ -62,6 +63,7 @@
#include <build/version.h>
#include <libavb/libavb.h>
#include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
#include <platform_tools_version.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -71,6 +73,7 @@
#include "diagnose_usb.h"
#include "fastboot_driver.h"
#include "fs.h"
+#include "super_flash_helper.h"
#include "tcp.h"
#include "transport.h"
#include "udp.h"
@@ -113,7 +116,7 @@
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 +249,6 @@
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 +819,32 @@
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");
+ 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");
- files = sparse_file_resparse(s, max_size, out_s, files);
- if (files < 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 +903,13 @@
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 +994,8 @@
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 +1078,16 @@
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 +1110,7 @@
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 +1398,6 @@
}
}
-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,
@@ -1418,20 +1407,30 @@
private:
void CheckRequirements();
- void DetermineSecondarySlot();
+ void DetermineSlot();
void CollectImages();
void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
void UpdateSuperPartition();
+ bool OptimizedFlashSuper();
+
+ // If the image uses the default slot, or the user specified "all", then
+ // the paired string will be empty. If the image requests a specific slot
+ // (for example, system_other) it is specified instead.
+ using ImageEntry = std::pair<const Image*, std::string>;
+
+ std::string GetPartitionName(const ImageEntry& entry);
const ImageSource& source_;
std::string slot_override_;
bool skip_secondary_;
bool wipe_;
bool force_flash_;
+ std::string current_slot_;
std::string secondary_slot_;
- std::vector<std::pair<const Image*, std::string>> boot_images_;
- std::vector<std::pair<const Image*, std::string>> os_images_;
+
+ std::vector<ImageEntry> boot_images_;
+ std::vector<ImageEntry> os_images_;
};
FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
@@ -1454,7 +1453,7 @@
set_active(slot_override_);
}
- DetermineSecondarySlot();
+ DetermineSlot();
CollectImages();
CancelSnapshotIfNeeded();
@@ -1463,24 +1462,92 @@
// or in bootloader fastboot.
FlashImages(boot_images_);
- // Sync the super partition. This will reboot to userspace fastboot if needed.
- UpdateSuperPartition();
+ if (!OptimizedFlashSuper()) {
+ // Sync the super partition. This will reboot to userspace fastboot if needed.
+ UpdateSuperPartition();
- // Resize any logical partition to 0, so each partition is reset to 0
- // extents, and will achieve more optimal allocation.
- for (const auto& [image, slot] : os_images_) {
- auto resize_partition = [](const std::string& partition) -> void {
- if (is_logical(partition)) {
- fb->ResizePartition(partition, "0");
- }
- };
- do_for_partitions(image->part_name, slot, resize_partition, false);
+ // Resize any logical partition to 0, so each partition is reset to 0
+ // extents, and will achieve more optimal allocation.
+ for (const auto& [image, slot] : os_images_) {
+ auto resize_partition = [](const std::string& partition) -> void {
+ if (is_logical(partition)) {
+ fb->ResizePartition(partition, "0");
+ }
+ };
+ do_for_partitions(image->part_name, slot, resize_partition, false);
+ }
}
// Flash OS images, resizing logical partitions as needed.
FlashImages(os_images_);
}
+bool FlashAllTool::OptimizedFlashSuper() {
+ if (!supports_AB()) {
+ LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
+ return false;
+ }
+ if (slot_override_ == "all") {
+ LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
+ return false;
+ }
+
+ // Does this device use dynamic partitions at all?
+ unique_fd fd = source_.OpenFile("super_empty.img");
+ if (fd < 0) {
+ LOG(VERBOSE) << "could not open super_empty.img";
+ return false;
+ }
+
+ // Try to find whether there is a super partition.
+ std::string super_name;
+ if (fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
+ super_name = "super";
+ }
+ std::string partition_size_str;
+ if (fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
+ LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
+ return false;
+ }
+
+ SuperFlashHelper helper(source_);
+ if (!helper.Open(fd)) {
+ return false;
+ }
+
+ for (const auto& entry : os_images_) {
+ auto partition = GetPartitionName(entry);
+ auto image = entry.first;
+
+ if (!helper.AddPartition(partition, image->img_name, image->optional_if_no_image)) {
+ return false;
+ }
+ }
+
+ auto s = helper.GetSparseLayout();
+ if (!s) {
+ return false;
+ }
+
+ std::vector<SparsePtr> files;
+ if (int limit = get_sparse_limit(sparse_file_len(s.get(), false, false))) {
+ files = resparse_file(s.get(), limit);
+ } else {
+ files.emplace_back(std::move(s));
+ }
+
+ // Send the data to the device.
+ flash_partition_files(super_name, files);
+
+ // Remove images that we already flashed, just in case we have non-dynamic OS images.
+ auto remove_if_callback = [&, this](const ImageEntry& entry) -> bool {
+ return helper.WillFlash(GetPartitionName(entry));
+ };
+ os_images_.erase(std::remove_if(os_images_.begin(), os_images_.end(), remove_if_callback),
+ os_images_.end());
+ return true;
+}
+
void FlashAllTool::CheckRequirements() {
std::vector<char> contents;
if (!source_.ReadFile("android-info.txt", &contents)) {
@@ -1489,7 +1556,13 @@
::CheckRequirements({contents.data(), contents.size()}, force_flash_);
}
-void FlashAllTool::DetermineSecondarySlot() {
+void FlashAllTool::DetermineSlot() {
+ if (slot_override_.empty()) {
+ current_slot_ = get_current_slot();
+ } else {
+ current_slot_ = slot_override_;
+ }
+
if (skip_secondary_) {
return;
}
@@ -1588,6 +1661,20 @@
}
}
+std::string FlashAllTool::GetPartitionName(const ImageEntry& entry) {
+ auto slot = entry.second;
+ if (slot.empty()) {
+ slot = current_slot_;
+ }
+ if (slot.empty()) {
+ return entry.first->part_name;
+ }
+ if (slot == "all") {
+ LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
+ }
+ return entry.first->part_name + "_" + slot;
+}
+
class ZipImageSource final : public ImageSource {
public:
explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
@@ -1782,20 +1869,7 @@
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 +1942,19 @@
}
}
+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;
diff --git a/fastboot/super_flash_helper.cpp b/fastboot/super_flash_helper.cpp
new file mode 100644
index 0000000..b617ce8
--- /dev/null
+++ b/fastboot/super_flash_helper.cpp
@@ -0,0 +1,125 @@
+//
+// 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.
+//
+
+#include "super_flash_helper.h"
+
+#include <android-base/logging.h>
+
+#include "util.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::fs_mgr::SuperImageExtent;
+
+SuperFlashHelper::SuperFlashHelper(const ImageSource& source) : source_(source) {}
+
+bool SuperFlashHelper::Open(borrowed_fd fd) {
+ if (!builder_.Open(fd)) {
+ LOG(VERBOSE) << "device does not support optimized super flashing";
+ return false;
+ }
+
+ base_metadata_ = builder_.Export();
+ return !!base_metadata_;
+}
+
+bool SuperFlashHelper::IncludeInSuper(const std::string& partition) {
+ return should_flash_in_userspace(*base_metadata_.get(), partition);
+}
+
+bool SuperFlashHelper::AddPartition(const std::string& partition, const std::string& image_name,
+ bool optional) {
+ if (!IncludeInSuper(partition)) {
+ return true;
+ }
+ auto iter = image_fds_.find(image_name);
+ if (iter == image_fds_.end()) {
+ unique_fd fd = source_.OpenFile(image_name);
+ if (fd < 0) {
+ if (!optional) {
+ LOG(VERBOSE) << "could not find partition image: " << image_name;
+ return false;
+ }
+ return true;
+ }
+ if (is_sparse_file(fd)) {
+ LOG(VERBOSE) << "cannot optimize dynamic partitions with sparse images";
+ return false;
+ }
+ iter = image_fds_.emplace(image_name, std::move(fd)).first;
+ }
+
+ if (!builder_.AddPartition(partition, image_name, get_file_size(iter->second))) {
+ return false;
+ }
+
+ will_flash_.emplace(partition);
+ return true;
+}
+
+SparsePtr SuperFlashHelper::GetSparseLayout() {
+ // Cache extents since the sparse ptr depends on data pointers.
+ if (extents_.empty()) {
+ extents_ = builder_.GetImageLayout();
+ if (extents_.empty()) {
+ LOG(VERBOSE) << "device does not support optimized super flashing";
+ return {nullptr, nullptr};
+ }
+ }
+
+ unsigned int block_size = base_metadata_->geometry.logical_block_size;
+ int64_t flashed_size = extents_.back().offset + extents_.back().size;
+ SparsePtr s(sparse_file_new(block_size, flashed_size), sparse_file_destroy);
+
+ for (const auto& extent : extents_) {
+ if (extent.offset / block_size > UINT_MAX) {
+ // Super image is too big to send via sparse files (>8TB).
+ LOG(VERBOSE) << "super image is too big to flash";
+ return {nullptr, nullptr};
+ }
+ unsigned int block = extent.offset / block_size;
+
+ int rv = 0;
+ switch (extent.type) {
+ case SuperImageExtent::Type::DONTCARE:
+ break;
+ case SuperImageExtent::Type::ZERO:
+ rv = sparse_file_add_fill(s.get(), 0, extent.size, block);
+ break;
+ case SuperImageExtent::Type::DATA:
+ rv = sparse_file_add_data(s.get(), extent.blob->data(), extent.size, block);
+ break;
+ case SuperImageExtent::Type::PARTITION: {
+ auto iter = image_fds_.find(extent.image_name);
+ if (iter == image_fds_.end()) {
+ LOG(FATAL) << "image added but not found: " << extent.image_name;
+ return {nullptr, nullptr};
+ }
+ rv = sparse_file_add_fd(s.get(), iter->second.get(), extent.image_offset,
+ extent.size, block);
+ break;
+ }
+ default:
+ LOG(VERBOSE) << "unrecognized extent type in super image layout";
+ return {nullptr, nullptr};
+ }
+ if (rv) {
+ LOG(VERBOSE) << "sparse failure building super image layout";
+ return {nullptr, nullptr};
+ }
+ }
+ return s;
+}
diff --git a/fastboot/super_flash_helper.h b/fastboot/super_flash_helper.h
new file mode 100644
index 0000000..29c15d0
--- /dev/null
+++ b/fastboot/super_flash_helper.h
@@ -0,0 +1,56 @@
+//
+// 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/unique_fd.h>
+#include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
+
+#include "util.h"
+
+class SuperFlashHelper final {
+ public:
+ explicit SuperFlashHelper(const ImageSource& source);
+
+ bool Open(android::base::borrowed_fd fd);
+ bool IncludeInSuper(const std::string& partition);
+ bool AddPartition(const std::string& partition, const std::string& image_name, bool optional);
+
+ // Note: the SparsePtr if non-null should not outlive SuperFlashHelper, since
+ // it depends on open fds and data pointers.
+ SparsePtr GetSparseLayout();
+
+ bool WillFlash(const std::string& partition) const {
+ return will_flash_.find(partition) != will_flash_.end();
+ }
+
+ private:
+ const ImageSource& source_;
+ android::fs_mgr::SuperLayoutBuilder builder_;
+ std::unique_ptr<android::fs_mgr::LpMetadata> base_metadata_;
+ std::vector<android::fs_mgr::SuperImageExtent> extents_;
+
+ // Cache open image fds. This keeps them alive while we flash the sparse
+ // file.
+ std::unordered_map<std::string, android::base::unique_fd> image_fds_;
+ std::unordered_set<std::string> will_flash_;
+};
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 900d6ea..ded54a5 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -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 @@
}
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;
+}
diff --git a/fastboot/util.h b/fastboot/util.h
index c719df2..290d0d5 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -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 @@
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;
+};
diff --git a/fs_mgr/libfiemap/metadata.cpp b/fs_mgr/libfiemap/metadata.cpp
index b0dfb5c..22b8afb 100644
--- a/fs_mgr/libfiemap/metadata.cpp
+++ b/fs_mgr/libfiemap/metadata.cpp
@@ -30,6 +30,7 @@
namespace fiemap {
using namespace android::fs_mgr;
+using android::base::unique_fd;
static constexpr uint32_t kMaxMetadataSize = 256 * 1024;
@@ -109,10 +110,18 @@
if (exported->partitions.empty() && android::base::RemoveFileIfExists(metadata_file)) {
return true;
}
- if (!WriteToImageFile(metadata_file, *exported.get())) {
+
+ unique_fd fd(open(metadata_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY | O_SYNC, 0644));
+ if (fd < 0) {
+ LOG(ERROR) << "open failed: " << metadata_file;
+ return false;
+ }
+
+ if (!WriteToImageFile(fd, *exported.get())) {
LOG(ERROR) << "Unable to save new metadata";
return false;
}
+
return true;
}
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index 088749d..a119bfc 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -195,7 +195,7 @@
}
int err = ioctl(fd, BLKGETSIZE64, out_size_num_byte);
if (err) {
- out_size_num_byte = 0;
+ *out_size_num_byte = 0;
return AVB_IO_RESULT_ERROR_IO;
}
return AVB_IO_RESULT_OK;
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 4b81c2c..996ffd7 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -39,6 +39,7 @@
],
srcs: [
"builder.cpp",
+ "super_layout_builder.cpp",
"images.cpp",
"partition_opener.cpp",
"property_fetcher.cpp",
@@ -62,17 +63,6 @@
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 @@
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 {
diff --git a/fs_mgr/liblp/include/liblp/super_layout_builder.h b/fs_mgr/liblp/include/liblp/super_layout_builder.h
new file mode 100644
index 0000000..d920855
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/super_layout_builder.h
@@ -0,0 +1,104 @@
+//
+// 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
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
deleted file mode 100644
index 98414b1..0000000
--- a/fs_mgr/liblp/liblp_test.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 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.
--->
-<configuration description="Config for liblp_test">
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="liblp_test" />
- </test>
-</configuration>
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
new file mode 100644
index 0000000..37f28e1
--- /dev/null
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -0,0 +1,241 @@
+//
+// 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.
+//
+#include <liblp/super_layout_builder.h>
+
+#include <liblp/liblp.h>
+
+#include "images.h"
+#include "utility.h"
+#include "writer.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+namespace android {
+namespace fs_mgr {
+
+bool SuperLayoutBuilder::Open(borrowed_fd fd) {
+ auto metadata = ReadFromImageFile(fd.get());
+ if (!metadata) {
+ return false;
+ }
+ return Open(*metadata.get());
+}
+
+bool SuperLayoutBuilder::Open(const void* data, size_t size) {
+ auto metadata = ReadFromImageBlob(data, size);
+ if (!metadata) {
+ return false;
+ }
+ return Open(*metadata.get());
+}
+
+bool SuperLayoutBuilder::Open(const LpMetadata& metadata) {
+ for (const auto& partition : metadata.partitions) {
+ if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
+ // Retrofit devices are not supported.
+ return false;
+ }
+ if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
+ // Writable partitions are not supported.
+ return false;
+ }
+ }
+ if (!metadata.extents.empty()) {
+ // Partitions that already have extents are not supported (should
+ // never be true of super_empty.img).
+ return false;
+ }
+ if (metadata.block_devices.size() != 1) {
+ // Only one "super" is supported.
+ return false;
+ }
+
+ builder_ = MetadataBuilder::New(metadata);
+ return !!builder_;
+}
+
+bool SuperLayoutBuilder::AddPartition(const std::string& partition_name,
+ const std::string& image_name, uint64_t partition_size) {
+ auto p = builder_->FindPartition(partition_name);
+ if (!p) {
+ return false;
+ }
+ if (!builder_->ResizePartition(p, partition_size)) {
+ return false;
+ }
+ image_map_.emplace(partition_name, image_name);
+ return true;
+}
+
+// Fill the space between each extent, if any, with either a fill or dontcare
+// extent. The caller constructs a sample extent to re-use.
+static bool AddGapExtents(std::vector<SuperImageExtent>* extents, SuperImageExtent::Type gap_type) {
+ std::vector<SuperImageExtent> old = std::move(*extents);
+ std::sort(old.begin(), old.end());
+
+ *extents = {};
+
+ uint64_t current_offset = 0;
+ for (const auto& extent : old) {
+ // Check for overlapping extents - this would be a serious error.
+ if (current_offset > extent.offset) {
+ LOG(INFO) << "Overlapping extents detected; cannot layout temporary super image";
+ return false;
+ }
+
+ if (extent.offset != current_offset) {
+ uint64_t gap_size = extent.offset - current_offset;
+ extents->emplace_back(current_offset, gap_size, gap_type);
+ current_offset = extent.offset;
+ }
+
+ extents->emplace_back(extent);
+ current_offset += extent.size;
+ }
+ return true;
+}
+
+std::vector<SuperImageExtent> SuperLayoutBuilder::GetImageLayout() {
+ auto metadata = builder_->Export();
+ if (!metadata) {
+ return {};
+ }
+
+ std::vector<SuperImageExtent> extents;
+
+ // Write the primary and backup copies of geometry.
+ std::string geometry_bytes = SerializeGeometry(metadata->geometry);
+ auto blob = std::make_shared<std::string>(std::move(geometry_bytes));
+
+ extents.emplace_back(0, GetPrimaryGeometryOffset(), SuperImageExtent::Type::ZERO);
+ extents.emplace_back(GetPrimaryGeometryOffset(), blob);
+ extents.emplace_back(GetBackupGeometryOffset(), blob);
+
+ // Write the primary and backup copies of each metadata slot. When flashing,
+ // all metadata copies are the same, even for different slots.
+ std::string metadata_bytes = SerializeMetadata(*metadata.get());
+
+ // Align metadata size to 4KB. This makes the layout easily compatible with
+ // libsparse.
+ static constexpr size_t kSparseAlignment = 4096;
+ size_t metadata_aligned_bytes;
+ if (!AlignTo(metadata_bytes.size(), kSparseAlignment, &metadata_aligned_bytes)) {
+ LOG(ERROR) << "Unable to align metadata size " << metadata_bytes.size() << " to "
+ << kSparseAlignment;
+ return {};
+ }
+ metadata_bytes.resize(metadata_aligned_bytes, '\0');
+
+ // However, alignment can cause larger-than-supported metadata blocks. Fall
+ // back to fastbootd/update-super.
+ if (metadata_bytes.size() > metadata->geometry.metadata_max_size) {
+ LOG(VERBOSE) << "Aligned metadata size " << metadata_bytes.size()
+ << " is larger than maximum metadata size "
+ << metadata->geometry.metadata_max_size;
+ return {};
+ }
+
+ blob = std::make_shared<std::string>(std::move(metadata_bytes));
+ for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
+ int64_t metadata_primary = GetPrimaryMetadataOffset(metadata->geometry, i);
+ int64_t metadata_backup = GetBackupMetadataOffset(metadata->geometry, i);
+ extents.emplace_back(metadata_primary, blob);
+ extents.emplace_back(metadata_backup, blob);
+ }
+
+ // Add extents for each partition.
+ for (const auto& partition : metadata->partitions) {
+ auto partition_name = GetPartitionName(partition);
+ auto image_name_iter = image_map_.find(partition_name);
+ if (image_name_iter == image_map_.end()) {
+ if (partition.num_extents != 0) {
+ LOG(ERROR) << "Partition " << partition_name
+ << " has extents but no image filename";
+ return {};
+ }
+ continue;
+ }
+ const auto& image_name = image_name_iter->second;
+
+ uint64_t image_offset = 0;
+ for (uint32_t i = 0; i < partition.num_extents; i++) {
+ const auto& e = metadata->extents[partition.first_extent_index + i];
+
+ if (e.target_type != LP_TARGET_TYPE_LINEAR) {
+ // Any type other than LINEAR isn't understood here. We don't even
+ // bother with ZERO, which is never generated.
+ LOG(INFO) << "Unknown extent type from liblp: " << e.target_type;
+ return {};
+ }
+
+ size_t size = e.num_sectors * LP_SECTOR_SIZE;
+ uint64_t super_offset = e.target_data * LP_SECTOR_SIZE;
+ extents.emplace_back(super_offset, size, image_name, image_offset);
+
+ image_offset += size;
+ }
+ }
+
+ if (!AddGapExtents(&extents, SuperImageExtent::Type::DONTCARE)) {
+ return {};
+ }
+ return extents;
+}
+
+bool SuperImageExtent::operator==(const SuperImageExtent& other) const {
+ if (offset != other.offset) {
+ return false;
+ }
+ if (size != other.size) {
+ return false;
+ }
+ if (type != other.type) {
+ return false;
+ }
+ switch (type) {
+ case Type::DATA:
+ return *blob == *other.blob;
+ case Type::PARTITION:
+ return image_name == other.image_name && image_offset == other.image_offset;
+ default:
+ return true;
+ }
+}
+
+std::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent) {
+ stream << "extent:" << extent.offset << ":" << extent.size << ":";
+ switch (extent.type) {
+ case SuperImageExtent::Type::DATA:
+ stream << "data";
+ break;
+ case SuperImageExtent::Type::PARTITION:
+ stream << "partition:" << extent.image_name << ":" << extent.image_offset;
+ break;
+ case SuperImageExtent::Type::ZERO:
+ stream << "zero";
+ break;
+ case SuperImageExtent::Type::DONTCARE:
+ stream << "dontcare";
+ break;
+ default:
+ stream << "invalid";
+ }
+ return stream;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/super_layout_builder_test.cpp b/fs_mgr/liblp/super_layout_builder_test.cpp
new file mode 100644
index 0000000..714b6b4
--- /dev/null
+++ b/fs_mgr/liblp/super_layout_builder_test.cpp
@@ -0,0 +1,141 @@
+//
+// 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.
+//
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <liblp/super_layout_builder.h>
+#include <storage_literals/storage_literals.h>
+
+#include "images.h"
+#include "writer.h"
+
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+
+TEST(SuperImageTool, Layout) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_TRUE(tool.Open(*metadata.get()));
+ ASSERT_TRUE(tool.AddPartition("system_a", "system.img", 16_KiB));
+
+ // Get a copy of the metadata we'd expect if flashing.
+ ASSERT_TRUE(builder->ResizePartition(p, 16_KiB));
+ metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ auto geometry_blob = std::make_shared<std::string>(SerializeGeometry(metadata->geometry));
+ auto metadata_blob = std::make_shared<std::string>(SerializeMetadata(*metadata.get()));
+ metadata_blob->resize(4_KiB, '\0');
+
+ auto extents = tool.GetImageLayout();
+ ASSERT_EQ(extents.size(), 12);
+ EXPECT_EQ(extents[0], SuperImageExtent(0, 4096, SuperImageExtent::Type::ZERO));
+ EXPECT_EQ(extents[1], SuperImageExtent(4096, geometry_blob));
+ EXPECT_EQ(extents[2], SuperImageExtent(8192, geometry_blob));
+ EXPECT_EQ(extents[3], SuperImageExtent(12288, metadata_blob));
+ EXPECT_EQ(extents[4], SuperImageExtent(16384, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[5], SuperImageExtent(20480, metadata_blob));
+ EXPECT_EQ(extents[6], SuperImageExtent(24576, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[7], SuperImageExtent(28672, metadata_blob));
+ EXPECT_EQ(extents[8], SuperImageExtent(32768, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[9], SuperImageExtent(36864, metadata_blob));
+ EXPECT_EQ(extents[10], SuperImageExtent(40960, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[11], SuperImageExtent(45056, 16384, "system.img", 0));
+}
+
+TEST(SuperImageTool, NoWritablePartitions) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", 0);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoRetrofit) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ // Add an extra block device.
+ metadata->block_devices.emplace_back(metadata->block_devices[0]);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoRetrofit2) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition(
+ "system_a", LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoFixedPartitions) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 4_KiB));
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, LargeAlignedMetadata) {
+ auto builder = MetadataBuilder::New(4_MiB, 512, 2);
+ ASSERT_NE(builder, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_TRUE(tool.Open(*metadata.get()));
+
+ auto extents = tool.GetImageLayout();
+ ASSERT_TRUE(extents.empty());
+}
diff --git a/init/init.cpp b/init/init.cpp
index 4262191..f964c60 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -952,6 +952,8 @@
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
+ SelinuxSetupKernelLogging();
+
// Update $PATH in the case the second stage init is newer than first stage init, where it is
// first set.
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
@@ -1012,7 +1014,6 @@
MountExtraFilesystems();
// Now set up SELinux for second stage.
- SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
diff --git a/libcutils/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
index b9e2832..50bd6d0 100644
--- a/libcutils/sched_policy_test.cpp
+++ b/libcutils/sched_policy_test.cpp
@@ -75,9 +75,11 @@
}
ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+ ASSERT_EQ(0, set_cpuset_policy(0, SP_BACKGROUND));
AssertPolicy(SP_BACKGROUND);
ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+ ASSERT_EQ(0, set_cpuset_policy(0, SP_FOREGROUND));
AssertPolicy(SP_FOREGROUND);
}