Merge "Removed RWLock fuzzer"
diff --git a/adb/README.md b/adb/README.md
new file mode 100644
index 0000000..224387c
--- /dev/null
+++ b/adb/README.md
@@ -0,0 +1,94 @@
+# ADB Internals
+
+If you are new to adb source code, you should start by reading [OVERVIEW.TXT](OVERVIEW.TXT) which describes the three components of adb pipeline.
+
+This document is here to boost what can be achieved within a "window of naive interest". You will not find function or class documentation here but rather the "big picture" which should allow you to build a mental map to help navigate the code.
+
+## Three components of adb pipeline
+
+As outlined in the overview, this codebase generates three components (Client, Server (a.k.a Host), and Daemon (a.k.a adbd)). The central part is the Server which runs on the Host computer. On one side the Server exposes a "Smart Socket" to Clients such as adb or DDMLIB. On the other side, the Server continuously monitors for connecting Daemons (as USB devices or TCP emulator). Communication with a device is done with a Transport.
+
+```
++----------+ +------------------------+
+| ADB +----------+ | ADB SERVER | +----------+
+| CLIENT | | | | (USB)| ADBD |
++----------+ | | Transport+-------------+ (DEVICE) |
+ | | | +----------+
++----------- | | |
+| ADB | v + | +----------+
+| CLIENT +--------->SmartSocket | (USB)| ADBD |
++----------+ ^ | (TCP/IP) Transport+-------------+ (DEVICE) |
+ | | | +----------+
++----------+ | | |
+| DDMLIB | | | Transport+--+ +----------+
+| CLIENT +----------+ | | | (TCP/IP)| ADBD |
++----------+ +------------------------+ +----------|(EMULATOR)|
+ +----------+
+```
+
+The Client and the Server are contained in the same executable and both run on the Host machine. Code sections specific to the Host is enclosed within `ADB_HOST` guard. adbd runs on the Android Device. Daemon specific code is enclosed in `!ADB_HOST` but also sometimes with-in `__ANDROID__` guard.
+
+
+## "SMART SOCKET" and TRANSPORT
+
+A smart socket is a simple TCP socket with a smart protocol built on top of it. This is what Clients connect onto from the Host side. The Client must always initiate communication via a human readable request but the response format varies. The smart protocol is documented in [SERVICES.TXT](SERVICES.TXT).
+
+On the other side, the Server communicate with a device via a Transport. adb initially targeted devices connecting over USB, which is restricted to a fixed number of data streams. Therefore, adb multiplexes multiple byte streams over a single pipe via Transport. When devices connecting over other mechanisms (e.g. emulators over TCP) were introduced, the existing transport protocol was maintained.
+
+## THREADING MODEL and FDEVENT system
+
+At the heart of both the Server and Daemon is a main thread running an fdevent loop, which is an platform-independent abstraction over poll/epoll/WSAPoll monitoring file descriptors events. Requests and services are usually server from the main thread but some service requests result in new threads being spawned.
+
+To allow for operations to run on the Main thread, fdevent features a RunQueue combined with an interrupt fd to force polling to return.
+
+```
++------------+ +-------------------------^
+| RUNQUEUE | | |
++------------+ | POLLING (Main thread) |
+| Function<> | | |
++------------+ | |
+| Function<> | ^-^-------^-------^------^^
++------------+ | | | |
+| ... | | | | |
++------------+ | | | |
+| | | | | |
+|============| | | | |
+|Interrupt fd+------+ +----+ +----+ +----+
++------------+ fd Socket Pipe
+```
+
+## ASOCKET, APACKET, and AMESSAGE
+
+The asocket, apacket, and amessage constructs exist only to wrap data while it transits on a Transport. An asocket handles a stream of apackets. An apacket consists in a amessage header featuring a command (`A_SYNC`, `A_OPEN`, `A_CLSE`, `A_WRTE`, `A_OKAY`, ...) followed by a payload (find more documentation in [protocol.txt](protocol.txt). There is no `A_READ` command because an asocket is unidirectional. To model a bi-directional stream, asocket have a peer which go in the opposite direction.
+
+An asocket features a buffer where the elemental unit is an apacket. Is traffic is inbound, the buffer stores apacket until they are consumed. If the traffic is oubound, the buffer store apackets until they are sent down the wire (with `A_WRTE` commands).
+
+```
++---------------------ASocket------------------------+
+ | |
+ | +----------------APacket Queue------------------+ |
+ | | | |
+ | | APacket APacket APacket | |
+ | | +--------+ +--------+ +--------+ | |
+ | | |AMessage| |AMessage| |AMessage| | |
+ | | +--------+ +--------+ +--------+ | |
+ | | | | | | | | | |
+ | | ..... | | | | | | | |
+ | | | Data | | Data | | Data | | |
+ | | | | | | | | | |
+ | | | | | | | | | |
+ | | +--------+ +--------+ +--------+ | |
+ | | | |
+ | +-----------------------------------------------+ |
+ +---------------------------------------------------+
+```
+
+This system allows to multiplex data streams on an unique byte stream. Without entering too much into details, the amessage fields arg1 and arg2 are used alike in the TCP protocol where local and remote ports identify an unique stream. Note that unlike TCP which feature an "unacknowledged-send window", an apacket is sent only after the previous one has been confirmed to be received.
+
+The two types of asocket (Remote and Local) differentiate between outbound and inbound traffic.
+
+## adbd <-> APPPLICATION communication
+
+This pipeline is detailed in [services.cpp](services.cpp). The JDWP extension implemented by Dalvik/ART are documented in:
+- platform/dalvik/+/master/docs/debugmon.html
+- platform/dalvik/+/master/docs/debugger.html
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/client/incremental_utils.cpp b/adb/client/incremental_utils.cpp
index 3297a6d..1a071fd 100644
--- a/adb/client/incremental_utils.cpp
+++ b/adb/client/incremental_utils.cpp
@@ -326,7 +326,7 @@
}
std::vector<int32_t> installationPriorityBlocks;
- ZipEntry entry;
+ ZipEntry64 entry;
std::string_view entryName;
while (Next(cookie, &entry, &entryName) == 0) {
if (entryName == "classes.dex"sv) {
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/fastboot/fastboot.bash b/fastboot/fastboot.bash
index a46f420..406e8b8 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $i -eq $COMP_CWORD ]]; then
- partitions="boot bootloader dtbo modem modules odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm"
+ partitions="boot bootloader dtbo modem odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm"
COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
else
_fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3969367..4bf791e 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -144,7 +144,6 @@
{ "cache", "cache.img", "cache.sig", "cache", true, ImageType::Extra },
{ "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, ImageType::BootCritical },
{ "dts", "dt.img", "dt.sig", "dts", true, ImageType::BootCritical },
- { "modules", "modules.img", "modules.sig", "modules", true, ImageType::Normal },
{ "odm", "odm.img", "odm.sig", "odm", true, ImageType::Normal },
{ "odm_dlkm", "odm_dlkm.img", "odm_dlkm.sig", "odm_dlkm", true, ImageType::Normal },
{ "product", "product.img", "product.sig", "product", true, ImageType::Normal },
@@ -527,12 +526,15 @@
static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
std::vector<char>* out) {
- ZipEntry zip_entry;
+ ZipEntry64 zip_entry;
if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
return false;
}
+ if (zip_entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
+ die("entry '%s' is too large: %" PRIu64, entry_name.c_str(), zip_entry.uncompressed_length);
+ }
out->resize(zip_entry.uncompressed_length);
fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
@@ -638,14 +640,14 @@
static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
- ZipEntry zip_entry;
+ ZipEntry64 zip_entry;
if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
errno = ENOENT;
return -1;
}
- fprintf(stderr, "extracting %s (%" PRIu32 " MB) to disk...", entry_name,
+ fprintf(stderr, "extracting %s (%" PRIu64 " MB) to disk...", entry_name,
zip_entry.uncompressed_length / 1024 / 1024);
double start = now();
int error = ExtractEntryToFile(zip, &zip_entry, fd);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 7912688..bb7d8b3 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -427,6 +427,20 @@
return true;
}
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns its UUID.
+bool DeviceMapper::GetDmDeviceUuidByName(const std::string& name, std::string* uuid) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
+ return false;
+ }
+
+ *uuid = std::string(io.uuid);
+ return true;
+}
+
bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
struct dm_ioctl io;
InitIo(&io, name);
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index abe9c4c..5d6db46 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -172,6 +172,13 @@
// could race with ueventd.
bool GetDmDevicePathByName(const std::string& name, std::string* path);
+ // Returns the device mapper UUID for a given name. If the device does not
+ // exist, false is returned, and the path parameter is not set.
+ //
+ // WaitForFile() should not be used in conjunction with this call, since it
+ // could race with ueventd.
+ bool GetDmDeviceUuidByName(const std::string& name, std::string* path);
+
// Returns a device's unique path as generated by ueventd. This will return
// true as long as the device has been created, even if ueventd has not
// processed it yet.
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index bdf1da6..aa41be3 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -30,6 +30,7 @@
static_libs: [
"libdm",
"libfstab",
+ "libsnapshot_cow",
"update_metadata-protos",
],
whole_static_libs: [
@@ -38,7 +39,9 @@
"libfstab",
],
header_libs: [
+ "libchrome",
"libfiemap_headers",
+ "libupdate_engine_headers",
],
export_static_lib_headers: [
"update_metadata-protos",
@@ -152,6 +155,7 @@
"liblog",
],
static_libs: [
+ "libbrotli",
"libz",
],
ramdisk_available: true,
@@ -312,8 +316,10 @@
"libprotobuf-mutator",
],
header_libs: [
+ "libchrome",
"libfiemap_headers",
"libstorage_literals_headers",
+ "libupdate_engine_headers",
],
proto: {
type: "full",
@@ -362,10 +368,11 @@
static_libs: [
"libbase",
+ "libbrotli",
"liblog",
"libdm",
- "libz",
- "libsnapshot_cow",
+ "libz",
+ "libsnapshot_cow",
],
}
@@ -403,12 +410,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 +503,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 d767022..80acb4a 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -37,52 +37,86 @@
class SnapuserdTest : public ::testing::Test {
protected:
void SetUp() override {
- cow_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(cow_->fd, 0) << strerror(errno);
+ cow_system_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_system_->fd, 0) << strerror(errno);
+
+ cow_product_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_product_->fd, 0) << strerror(errno);
+
+ size_ = 100_MiB;
}
- void TearDown() override { cow_ = nullptr; }
+ void TearDown() override {
+ cow_system_ = nullptr;
+ cow_product_ = nullptr;
+ }
- std::unique_ptr<TemporaryFile> cow_;
+ std::unique_ptr<TemporaryFile> cow_system_;
+ std::unique_ptr<TemporaryFile> cow_product_;
+
+ unique_fd sys_fd_;
+ unique_fd product_fd_;
+ size_t size_;
+
+ int system_blksize_;
+ int product_blksize_;
+ std::string system_device_name_;
+ std::string product_device_name_;
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_;
+ std::unique_ptr<uint8_t[]> random_buffer_2_;
+ std::unique_ptr<uint8_t[]> zero_buffer_;
+ std::unique_ptr<uint8_t[]> system_buffer_;
+ std::unique_ptr<uint8_t[]> product_buffer_;
+
+ void Init();
+ void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
+ void CreateSystemDmUser();
+ void CreateProductDmUser();
+ void StartSnapuserdDaemon();
+ void CreateSnapshotDevices();
+
+ void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf);
};
-TEST_F(SnapuserdTest, ReadWrite) {
- loff_t offset = 0;
- size_t size = 100_MiB;
+void SnapuserdTest::Init() {
unique_fd rnd_fd;
- unique_fd sys_fd;
- unique_fd snapshot_fd;
- unique_fd system_a_fd;
- std::string cmd;
+ loff_t offset = 0;
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
- std::unique_ptr<uint8_t[]> random_buffer_1;
- std::unique_ptr<uint8_t[]> random_buffer_2;
- std::unique_ptr<uint8_t[]> system_buffer;
-
- random_buffer_1 = std::make_unique<uint8_t[]>(size);
-
- random_buffer_2 = std::make_unique<uint8_t[]>(size);
-
- system_buffer = std::make_unique<uint8_t[]>(size);
+ random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+ random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
+ system_buffer_ = std::make_unique<uint8_t[]>(size_);
+ product_buffer_ = std::make_unique<uint8_t[]>(size_);
+ zero_buffer_ = std::make_unique<uint8_t[]>(size_);
// Fill random data
- for (size_t j = 0; j < (size / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1.get() + offset, 1_MiB, 0), true);
+ for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+ true);
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2.get() + offset, 1_MiB, 0), true);
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2_.get() + offset, 1_MiB, 0),
+ true);
offset += 1_MiB;
}
- sys_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
- ASSERT_TRUE(sys_fd > 0);
+ sys_fd_.reset(open("/dev/block/mapper/system_a", O_RDONLY));
+ ASSERT_TRUE(sys_fd_ > 0);
+
+ product_fd_.reset(open("/dev/block/mapper/product_a", O_RDONLY));
+ ASSERT_TRUE(product_fd_ > 0);
// Read from system partition from offset 0 of size 100MB
- ASSERT_EQ(ReadFullyAtOffset(sys_fd, system_buffer.get(), size, 0), true);
+ ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);
+ // Read from system partition from offset 0 of size 100MB
+ ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
+}
+
+void SnapuserdTest::CreateCowDevice(std::unique_ptr<TemporaryFile>& cow) {
//================Create a COW file with the following operations===========
//
// Create COW file which is gz compressed
@@ -96,12 +130,12 @@
options.compression = "gz";
CowWriter writer(options);
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize(cow->fd));
// Write 100MB random data to COW file which is gz compressed from block 0
- ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1.get(), size));
+ ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), size_));
- size_t num_blocks = size / options.block_size;
+ size_t num_blocks = size_ / options.block_size;
size_t blk_start_copy = num_blocks;
size_t blk_end_copy = blk_start_copy + num_blocks;
size_t source_blk = 0;
@@ -110,7 +144,7 @@
// has to read from block 0 in system_a partition
//
// This initializes copy operation from block 0 of size 100 MB from
- // /dev/block/mapper/system_a
+ // /dev/block/mapper/system_a or product_a
for (size_t i = blk_start_copy; i < blk_end_copy; i++) {
ASSERT_TRUE(writer.AddCopy(i, source_blk));
source_blk += 1;
@@ -125,14 +159,17 @@
// Final 100MB filled with random data which is gz compressed
size_t blk_random2_replace_start = blk_zero_copy_end;
- ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2.get(), size));
+ 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);
+ ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
+}
- //================Setup dm-snapshot and start snapuserd daemon===========
+void SnapuserdTest::CreateSystemDmUser() {
+ unique_fd system_a_fd;
+ std::string cmd;
// Create a COW device. Number of sectors is chosen random which can
// hold at least 400MB of data
@@ -140,39 +177,77 @@
system_a_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
ASSERT_TRUE(system_a_fd > 0);
- int blksize;
- int err = ioctl(system_a_fd.get(), BLKGETSIZE, &blksize);
- if (err < 0) {
- ASSERT_TRUE(0);
- }
+ int err = ioctl(system_a_fd.get(), BLKGETSIZE, &system_blksize_);
+ ASSERT_GE(err, 0);
- cmd = "dmctl create system_cow user 0 " + std::to_string(blksize);
+ std::string str(cow_system_->path);
+ std::size_t found = str.find_last_of("/\\");
+ ASSERT_NE(found, std::string::npos);
+ system_device_name_ = str.substr(found + 1);
+ cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_);
+
system(cmd.c_str());
+}
+void SnapuserdTest::CreateProductDmUser() {
+ unique_fd product_a_fd;
+ std::string cmd;
+
+ // Create a COW device. Number of sectors is chosen random which can
+ // hold at least 400MB of data
+
+ product_a_fd.reset(open("/dev/block/mapper/product_a", O_RDONLY));
+ ASSERT_TRUE(product_a_fd > 0);
+
+ int err = ioctl(product_a_fd.get(), BLKGETSIZE, &product_blksize_);
+ ASSERT_GE(err, 0);
+
+ std::string str(cow_product_->path);
+ std::size_t found = str.find_last_of("/\\");
+ ASSERT_NE(found, std::string::npos);
+ product_device_name_ = str.substr(found + 1);
+ cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_);
+
+ system(cmd.c_str());
+}
+
+void SnapuserdTest::StartSnapuserdDaemon() {
// Start the snapuserd daemon
if (fork() == 0) {
- const char* argv[] = {"/system/bin/snapuserd", cow_->path, "/dev/block/mapper/system_a",
- nullptr};
+ const char* argv[] = {"/system/bin/snapuserd", cow_system_->path,
+ "/dev/block/mapper/system_a", cow_product_->path,
+ "/dev/block/mapper/product_a", nullptr};
if (execv(argv[0], const_cast<char**>(argv))) {
ASSERT_TRUE(0);
}
}
+}
+
+void SnapuserdTest::CreateSnapshotDevices() {
+ std::string cmd;
+
+ cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_);
+ cmd += " /dev/block/mapper/system_a";
+ cmd += " /dev/block/mapper/" + system_device_name_;
+ cmd += " P 8";
+
+ system(cmd.c_str());
cmd.clear();
- cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(blksize);
- cmd += " /dev/block/mapper/system_a /dev/block/mapper/system_cow ";
- cmd += "P 8";
+ cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_);
+ cmd += " /dev/block/mapper/product_a";
+ cmd += " /dev/block/mapper/" + product_device_name_;
+ cmd += " P 8";
+
system(cmd.c_str());
+}
- // Wait so that snapshot device is created
- sleep(5);
- std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size);
+void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf) {
+ loff_t offset = 0;
+ std::unique_ptr<uint8_t[]> buffer = std::move(buf);
- offset = 0;
-
- snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
//================Start IO operation on dm-snapshot device=================
// This will test the following paths:
@@ -189,16 +264,16 @@
// dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
// op)->decompress_cow->return
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
// Update the offset
- offset += size;
+ offset += size_;
- // Compare data with random_buffer_1.
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1.get(), size), 0);
+ // Compare data with random_buffer_1_.
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1_.get(), size_), 0);
// Clear the buffer
- memset(snapuserd_buffer.get(), 0, size);
+ memset(snapuserd_buffer.get(), 0, size_);
// Read from snapshot device of size 100MB from offset 100MB. This tests the
// copy operation.
@@ -207,13 +282,13 @@
//
// dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_system_a_partition
// (copy op) -> return
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
// Update the offset
- offset += size;
+ offset += size_;
- // Compare data with system_buffer.
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), system_buffer.get(), size), 0);
+ // Compare data with buffer.
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), buffer.get(), size_), 0);
// Read from snapshot device of size 100MB from offset 200MB. This tests the
// zero operation.
@@ -222,16 +297,13 @@
//
// dm-snap->dm-snap-persistent->dm-user->snapuserd->fill_memory_with_zero
// (zero op) -> return
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
-
- // Fill the random_buffer_1 with zero as we no longer need it
- memset(random_buffer_1.get(), 0, size);
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
// Compare data with zero filled buffer
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1.get(), size), 0);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), zero_buffer_.get(), size_), 0);
// Update the offset
- offset += size;
+ offset += size_;
// Read from snapshot device of size 100MB from offset 300MB. This tests the
// final replace operation.
@@ -240,10 +312,34 @@
//
// dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
// op)->decompress_cow->return
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- // Compare data with random_buffer_2.
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2.get(), size), 0);
+ // Compare data with random_buffer_2_.
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2_.get(), size_), 0);
+}
+
+TEST_F(SnapuserdTest, ReadWrite) {
+ unique_fd snapshot_fd;
+
+ Init();
+
+ CreateCowDevice(cow_system_);
+ CreateCowDevice(cow_product_);
+
+ CreateSystemDmUser();
+ CreateProductDmUser();
+
+ StartSnapuserdDaemon();
+
+ CreateSnapshotDevices();
+
+ snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+ TestIO(snapshot_fd, std::move(system_buffer_));
+
+ snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+ TestIO(snapshot_fd, std::move(product_buffer_));
}
} // namespace snapshot
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/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 4457de3..eb6ad05 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -15,6 +15,7 @@
#pragma once
#include <libsnapshot/snapshot.h>
+#include <payload_consumer/file_descriptor.h>
#include <gmock/gmock.h>
@@ -37,6 +38,10 @@
(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path),
(override));
+ MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,
+ (const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
+ MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenSnapshotReader,
+ (const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
MOCK_METHOD(bool, CreateLogicalAndSnapshotPartitions,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index a4a3150..6fef58a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -35,6 +35,7 @@
#include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h>
+#include <libsnapshot/cow_writer.h>
#include <libsnapshot/return.h>
#ifndef FRIEND_TEST
@@ -43,6 +44,10 @@
#define DEFINED_FRIEND_TEST
#endif
+namespace chromeos_update_engine {
+class FileDescriptor;
+} // namespace chromeos_update_engine
+
namespace android {
namespace fiemap {
@@ -105,6 +110,8 @@
};
virtual ~ISnapshotManager() = default;
+ using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
virtual bool BeginUpdate() = 0;
@@ -173,11 +180,26 @@
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
+ //
// |snapshot_path| must not be nullptr.
+ //
+ // This method will return false if ro.virtual_ab.compression.enabled is true.
virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) = 0;
- // Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
+ // Create an ICowWriter to build a snapshot against a target partition. The partition name must
+ // be suffixed.
+ virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
+
+ // Open a snapshot for reading. A file-like interface is provided through the FileDescriptor.
+ // In this mode, writes are not supported. The partition name must be suffixed.
+ virtual std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
+
+ // Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
+ // OpenSnapshotWriter, or OpenSnapshotReader. All outstanding open descriptors, writers,
+ // or readers must be deleted before this is called.
virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
// If this returns true, first-stage mount must call
@@ -288,6 +310,10 @@
Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
std::string* snapshot_path) override;
+ std::unique_ptr<ICowWriter> OpenSnapshotWriter(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) override;
+ std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override;
bool CreateLogicalAndSnapshotPartitions(
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 7a27fad..149f463 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -15,6 +15,7 @@
#pragma once
#include <libsnapshot/snapshot.h>
+#include <payload_consumer/file_descriptor.h>
namespace android::snapshot {
@@ -35,6 +36,10 @@
const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) override;
+ std::unique_ptr<ICowWriter> OpenSnapshotWriter(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) override;
+ std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override;
bool CreateLogicalAndSnapshotPartitions(
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/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index b49f99e..0904fc7 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -27,6 +27,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
@@ -68,6 +69,7 @@
using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::Extent;
+using chromeos_update_engine::FileDescriptor;
using chromeos_update_engine::InstallOperation;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
@@ -103,6 +105,10 @@
metadata_dir_ = device_->GetMetadataDir();
}
+static inline bool IsCompressionEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
+}
+
static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
}
@@ -2420,6 +2426,11 @@
bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
std::string* snapshot_path) {
+ if (IsCompressionEnabled()) {
+ LOG(ERROR) << "MapUpdateSnapshot cannot be used in compression mode.";
+ return false;
+ }
+
auto lock = LockShared();
if (!lock) return false;
if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
@@ -2430,6 +2441,22 @@
return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
}
+std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) {
+ (void)params;
+
+ LOG(ERROR) << "OpenSnapshotWriter not yet implemented";
+ return nullptr;
+}
+
+std::unique_ptr<FileDescriptor> SnapshotManager::OpenSnapshotReader(
+ const android::fs_mgr::CreateLogicalPartitionParams& params) {
+ (void)params;
+
+ LOG(ERROR) << "OpenSnapshotReader not yet implemented";
+ return nullptr;
+}
+
bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
auto lock = LockShared();
if (!lock) return false;
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 9b6f758..8ae6305 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -20,6 +20,7 @@
using android::fs_mgr::CreateLogicalPartitionParams;
using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::FileDescriptor;
namespace android::snapshot {
@@ -129,4 +130,16 @@
return &snapshot_merge_stats;
}
+std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(
+ const CreateLogicalPartitionParams&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return nullptr;
+}
+
+std::unique_ptr<FileDescriptor> SnapshotManagerStub::OpenSnapshotReader(
+ const CreateLogicalPartitionParams&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return nullptr;
+}
+
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 605af9b..d3f4f70 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -17,10 +17,12 @@
#include <linux/types.h>
#include <stdlib.h>
+#include <csignal>
#include <cstring>
#include <iostream>
#include <limits>
#include <string>
+#include <thread>
#include <vector>
#include <android-base/file.h>
@@ -35,6 +37,7 @@
namespace android {
namespace snapshot {
+using namespace android;
using namespace android::dm;
using android::base::unique_fd;
@@ -45,6 +48,60 @@
static_assert(PAYLOAD_SIZE >= BLOCK_SIZE);
+class Target {
+ public:
+ // Represents an already-created Target, which is referenced by UUID.
+ Target(std::string uuid) : uuid_(uuid) {}
+
+ const auto& uuid() { return uuid_; }
+ std::string control_path() { return std::string("/dev/dm-user-") + uuid(); }
+
+ private:
+ const std::string uuid_;
+};
+
+class Daemon {
+ // The Daemon class is a singleton to avoid
+ // instantiating more than once
+ public:
+ static Daemon& Instance() {
+ static Daemon instance;
+ return instance;
+ }
+
+ bool IsRunning();
+
+ private:
+ bool is_running_;
+
+ Daemon();
+ Daemon(Daemon const&) = delete;
+ void operator=(Daemon const&) = delete;
+
+ static void SignalHandler(int signal);
+};
+
+Daemon::Daemon() {
+ is_running_ = true;
+ signal(SIGINT, Daemon::SignalHandler);
+ signal(SIGTERM, Daemon::SignalHandler);
+}
+
+bool Daemon::IsRunning() {
+ return is_running_;
+}
+
+void Daemon::SignalHandler(int signal) {
+ LOG(DEBUG) << "Snapuserd received signal: " << signal;
+ switch (signal) {
+ case SIGINT:
+ case SIGTERM: {
+ Daemon::Instance().is_running_ = false;
+ break;
+ }
+ }
+}
+
class BufferSink : public IByteSink {
public:
void Initialize(size_t size) {
@@ -558,10 +615,26 @@
return 1;
}
- // TODO: use UUID to support multiple partitions
- ctrl_fd_.reset(open("/dev/dm-user", O_RDWR));
+ std::string str(in_cow_device_);
+ std::size_t found = str.find_last_of("/\\");
+ CHECK(found != std::string::npos);
+ std::string device_name = str.substr(found + 1);
+
+ LOG(DEBUG) << "Fetching UUID for: " << device_name;
+
+ auto& dm = dm::DeviceMapper::Instance();
+ std::string uuid;
+ if (!dm.GetDmDeviceUuidByName(device_name, &uuid)) {
+ LOG(ERROR) << "Unable to find UUID for " << in_cow_device_;
+ return 1;
+ }
+
+ LOG(DEBUG) << "UUID: " << uuid;
+ Target t(uuid);
+
+ ctrl_fd_.reset(open(t.control_path().c_str(), O_RDWR));
if (ctrl_fd_ < 0) {
- LOG(ERROR) << "Unable to open /dev/dm-user";
+ LOG(ERROR) << "Unable to open " << t.control_path();
return 1;
}
@@ -682,9 +755,30 @@
} // namespace snapshot
} // namespace android
+void run_thread(std::string cow_device, std::string backing_device) {
+ android::snapshot::Snapuserd snapd(cow_device, backing_device);
+ snapd.Run();
+}
+
int main([[maybe_unused]] int argc, char** argv) {
android::base::InitLogging(argv, &android::base::KernelLogger);
- android::snapshot::Snapuserd snapd(argv[1], argv[2]);
- return snapd.Run();
+ android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
+
+ while (daemon.IsRunning()) {
+ // TODO: This is hardcoded wherein:
+ // argv[1] = system_cow, argv[2] = /dev/block/mapper/system_a
+ // argv[3] = product_cow, argv[4] = /dev/block/mapper/product_a
+ //
+ // This should be fixed based on some kind of IPC or setup a
+ // command socket and spin up the thread based when a new
+ // partition is visible.
+ std::thread system_a(run_thread, argv[1], argv[2]);
+ std::thread product_a(run_thread, argv[3], argv[4]);
+
+ system_a.join();
+ product_a.join();
+ }
+
+ return 0;
}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7a3d9a9..9edcda7 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,6 +38,7 @@
#include <vector>
using namespace std::literals::string_literals;
+using namespace std::chrono_literals;
using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
@@ -49,6 +50,7 @@
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
+ std::cerr << " getuuid <dm-name>" << std::endl;
std::cerr << " info <dm-name>" << std::endl;
std::cerr << " status <dm-name>" << std::endl;
std::cerr << " resume <dm-name>" << std::endl;
@@ -241,8 +243,9 @@
return ret;
}
+ std::string ignore_path;
DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice(name, table)) {
+ if (!dm.CreateDevice(name, table, &ignore_path, 5s)) {
std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
return -EIO;
}
@@ -391,6 +394,22 @@
return 0;
}
+static int GetUuidCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string uuid;
+ if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {
+ std::cerr << "Could not query uuid of device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+ std::cout << uuid << std::endl;
+ return 0;
+}
+
static int InfoCmdHandler(int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
@@ -504,6 +523,7 @@
{"list", DmListCmdHandler},
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
+ {"getuuid", GetUuidCmdHandler},
{"info", InfoCmdHandler},
{"table", TableCmdHandler},
{"status", StatusCmdHandler},
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
deleted file mode 120000
index f310b35..0000000
--- a/include/private/android_filesystem_capability.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libcutils/include/private/android_filesystem_capability.h
\ No newline at end of file
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
deleted file mode 120000
index f187a6d..0000000
--- a/include/private/android_logger.h
+++ /dev/null
@@ -1 +0,0 @@
-../../liblog/include/private/android_logger.h
\ No newline at end of file
diff --git a/include/private/canned_fs_config.h b/include/private/canned_fs_config.h
deleted file mode 120000
index 8f92b2d..0000000
--- a/include/private/canned_fs_config.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libcutils/include/private/canned_fs_config.h
\ No newline at end of file
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
deleted file mode 100644
index e9868a4..0000000
--- a/include/private/fs_config.h
+++ /dev/null
@@ -1,4 +0,0 @@
-// TODO(b/63135587) remove this file after the transitive dependency
-// from private/android_filesystem_config.h is resolved. All files that use
-// libcutils/include/private/fs_config.h should include the file directly, not
-// indirectly via private/android_filesystem_config.h.
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..4363f3c 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/*/etc/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..ba7e6bd 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/*/etc/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/init/subcontext.cpp b/init/subcontext.cpp
index 9d4ea8c..dc2455e 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -30,6 +30,7 @@
#include "action.h"
#include "builtins.h"
+#include "mount_namespace.h"
#include "proto_utils.h"
#include "util.h"
@@ -217,7 +218,13 @@
PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
}
}
-
+#if defined(__ANDROID__)
+ // subcontext init runs in "default" mount namespace
+ // so that it can access /apex/*
+ if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
+ LOG(FATAL) << "Could not switch to \"default\" mount namespace: " << result.error();
+ }
+#endif
auto init_path = GetExecutablePath();
auto child_fd_string = std::to_string(child_fd);
const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
diff --git a/init/sysprop/api/com.android.sysprop.init-latest.txt b/init/sysprop/api/com.android.sysprop.init-latest.txt
index c835b95..01f4e9a 100644
--- a/init/sysprop/api/com.android.sysprop.init-latest.txt
+++ b/init/sysprop/api/com.android.sysprop.init-latest.txt
@@ -1,8 +1,13 @@
props {
module: "android.sysprop.InitProperties"
prop {
+ api_name: "is_userspace_reboot_supported"
+ prop_name: "init.userspace_reboot.is_supported"
+ integer_as_bool: true
+ }
+ prop {
api_name: "userspace_reboot_in_progress"
- scope: Public
+ access: ReadWrite
prop_name: "sys.init.userspace_reboot.in_progress"
integer_as_bool: true
}
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 04b8f66..524b715 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -28,6 +28,7 @@
name: "libcutils_headers",
vendor_available: true,
recovery_available: true,
+ ramdisk_available: true,
host_supported: true,
apex_available: [
"//apex_available:platform",
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index b9fc82e..31e1679 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -80,6 +80,7 @@
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
{ 00751, AID_ROOT, AID_SHELL, 0, "product/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "product/apex/*/bin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
@@ -90,6 +91,7 @@
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "vendor/apex/*/bin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
// clang-format on
@@ -210,12 +212,14 @@
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "odm/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "product/apex/*bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system_ext/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "vendor/apex/*bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
// clang-format on
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 5a09a2d..1ab63dc 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -30,9 +30,9 @@
static void atrace_init_once()
{
- atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
if (atrace_marker_fd == -1) {
- atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
}
if (atrace_marker_fd == -1) {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 6051ac7..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: [],
@@ -97,6 +98,7 @@
header_libs: [
"libbase_headers",
+ "libcutils_headers",
"liblog_headers",
],
export_header_lib_headers: ["liblog_headers"],
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index a63a5b6..43ae69d 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -32,7 +32,10 @@
"-DWRITE_TO_STATSD=1",
"-DWRITE_TO_LOGD=0",
],
- header_libs: ["libstatssocket_headers"],
+ header_libs: [
+ "libcutils_headers",
+ "libstatssocket_headers",
+ ],
static_libs: [
"libbase",
],
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index bf79ea2..89cdfe5 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -29,6 +29,7 @@
static_libs: [
"libcutils", // does not expose a stable C API
],
+ header_libs: ["liblog_headers"],
cflags: [
"-Wall",
"-Werror",
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.
diff --git a/logd/Android.bp b/logd/Android.bp
index 7f67ab0..c057ef0 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -36,6 +36,7 @@
"libz",
],
static_libs: ["libzstd"],
+ header_libs: ["libcutils_headers"],
cflags: [
"-Wextra",
"-Wthread-safety",
@@ -66,6 +67,7 @@
"SerializedLogChunk.cpp",
"SimpleLogBuffer.cpp",
],
+ static_libs: ["liblog"],
logtags: ["event.logtags"],
export_include_dirs: ["."],
diff --git a/logd/SerializedFlushToState.cpp b/logd/SerializedFlushToState.cpp
index b02ccc3..fdf1dd3 100644
--- a/logd/SerializedFlushToState.cpp
+++ b/logd/SerializedFlushToState.cpp
@@ -16,6 +16,8 @@
#include "SerializedFlushToState.h"
+#include <limits>
+
#include <android-base/logging.h>
SerializedFlushToState::SerializedFlushToState(uint64_t start, LogMask log_mask)
@@ -63,14 +65,13 @@
log_positions_[log_id].emplace(log_position);
}
-void SerializedFlushToState::AddMinHeapEntry(log_id_t log_id) {
+void SerializedFlushToState::UpdateLogsNeeded(log_id_t log_id) {
auto& buffer_it = log_positions_[log_id]->buffer_it;
auto read_offset = log_positions_[log_id]->read_offset;
- // If there is another log to read in this buffer, add it to the min heap.
+ // If there is another log to read in this buffer, let it be read.
if (read_offset < buffer_it->write_offset()) {
- auto* entry = buffer_it->log_entry(read_offset);
- min_heap_.emplace(log_id, entry);
+ logs_needed_from_next_position_[log_id] = false;
} else if (read_offset == buffer_it->write_offset()) {
// If there are no more logs to read in this buffer and it's the last buffer, then
// set logs_needed_from_next_position_ to wait until more logs get logged.
@@ -85,13 +86,13 @@
if (buffer_it->write_offset() == 0) {
logs_needed_from_next_position_[log_id] = true;
} else {
- auto* entry = buffer_it->log_entry(0);
- min_heap_.emplace(log_id, entry);
+ logs_needed_from_next_position_[log_id] = false;
}
}
} else {
// read_offset > buffer_it->write_offset() should never happen.
- CHECK(false);
+ LOG(FATAL) << "read_offset (" << read_offset << ") > buffer_it->write_offset() ("
+ << buffer_it->write_offset() << ")";
}
}
@@ -106,24 +107,41 @@
}
CreateLogPosition(i);
}
- logs_needed_from_next_position_[i] = false;
- // If it wasn't possible to insert, logs_needed_from_next_position will be set back to true.
- AddMinHeapEntry(i);
+ UpdateLogsNeeded(i);
}
}
-MinHeapElement SerializedFlushToState::PopNextUnreadLog() {
- auto top = min_heap_.top();
- min_heap_.pop();
+bool SerializedFlushToState::HasUnreadLogs() {
+ CheckForNewLogs();
+ log_id_for_each(i) {
+ if (log_positions_[i] && !logs_needed_from_next_position_[i]) {
+ return true;
+ }
+ }
+ return false;
+}
- auto* entry = top.entry;
- auto log_id = top.log_id;
+LogWithId SerializedFlushToState::PopNextUnreadLog() {
+ uint64_t min_sequence = std::numeric_limits<uint64_t>::max();
+ log_id_t log_id;
+ const SerializedLogEntry* entry = nullptr;
+ log_id_for_each(i) {
+ if (!log_positions_[i] || logs_needed_from_next_position_[i]) {
+ continue;
+ }
+ if (log_positions_[i]->log_entry()->sequence() < min_sequence) {
+ log_id = i;
+ entry = log_positions_[i]->log_entry();
+ min_sequence = entry->sequence();
+ }
+ }
+ CHECK_NE(nullptr, entry);
log_positions_[log_id]->read_offset += entry->total_len();
logs_needed_from_next_position_[log_id] = true;
- return top;
+ return {log_id, entry};
}
void SerializedFlushToState::Prune(log_id_t log_id,
@@ -133,25 +151,12 @@
return;
}
- // // Decrease the ref count since we're deleting our reference.
+ // Decrease the ref count since we're deleting our reference.
buffer_it->DecReaderRefCount();
// Delete in the reference.
log_positions_[log_id].reset();
- // Remove the MinHeapElement referencing log_id, if it exists, but retain the others.
- std::vector<MinHeapElement> old_elements;
- while (!min_heap_.empty()) {
- auto& element = min_heap_.top();
- if (element.log_id != log_id) {
- old_elements.emplace_back(element);
- }
- min_heap_.pop();
- }
- for (auto&& element : old_elements) {
- min_heap_.emplace(element);
- }
-
// Finally set logs_needed_from_next_position_, so CheckForNewLogs() will re-create the
// log_position_ object during the next read.
logs_needed_from_next_position_[log_id] = true;
diff --git a/logd/SerializedFlushToState.h b/logd/SerializedFlushToState.h
index 0b20822..c953a16 100644
--- a/logd/SerializedFlushToState.h
+++ b/logd/SerializedFlushToState.h
@@ -27,26 +27,19 @@
struct LogPosition {
std::list<SerializedLogChunk>::iterator buffer_it;
int read_offset;
+
+ const SerializedLogEntry* log_entry() const { return buffer_it->log_entry(read_offset); }
};
-struct MinHeapElement {
- MinHeapElement(log_id_t log_id, const SerializedLogEntry* entry)
- : log_id(log_id), entry(entry) {}
+struct LogWithId {
log_id_t log_id;
const SerializedLogEntry* entry;
- // The change of comparison operators is intentional, std::priority_queue uses operator<() to
- // compare but creates a max heap. Since we want a min heap, we return the opposite result.
- bool operator<(const MinHeapElement& rhs) const {
- return entry->sequence() > rhs.entry->sequence();
- }
};
// This class tracks the specific point where a FlushTo client has read through the logs. It
// directly references the std::list<> iterators from the parent SerializedLogBuffer and the offset
// into each log chunk where it has last read. All interactions with this class, except for its
-// construction, must be done with SerializedLogBuffer::lock_ held. No log chunks that it
-// references may be pruned, which is handled by ensuring prune does not touch any log chunk with
-// highest sequence number greater or equal to start().
+// construction, must be done with SerializedLogBuffer::lock_ held.
class SerializedFlushToState : public FlushToState {
public:
// Initializes this state object. For each log buffer set in log_mask, this sets
@@ -61,31 +54,29 @@
if (logs_ == nullptr) logs_ = logs;
}
- bool HasUnreadLogs() {
- CheckForNewLogs();
- return !min_heap_.empty();
- }
+ // Updates the state of log_positions_ and logs_needed_from_next_position_ then returns true if
+ // there are any unread logs, false otherwise.
+ bool HasUnreadLogs();
- // Pops the next unread log from the min heap and sets logs_needed_from_next_position_ to
- // indicate that we're waiting for more logs from the associated log buffer.
- MinHeapElement PopNextUnreadLog();
+ // Returns the next unread log and sets logs_needed_from_next_position_ to indicate that we're
+ // waiting for more logs from the associated log buffer.
+ LogWithId PopNextUnreadLog();
// If the parent log buffer prunes logs, the reference that this class contains may become
// invalid, so this must be called first to drop the reference to buffer_it, if any.
void Prune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& buffer_it);
private:
- // If there is a log in the serialized log buffer for `log_id` at the read_offset, add it to the
- // min heap for reading, otherwise set logs_needed_from_next_position_ to indicate that we're
- // waiting for the next log.
- void AddMinHeapEntry(log_id_t log_id);
+ // Set logs_needed_from_next_position_[i] to indicate if log_positions_[i] points to an unread
+ // log or to the point at which the next log will appear.
+ void UpdateLogsNeeded(log_id_t log_id);
// Create a LogPosition object for the given log_id by searching through the log chunks for the
// first chunk and then first log entry within that chunk that is greater or equal to start().
void CreateLogPosition(log_id_t log_id);
// Checks to see if any log buffers set in logs_needed_from_next_position_ have new logs and
- // calls AddMinHeapEntry() if so.
+ // calls UpdateLogsNeeded() if so.
void CheckForNewLogs();
std::list<SerializedLogChunk>* logs_ = nullptr;
@@ -97,7 +88,4 @@
// next_log_position == logs_write_position_)`. These will be re-checked in each
// loop in case new logs came in.
std::bitset<LOG_ID_MAX> logs_needed_from_next_position_ = {};
- // A min heap that has up to one entry per log buffer, sorted by sequence number, of the next
- // element that this reader should read.
- std::priority_queue<MinHeapElement> min_heap_;
};
diff --git a/logd/SerializedFlushToStateTest.cpp b/logd/SerializedFlushToStateTest.cpp
index f4515c8..88f4052 100644
--- a/logd/SerializedFlushToStateTest.cpp
+++ b/logd/SerializedFlushToStateTest.cpp
@@ -287,4 +287,21 @@
EXPECT_EQ(second_chunk->reader_ref_count(), 1U);
EXPECT_FALSE(state.HasUnreadLogs());
-}
\ No newline at end of file
+}
+
+TEST(SerializedFlushToState, Prune) {
+ auto chunk = SerializedLogChunk{kChunkSize};
+ chunk.Log(1, log_time(), 0, 1, 1, "abc", 3);
+ chunk.Log(2, log_time(), 0, 1, 1, "abc", 3);
+ chunk.Log(3, log_time(), 0, 1, 1, "abc", 3);
+ chunk.FinishWriting();
+
+ std::list<SerializedLogChunk> log_chunks[LOG_ID_MAX];
+ log_chunks[LOG_ID_MAIN].emplace_back(std::move(chunk));
+
+ auto state = SerializedFlushToState{1, kLogMaskAll};
+ state.InitializeLogs(log_chunks);
+ ASSERT_TRUE(state.HasUnreadLogs());
+
+ state.Prune(LOG_ID_MAIN, log_chunks[LOG_ID_MAIN].begin());
+}
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index 5012d3d..acd093b 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -211,7 +211,7 @@
state.InitializeLogs(logs_);
while (state.HasUnreadLogs()) {
- MinHeapElement top = state.PopNextUnreadLog();
+ LogWithId top = state.PopNextUnreadLog();
auto* entry = top.entry;
auto log_id = top.log_id;
diff --git a/logd/SerializedLogChunk.h b/logd/SerializedLogChunk.h
index 0991eac..645433d 100644
--- a/logd/SerializedLogChunk.h
+++ b/logd/SerializedLogChunk.h
@@ -18,6 +18,8 @@
#include <sys/types.h>
+#include <android-base/logging.h>
+
#include "LogWriter.h"
#include "SerializedData.h"
#include "SerializedLogEntry.h"
@@ -55,6 +57,7 @@
}
const SerializedLogEntry* log_entry(int offset) const {
+ CHECK(writer_active_ || reader_ref_count_ > 0);
return reinterpret_cast<const SerializedLogEntry*>(data() + offset);
}
const uint8_t* data() const { return contents_.data(); }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 81dba1f..77fa94e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -141,9 +141,6 @@
# via /odm/lib/modules directly.
LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
-# For /modules partition.
-LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/modules
-
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 9c2cdf2..1994bdb 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -17,6 +17,9 @@
devname uevent_devpath
dirname /dev/snd
+subsystem dma_heap
+ devname uevent_devpath
+ dirname /dev/dma_heap
# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
#
@@ -39,6 +42,7 @@
/dev/vndbinder 0666 root root
/dev/pmsg0 0222 root log
+/dev/dma_heap/system 0666 system system
# kms driver for drm based gpu
/dev/dri/* 0666 root graphics
diff --git a/run-as/Android.bp b/run-as/Android.bp
index 840a43c..accd07d 100644
--- a/run-as/Android.bp
+++ b/run-as/Android.bp
@@ -25,4 +25,5 @@
"libpackagelistparser",
"libminijail",
],
+ header_libs: ["libcutils_headers"],
}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 6840baa..27e1a3f 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -75,3 +75,36 @@
vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
}
+
+prebuilt_etc {
+ name: "keymaster_soft_attestation_keys.xml",
+ vendor: true,
+ src: "set_attestation_key/keymaster_soft_attestation_keys.xml",
+}
+
+cc_binary {
+ name: "trusty_keymaster_set_attestation_key",
+ vendor: true,
+
+ srcs: [
+ "set_attestation_key/set_attestation_key.cpp",
+ "ipc/trusty_keymaster_ipc.cpp",
+ ],
+
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "libc",
+ "libcrypto",
+ "liblog",
+ "libtrusty",
+ "libhardware",
+ "libkeymaster_messages",
+ "libxml2",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index 13e6725..ce2cc2e 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -53,6 +53,19 @@
KM_DELETE_ALL_KEYS = (23 << KEYMASTER_REQ_SHIFT),
KM_DESTROY_ATTESTATION_IDS = (24 << KEYMASTER_REQ_SHIFT),
KM_IMPORT_WRAPPED_KEY = (25 << KEYMASTER_REQ_SHIFT),
+
+ // Bootloader/provisioning calls.
+ KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT),
+ KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_GET_CA_REQUEST = (0x4000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_SET_CA_RESPONSE_BEGIN = (0x5000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_SET_CA_RESPONSE_UPDATE = (0x6000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_SET_CA_RESPONSE_FINISH = (0x7000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_READ_UUID = (0x8000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),
+ KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
};
#ifdef __ANDROID__
diff --git a/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml b/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml
new file mode 100644
index 0000000..fce2ac2
--- /dev/null
+++ b/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+<AndroidAttestation>
+ <NumberOfKeyboxes>10</NumberOfKeyboxes>
+ <Keybox DeviceID="dev1">
+ <Key algorithm="rsa">
+ <PrivateKey format="pem">
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb
+KvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI
+O2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB
+AoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3
+wc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C
+woB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs
+NoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU
+0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy
+DSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY
+ENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3
+TPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y
+qAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x
+fs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l
+-----END RSA PRIVATE KEY-----
+ </PrivateKey>
+ <CertificateChain>
+ <NumberOfCertificates>2</NumberOfCertificates>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx
+MjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
+YWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv
+aWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1
+qEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX
+4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd
+XX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ
+k4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw
+BgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn
+M9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4
+Fg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f
+9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8=
+-----END CERTIFICATE-----
+ </Certificate>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN
+MTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE
+CgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs
+Q+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL
+t0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID
+AQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw
+FoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
+Af8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j
+HBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc
+DyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op
+Lvtc9mL1J1IXvmM=
+-----END CERTIFICATE-----
+ </Certificate>
+ </CertificateChain>
+ </Key>
+ </Keybox>
+ <Keybox DeviceID="dev1">
+ <Key algorithm="ecdsa">
+ <PrivateKey format="pem">
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49
+AwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l
+uU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA==
+-----END EC PRIVATE KEY-----
+ </PrivateKey>
+ <CertificateChain>
+ <NumberOfCertificates>2</NumberOfCertificates>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
+EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
+VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
+ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
+MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
+bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
+dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
+efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
+U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
+qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
+wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
+Xvsiu+f+uXc/WT/7
+-----END CERTIFICATE-----
+ </Certificate>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
+EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
+dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
+VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
+HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
+QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
+KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
+dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
+BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
+EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
+SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
+C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
+-----END CERTIFICATE-----
+ </Certificate>
+ </CertificateChain>
+ </Key>
+ </Keybox>
+</AndroidAttestation>
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
new file mode 100644
index 0000000..a89a4a8
--- /dev/null
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2020 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 <errno.h>
+#include <getopt.h>
+#include <libxml/xmlreader.h>
+#include <openssl/pem.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <string>
+
+using std::string;
+
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+static const char* _sopts = "h";
+static const struct option _lopts[] = {
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0},
+};
+
+static const char* usage =
+ "Usage: %s [options] xml-file\n"
+ "\n"
+ "options:\n"
+ " -h, --help prints this message and exit\n"
+ "\n";
+
+static void print_usage_and_exit(const char* prog, int code) {
+ fprintf(stderr, usage, prog);
+ exit(code);
+}
+
+static void parse_options(int argc, char** argv) {
+ int c;
+ int oidx = 0;
+
+ while (1) {
+ c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+ if (c == -1) {
+ break; /* done */
+ }
+
+ switch (c) {
+ case 'h':
+ print_usage_and_exit(argv[0], EXIT_SUCCESS);
+ break;
+
+ default:
+ print_usage_and_exit(argv[0], EXIT_FAILURE);
+ }
+ }
+}
+
+struct SetAttestationKeyRequest : public keymaster::KeymasterMessage {
+ explicit SetAttestationKeyRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ : KeymasterMessage(ver) {}
+
+ size_t SerializedSize() const override { return sizeof(uint32_t) + key_data.SerializedSize(); }
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
+ buf = keymaster::append_uint32_to_buf(buf, end, algorithm);
+ return key_data.Serialize(buf, end);
+ }
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
+ return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm) &&
+ key_data.Deserialize(buf_ptr, end);
+ }
+
+ keymaster_algorithm_t algorithm;
+ keymaster::Buffer key_data;
+};
+
+struct KeymasterNoResponse : public keymaster::KeymasterResponse {
+ explicit KeymasterNoResponse(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ : keymaster::KeymasterResponse(ver) {}
+
+ size_t NonErrorSerializedSize() const override { return 0; }
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; }
+ bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; }
+};
+
+struct SetAttestationKeyResponse : public KeymasterNoResponse {};
+
+struct ClearAttestationCertChainRequest : public keymaster::KeymasterMessage {
+ explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ : KeymasterMessage(ver) {}
+
+ size_t SerializedSize() const override { return sizeof(uint32_t); }
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
+ return keymaster::append_uint32_to_buf(buf, end, algorithm);
+ }
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
+ return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm);
+ }
+
+ keymaster_algorithm_t algorithm;
+};
+
+struct ClearAttestationCertChainResponse : public KeymasterNoResponse {};
+
+static int set_attestation_key_or_cert_bin(uint32_t cmd, keymaster_algorithm_t algorithm,
+ const void* key_data, size_t key_data_size) {
+ int ret;
+
+ SetAttestationKeyRequest req;
+ req.algorithm = algorithm;
+ req.key_data.Reinitialize(key_data, key_data_size);
+ SetAttestationKeyResponse rsp;
+
+ ret = trusty_keymaster_send(cmd, req, &rsp);
+ if (ret) {
+ fprintf(stderr, "trusty_keymaster_send cmd 0x%x failed %d\n", cmd, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int set_attestation_key_or_cert_pem(uint32_t cmd, keymaster_algorithm_t algorithm,
+ const xmlChar* pemkey) {
+ int ret;
+ int sslret;
+
+ /* Convert from pem to binary */
+ BIO* bio = BIO_new_mem_buf(pemkey, xmlStrlen(pemkey));
+ if (!bio) {
+ fprintf(stderr, "BIO_new_mem_buf failed\n");
+ ERR_print_errors_fp(stderr);
+ return -1;
+ }
+
+ char* key_name;
+ char* key_header;
+ uint8_t* key;
+ long keylen;
+ sslret = PEM_read_bio(bio, &key_name, &key_header, &key, &keylen);
+ BIO_free(bio);
+
+ if (!sslret) {
+ fprintf(stderr, "PEM_read_bio failed\n");
+ ERR_print_errors_fp(stderr);
+ return -1;
+ }
+
+ /* Send key in binary format to trusty */
+ ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);
+
+ OPENSSL_free(key_name);
+ OPENSSL_free(key_header);
+ OPENSSL_free(key);
+
+ return ret;
+}
+
+static int set_attestation_key_or_cert_iecs(uint32_t cmd, keymaster_algorithm_t algorithm,
+ const xmlChar* key_base64) {
+ int ret;
+ int sslret;
+
+ /* Remove all whitespace. EVP_DecodeBase64 does not support whitespace. */
+ string key_base64_str((const char*)key_base64);
+ key_base64_str.erase(remove_if(key_base64_str.begin(), key_base64_str.end(), isspace),
+ key_base64_str.end());
+
+ /* Convert from base64 to binary */
+ uint8_t* key;
+ size_t keylen;
+ size_t key_base64_len = key_base64_str.length();
+
+ sslret = EVP_DecodedLength(&keylen, key_base64_len);
+ if (!sslret) {
+ fprintf(stderr, "invalid input length, %zu\n", key_base64_len);
+ return -1;
+ }
+ key = (uint8_t*)malloc(keylen);
+ if (!key) {
+ fprintf(stderr, "failed to allocate key, size %zu\n", key_base64_len);
+ return -1;
+ }
+ sslret = EVP_DecodeBase64(key, &keylen, keylen, (const uint8_t*)key_base64_str.data(),
+ key_base64_len);
+ if (!sslret) {
+ fprintf(stderr, "EVP_DecodeBase64 failed\n");
+ ERR_print_errors_fp(stderr);
+ free(key);
+ return -1;
+ }
+
+ /* Send key in binary format to trusty */
+ ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);
+
+ free(key);
+
+ return ret;
+}
+
+static int str_to_algorithm(keymaster_algorithm_t* algorithm, const xmlChar* algorithm_str) {
+ if (xmlStrEqual(algorithm_str, BAD_CAST "rsa")) {
+ *algorithm = KM_ALGORITHM_RSA;
+ } else if (xmlStrEqual(algorithm_str, BAD_CAST "ecdsa")) {
+ *algorithm = KM_ALGORITHM_EC;
+ } else {
+ printf("unsupported algorithm: %s\n", algorithm_str);
+ return -1;
+ }
+ return 0;
+}
+
+static int set_attestation_key_or_cert(uint32_t cmd, const xmlChar* algorithm_str,
+ const xmlChar* format, const xmlChar* str) {
+ int ret;
+ keymaster_algorithm_t algorithm;
+
+ ret = str_to_algorithm(&algorithm, algorithm_str);
+ if (ret) {
+ return ret;
+ }
+
+ if (xmlStrEqual(format, BAD_CAST "pem")) {
+ ret = set_attestation_key_or_cert_pem(cmd, algorithm, str);
+ } else if (xmlStrEqual(format, BAD_CAST "iecs")) {
+ ret = set_attestation_key_or_cert_iecs(cmd, algorithm, str);
+ } else {
+ printf("unsupported key/cert format: %s\n", format);
+ return -1;
+ }
+ return ret;
+}
+
+static int clear_cert_chain(const xmlChar* algorithm_str) {
+ int ret;
+ ClearAttestationCertChainRequest req;
+ ClearAttestationCertChainResponse rsp;
+
+ ret = str_to_algorithm(&req.algorithm, algorithm_str);
+ if (ret) {
+ return ret;
+ }
+
+ ret = trusty_keymaster_send(KM_CLEAR_ATTESTATION_CERT_CHAIN, req, &rsp);
+ if (ret) {
+ fprintf(stderr, "%s: trusty_keymaster_send failed %d\n", __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int process_xml(xmlTextReaderPtr xml) {
+ int ret;
+ const xmlChar* algorithm = NULL;
+ const xmlChar* element = NULL;
+ const xmlChar* element_format = NULL;
+
+ while ((ret = xmlTextReaderRead(xml)) == 1) {
+ int nodetype = xmlTextReaderNodeType(xml);
+ const xmlChar *name, *value;
+ name = xmlTextReaderConstName(xml);
+ switch (nodetype) {
+ case XML_READER_TYPE_ELEMENT:
+ element = name;
+ element_format = xmlTextReaderGetAttribute(xml, BAD_CAST "format");
+ if (xmlStrEqual(name, BAD_CAST "Key")) {
+ algorithm = xmlTextReaderGetAttribute(xml, BAD_CAST "algorithm");
+ } else if (xmlStrEqual(name, BAD_CAST "CertificateChain")) {
+ ret = clear_cert_chain(algorithm);
+ if (ret) {
+ fprintf(stderr, "%s, algorithm %s: Clear cert chain cmd failed, %d\n",
+ element, algorithm, ret);
+ return ret;
+ }
+ printf("%s, algorithm %s: Clear cert chain cmd done\n", element, algorithm);
+ }
+ break;
+ case XML_READER_TYPE_TEXT:
+ value = xmlTextReaderConstValue(xml);
+ uint32_t cmd;
+ if (xmlStrEqual(element, BAD_CAST "PrivateKey")) {
+ cmd = KM_SET_ATTESTATION_KEY;
+ } else if (xmlStrEqual(element, BAD_CAST "WrappedPrivateKey")) {
+ cmd = KM_SET_WRAPPED_ATTESTATION_KEY;
+ } else if (xmlStrEqual(element, BAD_CAST "Certificate")) {
+ cmd = KM_APPEND_ATTESTATION_CERT_CHAIN;
+ } else {
+ break;
+ }
+
+ ret = set_attestation_key_or_cert(cmd, algorithm, element_format, value);
+ if (ret) {
+ fprintf(stderr, "%s, algorithm %s, format %s: Cmd 0x%x failed, %d\n", element,
+ algorithm, element_format, cmd, ret);
+ return ret;
+ }
+ printf("%s, algorithm %s, format %s: Cmd 0x%x done\n", element, algorithm,
+ element_format, cmd);
+ break;
+ case XML_READER_TYPE_END_ELEMENT:
+ element = NULL;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int parse_xml_file(const char* filename) {
+ int ret;
+ xmlTextReaderPtr xml = xmlReaderForFile(filename, NULL, 0);
+ if (!xml) {
+ fprintf(stderr, "failed to open %s\n", filename);
+ return -1;
+ }
+
+ ret = process_xml(xml);
+
+ xmlFreeTextReader(xml);
+ if (ret != 0) {
+ fprintf(stderr, "Failed to parse or process %s\n", filename);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ int ret = 0;
+
+ parse_options(argc, argv);
+ if (optind + 1 != argc) {
+ print_usage_and_exit(argv[0], EXIT_FAILURE);
+ }
+
+ ret = trusty_keymaster_connect();
+ if (ret) {
+ fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
+ } else {
+ ret = parse_xml_file(argv[optind]);
+ trusty_keymaster_disconnect();
+ }
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index fd353d1..dc4c962 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -14,3 +14,5 @@
PRODUCT_PACKAGES += \
spiproxyd \
+ trusty_keymaster_set_attestation_key \
+ keymaster_soft_attestation_keys.xml \
\ No newline at end of file