Merge "Use libcutils_headers for android_filesystem_config.h"
diff --git a/adb/apex/apex_manifest.json b/adb/apex/apex_manifest.json
index 0444409..4a07bdc 100644
--- a/adb/apex/apex_manifest.json
+++ b/adb/apex/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.adbd",
- "version": 300000000
+ "version": 300900700
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index a663871..50d7364 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -584,12 +584,11 @@
incoming_header_ = msg;
} else {
size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
- Block payload = std::move(block->payload);
if (block->payload.size() > bytes_left) {
HandleError("received too many bytes while waiting for payload");
return false;
}
- incoming_payload_.append(std::move(payload));
+ incoming_payload_.append(std::move(block->payload));
}
if (incoming_header_->data_length == incoming_payload_.size()) {
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index ad10a1f..99cabdd 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -343,6 +343,12 @@
apex_available: [
"com.android.runtime",
],
+
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
}
cc_binary {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index d7cb972..c52c6f7 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -40,6 +40,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <cutils/sockets.h>
#include <log/log.h>
@@ -486,6 +487,17 @@
continue;
}
+#ifdef ANDROID_EXPERIMENTAL_MTE
+ struct iovec iov = {
+ &info.tagged_addr_ctrl,
+ sizeof(info.tagged_addr_ctrl),
+ };
+ if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TAGGED_ADDR_CTRL,
+ reinterpret_cast<void*>(&iov)) == -1) {
+ info.tagged_addr_ctrl = -1;
+ }
+#endif
+
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info);
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 108787e..5ed9e57 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -309,6 +309,11 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
+
+ if (mte_supported()) {
+ // Test that the default TAGGED_ADDR_CTRL value is set.
+ ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)");
+ }
}
TEST_F(CrasherTest, tagged_fault_addr) {
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 04c4b5c..30e75e1 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -23,6 +23,7 @@
struct ThreadInfo {
std::unique_ptr<unwindstack::Regs> registers;
+ long tagged_addr_ctrl = -1;
pid_t uid;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 7af99c9..e1fe82b 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -180,6 +180,9 @@
_LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid,
thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
_LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
+ if (thread_info.tagged_addr_ctrl != -1) {
+ _LOG(log, logtype::HEADER, "tagged_addr_ctrl: %016lx\n", thread_info.tagged_addr_ctrl);
+ }
}
static std::string get_addr_string(uint64_t addr) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index bdf1da6..0bb1b87 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -152,6 +152,7 @@
"liblog",
],
static_libs: [
+ "libbrotli",
"libz",
],
ramdisk_available: true,
@@ -362,10 +363,11 @@
static_libs: [
"libbase",
+ "libbrotli",
"liblog",
"libdm",
- "libz",
- "libsnapshot_cow",
+ "libz",
+ "libsnapshot_cow",
],
}
@@ -403,12 +405,14 @@
"libz",
],
static_libs: [
+ "libbrotli",
"libgtest",
"libsnapshot_cow",
],
test_min_api_level: 30,
auto_gen_config: true,
require_root: false,
+ host_supported: true,
}
cc_binary {
@@ -494,11 +498,12 @@
shared_libs: [
"libbase",
"liblog",
- "libz",
],
static_libs: [
+ "libbrotli",
"libgtest",
"libsnapshot_cow",
+ "libz",
],
header_libs: [
"libstorage_literals_headers",
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index d98fe59..40d5efb 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -30,12 +30,12 @@
class CowTest : public ::testing::Test {
protected:
- void SetUp() override {
+ virtual void SetUp() override {
cow_ = std::make_unique<TemporaryFile>();
ASSERT_GE(cow_->fd, 0) << strerror(errno);
}
- void TearDown() override { cow_ = nullptr; }
+ virtual void TearDown() override { cow_ = nullptr; }
std::unique_ptr<TemporaryFile> cow_;
};
@@ -70,7 +70,7 @@
ASSERT_TRUE(writer.AddCopy(10, 20));
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -105,7 +105,7 @@
ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(op->source, 104);
+ ASSERT_EQ(op->source, 106);
ASSERT_TRUE(reader.ReadData(*op, &sink));
ASSERT_EQ(sink.stream(), data);
@@ -145,7 +145,7 @@
data.resize(options.block_size, '\0');
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -163,7 +163,7 @@
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(op->source, 104);
+ ASSERT_EQ(op->source, 106);
ASSERT_TRUE(reader.ReadData(*op, &sink));
ASSERT_EQ(sink.stream(), data);
@@ -182,7 +182,7 @@
data.resize(options.block_size * 2, '\0');
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -211,9 +211,11 @@
void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
};
-TEST_F(CowTest, HorribleSink) {
+class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
+
+TEST_P(CompressionTest, HorribleSink) {
CowOptions options;
- options.compression = "gz";
+ options.compression = GetParam();
CowWriter writer(options);
ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -222,7 +224,7 @@
data.resize(options.block_size, '\0');
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -239,6 +241,8 @@
ASSERT_EQ(sink.stream(), data);
}
+INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
+
TEST_F(CowTest, GetSize) {
CowOptions options;
CowWriter writer(options);
@@ -255,7 +259,7 @@
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
auto size_before = writer.GetCowSize();
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
auto size_after = writer.GetCowSize();
ASSERT_EQ(size_before, size_after);
struct stat buf;
@@ -267,6 +271,60 @@
ASSERT_EQ(buf.st_size, writer.GetCowSize());
}
+TEST_F(CowTest, Append) {
+ CowOptions options;
+ auto writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd, CowWriter::OpenMode::APPEND));
+
+ std::string data2 = "More data!";
+ data2.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+ ASSERT_TRUE(writer->Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ struct stat buf;
+ ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+ ASSERT_EQ(buf.st_size, writer->GetCowSize());
+
+ // Read back both operations.
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ StringSink sink;
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ sink.Reset();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data2);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp
index f480b85..faceafe 100644
--- a/fs_mgr/libsnapshot/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/cow_decompress.cpp
@@ -19,6 +19,7 @@
#include <utility>
#include <android-base/logging.h>
+#include <brotli/decode.h>
#include <zlib.h>
namespace android {
@@ -207,5 +208,57 @@
return std::unique_ptr<IDecompressor>(new GzDecompressor());
}
+class BrotliDecompressor final : public StreamDecompressor {
+ public:
+ ~BrotliDecompressor();
+
+ bool Init() override;
+ bool DecompressInput(const uint8_t* data, size_t length) override;
+ bool Done() override { return BrotliDecoderIsFinished(decoder_); }
+
+ private:
+ BrotliDecoderState* decoder_ = nullptr;
+};
+
+bool BrotliDecompressor::Init() {
+ decoder_ = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+ return true;
+}
+
+BrotliDecompressor::~BrotliDecompressor() {
+ if (decoder_) {
+ BrotliDecoderDestroyInstance(decoder_);
+ }
+}
+
+bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+ size_t available_in = length;
+ const uint8_t* next_in = data;
+
+ bool needs_more_output = false;
+ while (available_in || needs_more_output) {
+ if (!output_buffer_remaining_ && !GetFreshBuffer()) {
+ return false;
+ }
+
+ auto output_buffer = output_buffer_;
+ auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
+ &output_buffer_remaining_, &output_buffer_, nullptr);
+ if (r == BROTLI_DECODER_RESULT_ERROR) {
+ LOG(ERROR) << "brotli decode failed";
+ return false;
+ }
+ if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
+ return false;
+ }
+ needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
+ }
+ return true;
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
+ return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h
index 1c8c40d..f485256 100644
--- a/fs_mgr/libsnapshot/cow_decompress.h
+++ b/fs_mgr/libsnapshot/cow_decompress.h
@@ -40,6 +40,7 @@
// Factory methods for decompression methods.
static std::unique_ptr<IDecompressor> Uncompressed();
static std::unique_ptr<IDecompressor> Gz();
+ static std::unique_ptr<IDecompressor> Brotli();
// |output_bytes| is the expected total number of bytes to sink.
virtual bool Decompress(size_t output_bytes) = 0;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 1aea3a9..60093ab 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -78,6 +78,11 @@
<< "Expected: " << kCowMagicNumber;
return false;
}
+ if (header_.header_size != sizeof(CowHeader)) {
+ LOG(ERROR) << "Header size unknown, read " << header_.header_size << ", expected "
+ << sizeof(CowHeader);
+ return false;
+ }
if ((header_.major_version != kCowVersionMajor) ||
(header_.minor_version != kCowVersionMinor)) {
@@ -233,6 +238,9 @@
case kCowCompressGz:
decompressor = IDecompressor::Gz();
break;
+ case kCowCompressBrotli:
+ decompressor = IDecompressor::Brotli();
+ break;
default:
LOG(ERROR) << "Unknown compression type: " << op.compression;
return false;
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 6970ec1..80acb4a 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -162,7 +162,7 @@
ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2_.get(), size_));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
}
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 76238c2..4cf2119 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -22,6 +22,8 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <brotli/encode.h>
+#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
#include <zlib.h>
@@ -39,17 +41,48 @@
header_.magic = kCowMagicNumber;
header_.major_version = kCowVersionMajor;
header_.minor_version = kCowVersionMinor;
+ header_.header_size = sizeof(CowHeader);
header_.block_size = options_.block_size;
}
-bool CowWriter::Initialize(android::base::unique_fd&& fd) {
- owned_fd_ = std::move(fd);
- return Initialize(android::base::borrowed_fd{owned_fd_});
+bool CowWriter::ParseOptions() {
+ if (options_.compression == "gz") {
+ compression_ = kCowCompressGz;
+ } else if (options_.compression == "brotli") {
+ compression_ = kCowCompressBrotli;
+ } else if (options_.compression == "none") {
+ compression_ = kCowCompressNone;
+ } else if (!options_.compression.empty()) {
+ LOG(ERROR) << "unrecognized compression: " << options_.compression;
+ return false;
+ }
+ return true;
}
-bool CowWriter::Initialize(android::base::borrowed_fd fd) {
+bool CowWriter::Initialize(android::base::unique_fd&& fd, OpenMode mode) {
+ owned_fd_ = std::move(fd);
+ return Initialize(android::base::borrowed_fd{owned_fd_}, mode);
+}
+
+bool CowWriter::Initialize(android::base::borrowed_fd fd, OpenMode mode) {
fd_ = fd;
+ if (!ParseOptions()) {
+ return false;
+ }
+
+ switch (mode) {
+ case OpenMode::WRITE:
+ return OpenForWrite();
+ case OpenMode::APPEND:
+ return OpenForAppend();
+ default:
+ LOG(ERROR) << "Unknown open mode in CowWriter";
+ return false;
+ }
+}
+
+bool CowWriter::OpenForWrite() {
// This limitation is tied to the data field size in CowOperation.
if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Block size is too large";
@@ -61,31 +94,52 @@
return false;
}
- if (options_.compression == "gz") {
- compression_ = kCowCompressGz;
- } else if (!options_.compression.empty()) {
- LOG(ERROR) << "unrecognized compression: " << options_.compression;
+ // Headers are not complete, but this ensures the file is at the right
+ // position.
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write failed";
return false;
}
- // Headers are not complete, but this ensures the file is at the right
- // position.
- if (!WriteFully(fd_, &header_, sizeof(header_))) {
- PLOG(ERROR) << "write failed";
+ header_.ops_offset = header_.header_size;
+ return true;
+}
+
+bool CowWriter::OpenForAppend() {
+ auto reader = std::make_unique<CowReader>();
+ if (!reader->Parse(fd_) || !reader->GetHeader(&header_)) {
+ return false;
+ }
+ options_.block_size = header_.block_size;
+
+ // Reset this, since we're going to reimport all operations.
+ header_.num_ops = 0;
+
+ auto iter = reader->GetOpIter();
+ while (!iter->Done()) {
+ auto& op = iter->Get();
+ AddOperation(op);
+
+ iter->Next();
+ }
+
+ // Free reader so we own the descriptor position again.
+ reader = nullptr;
+
+ // Seek to the end of the data section.
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
return false;
}
return true;
}
bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
- header_.num_ops++;
-
CowOperation op = {};
op.type = kCowCopyOp;
op.new_block = new_block;
op.source = old_block;
- ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
-
+ AddOperation(op);
return true;
}
@@ -103,8 +157,6 @@
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
for (size_t i = 0; i < size / header_.block_size; i++) {
- header_.num_ops++;
-
CowOperation op = {};
op.type = kCowReplaceOp;
op.new_block = new_block_start + i;
@@ -120,7 +172,7 @@
LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
return false;
}
- if (!WriteFully(fd_, data.data(), data.size())) {
+ if (!WriteRawData(data.data(), data.size())) {
PLOG(ERROR) << "AddRawBlocks: write failed";
return false;
}
@@ -132,11 +184,11 @@
pos += header_.block_size;
}
- ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+ AddOperation(op);
iter += header_.block_size;
}
- if (!compression_ && !WriteFully(fd_, data, size)) {
+ if (!compression_ && !WriteRawData(data, size)) {
PLOG(ERROR) << "AddRawBlocks: write failed";
return false;
}
@@ -145,13 +197,11 @@
bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
for (uint64_t i = 0; i < num_blocks; i++) {
- header_.num_ops++;
-
CowOperation op = {};
op.type = kCowZeroOp;
op.new_block = new_block_start + i;
op.source = 0;
- ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+ AddOperation(op);
}
return true;
}
@@ -171,6 +221,24 @@
}
return std::basic_string<uint8_t>(buffer.get(), dest_len);
}
+ case kCowCompressBrotli: {
+ auto bound = BrotliEncoderMaxCompressedSize(length);
+ if (!bound) {
+ LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
+ return {};
+ }
+ auto buffer = std::make_unique<uint8_t[]>(bound);
+
+ size_t encoded_size = bound;
+ auto rv = BrotliEncoderCompress(
+ BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
+ reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.get());
+ if (!rv) {
+ LOG(ERROR) << "BrotliEncoderCompress failed";
+ return {};
+ }
+ return std::basic_string<uint8_t>(buffer.get(), encoded_size);
+ }
default:
LOG(ERROR) << "unhandled compression type: " << compression_;
break;
@@ -189,17 +257,7 @@
#endif
}
-bool CowWriter::Finalize() {
- // If both fields are set then Finalize is already called.
- if (header_.ops_offset > 0 && header_.ops_size > 0) {
- return true;
- }
- auto offs = lseek(fd_.get(), 0, SEEK_CUR);
- if (offs < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
- header_.ops_offset = offs;
+bool CowWriter::Flush() {
header_.ops_size = ops_.size();
memset(header_.ops_checksum, 0, sizeof(uint8_t) * 32);
@@ -212,8 +270,6 @@
PLOG(ERROR) << "lseek failed";
return false;
}
- // Header is already written, calling WriteFully will increment
- // bytes_written_. So use android::base::WriteFully() here.
if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
PLOG(ERROR) << "write header failed";
return false;
@@ -227,13 +283,16 @@
return false;
}
- // clear ops_ so that subsequent calls to GetSize() still works.
- ops_.clear();
+ // Re-position for any subsequent writes.
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
return true;
}
size_t CowWriter::GetCowSize() {
- return bytes_written_ + ops_.size() * sizeof(ops_[0]);
+ return header_.ops_offset + header_.num_ops * sizeof(CowOperation);
}
bool CowWriter::GetDataPos(uint64_t* pos) {
@@ -246,9 +305,17 @@
return true;
}
-bool CowWriter::WriteFully(base::borrowed_fd fd, const void* data, size_t size) {
- bytes_written_ += size;
- return android::base::WriteFully(fd, data, size);
+void CowWriter::AddOperation(const CowOperation& op) {
+ header_.num_ops++;
+ ops_.insert(ops_.size(), reinterpret_cast<const uint8_t*>(&op), sizeof(op));
+}
+
+bool CowWriter::WriteRawData(const void* data, size_t size) {
+ if (!android::base::WriteFully(fd_, data, size)) {
+ return false;
+ }
+ header_.ops_offset += size;
+ return true;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
index 45833e1..2a0136b 100644
--- a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
+++ b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
@@ -375,7 +375,7 @@
}
}
- if (!writer->Finalize()) {
+ if (!writer->Flush()) {
return false;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 6d500e7..4a6bd4e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -45,6 +45,9 @@
uint16_t major_version;
uint16_t minor_version;
+ // Size of this struct.
+ uint16_t header_size;
+
// Offset to the location of the operation sequence, and size of the
// operation sequence buffer. |ops_offset| is also the end of the
// raw data region.
@@ -98,6 +101,7 @@
static constexpr uint8_t kCowCompressNone = 0;
static constexpr uint8_t kCowCompressGz = 1;
+static constexpr uint8_t kCowCompressBrotli = 2;
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 8826b7a..8569161 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -47,12 +47,11 @@
// Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
- // Finalize all COW operations and flush pending writes.
- // Return true if successful.
- virtual bool Finalize() = 0;
+ // Flush all pending writes. This must be called before closing the writer
+ // to ensure that the correct headers and footers are written.
+ virtual bool Flush() = 0;
- // Return 0 if failed, on success return number of bytes the cow image would be
- // after calling Finalize();
+ // Return number of bytes the cow image occupies on disk.
virtual size_t GetCowSize() = 0;
protected:
@@ -61,24 +60,30 @@
class CowWriter : public ICowWriter {
public:
+ enum class OpenMode { WRITE, APPEND };
+
explicit CowWriter(const CowOptions& options);
// Set up the writer.
- bool Initialize(android::base::unique_fd&& fd);
- bool Initialize(android::base::borrowed_fd fd);
+ bool Initialize(android::base::unique_fd&& fd, OpenMode mode = OpenMode::WRITE);
+ bool Initialize(android::base::borrowed_fd fd, OpenMode mode = OpenMode::WRITE);
bool AddCopy(uint64_t new_block, uint64_t old_block) override;
bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- bool Finalize() override;
+ bool Flush() override;
size_t GetCowSize() override;
private:
void SetupHeaders();
+ bool ParseOptions();
+ bool OpenForWrite();
+ bool OpenForAppend();
bool GetDataPos(uint64_t* pos);
- bool WriteFully(base::borrowed_fd fd, const void* data, size_t size);
+ bool WriteRawData(const void* data, size_t size);
+ void AddOperation(const CowOperation& op);
std::basic_string<uint8_t> Compress(const void* data, size_t length);
private:
@@ -90,7 +95,6 @@
// :TODO: this is not efficient, but stringstream ubsan aborts because some
// bytes overflow a signed char.
std::basic_string<uint8_t> ops_;
- std::atomic<size_t> bytes_written_ = 0;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
index d0b8f52..f761077 100644
--- a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
+++ b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
@@ -204,7 +204,7 @@
}
}
- if (!writer_->Finalize()) {
+ if (!writer_->Flush()) {
LOG(ERROR) << "Unable to finalize COW for " << partition_name;
return false;
}
diff --git a/include/sysutils b/include/sysutils
deleted file mode 120000
index 1c8e85b..0000000
--- a/include/sysutils
+++ /dev/null
@@ -1 +0,0 @@
-../libsysutils/include/sysutils/
\ No newline at end of file
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 053ebf8..0f584b2 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -86,6 +86,8 @@
for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the
kernel.
+`/apex/*/firmware` is also searched after a list of firmware directories.
+
The list of firmware directories is customized by a `firmware_directories` line in a ueventd.rc
file. This line takes the format of
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index dff7b69..b50b4ef 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -17,6 +17,7 @@
#include "firmware_handler.h"
#include <fcntl.h>
+#include <glob.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
@@ -30,6 +31,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -203,25 +205,28 @@
}
std::vector<std::string> attempted_paths_and_errors;
-
- int booting = IsBooting();
-try_loading_again:
- attempted_paths_and_errors.clear();
- for (const auto& firmware_directory : firmware_directories_) {
+ auto TryLoadFirmware = [&](const std::string& firmware_directory) {
std::string file = firmware_directory + firmware;
unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
if (fw_fd == -1) {
attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
", open failed: " + strerror(errno));
- continue;
+ return false;
}
struct stat sb;
if (fstat(fw_fd, &sb) == -1) {
attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
", fstat failed: " + strerror(errno));
- continue;
+ return false;
}
LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return true;
+ };
+
+ int booting = IsBooting();
+try_loading_again:
+ attempted_paths_and_errors.clear();
+ if (ForEachFirmwareDirectory(TryLoadFirmware)) {
return;
}
@@ -242,6 +247,33 @@
write(loading_fd, "-1", 2);
}
+bool FirmwareHandler::ForEachFirmwareDirectory(
+ std::function<bool(const std::string&)> handler) const {
+ for (const std::string& firmware_directory : firmware_directories_) {
+ if (std::invoke(handler, firmware_directory)) {
+ return true;
+ }
+ }
+
+ glob_t glob_result;
+ glob("/apex/*/firmware/", GLOB_MARK, nullptr, &glob_result);
+ auto free_glob = android::base::make_scope_guard(std::bind(&globfree, &glob_result));
+ for (size_t i = 0; i < glob_result.gl_pathc; i++) {
+ char* apex_firmware_directory = glob_result.gl_pathv[i];
+ // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will look into the
+ // same apex twice.
+ if (strchr(apex_firmware_directory, '@')) {
+ continue;
+ }
+ if (std::invoke(handler, apex_firmware_directory)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void FirmwareHandler::HandleUevent(const Uevent& uevent) {
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index b4138f1..8b758ae 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -18,6 +18,7 @@
#include <pwd.h>
+#include <functional>
#include <string>
#include <vector>
@@ -52,6 +53,7 @@
const Uevent& uevent) const;
std::string GetFirmwarePath(const Uevent& uevent) const;
void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
+ bool ForEachFirmwareDirectory(std::function<bool(const std::string&)> handler) const;
std::vector<std::string> firmware_directories_;
std::vector<ExternalFirmwareHandler> external_firmware_handlers_;
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 9c1c463..8f15541 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -43,6 +43,7 @@
"//apex_available:anyapex",
],
min_sdk_version: "29",
+ sdk_version: "minimum",
native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index c9e245d..26d9f65 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -141,15 +141,14 @@
return false;
}
- uint16_t data2;
- if (!elf_memory->ReadFully(elf_offset + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+ uint8_t data2;
+ if (!elf_memory->ReadFully(elf_offset + 8, &data2, sizeof(data2)) || data2 != 0x05) {
return false;
}
// __restore_rt:
// 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00 mov $0xf,%rax
// 0x0f 0x05 syscall
- // 0x0f nopl 0x0($rax)
// Read the mcontext data from the stack.
// sp points to the ucontext data structure, read only the mcontext part.