Merge "Remove logging symlinks from system/core/include"
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index 0b4a925..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,4 +0,0 @@
-filegroup {
- name: "android_filesystem_config_header",
- srcs: ["include/private/android_filesystem_config.h"],
-}
diff --git a/adb/Android.bp b/adb/Android.bp
index df2c0f9..5c1e1fe 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -25,6 +25,7 @@
"-Wextra",
"-Werror",
"-Wexit-time-destructors",
+ "-Wno-non-virtual-dtor",
"-Wno-unused-parameter",
"-Wno-missing-field-initializers",
"-Wthread-safety",
diff --git a/adb/apex/apex_manifest.json b/adb/apex/apex_manifest.json
index 0444409..4a07bdc 100644
--- a/adb/apex/apex_manifest.json
+++ b/adb/apex/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.adbd",
- "version": 300000000
+ "version": 300900700
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index a663871..50d7364 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -584,12 +584,11 @@
incoming_header_ = msg;
} else {
size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
- Block payload = std::move(block->payload);
if (block->payload.size() > bytes_left) {
HandleError("received too many bytes while waiting for payload");
return false;
}
- incoming_payload_.append(std::move(payload));
+ incoming_payload_.append(std::move(block->payload));
}
if (incoming_header_->data_length == incoming_payload_.size()) {
diff --git a/cli-test/cli-test.cpp b/cli-test/cli-test.cpp
index d6e27ee..d1ef1b4 100644
--- a/cli-test/cli-test.cpp
+++ b/cli-test/cli-test.cpp
@@ -146,6 +146,13 @@
test->befores.push_back(line);
} else if (Match(&line, "after:")) {
test->afters.push_back(line);
+ } else if (Match(&line, "expected-exit-status:")) {
+ char* end_p;
+ errno = 0;
+ test->exit_status = strtol(line.c_str(), &end_p, 10);
+ if (errno != 0 || *end_p != '\0') {
+ Die(0, "%s:%zu: bad exit status: \"%s\"", g_file, g_line, line.c_str());
+ }
} else if (Match(&line, "expected-stdout:")) {
// Collect tab-indented lines.
std::string text;
@@ -231,15 +238,15 @@
V("running command \"%s\"", test.command.c_str());
CapturedStdout test_stdout;
CapturedStderr test_stderr;
- int exit_status = system(test.command.c_str());
+ int status = system(test.command.c_str());
test_stdout.Stop();
test_stderr.Stop();
- V("exit status %d", exit_status);
- if (exit_status != test.exit_status) {
+ V("system() returned status %d", status);
+ if (WEXITSTATUS(status) != test.exit_status) {
failed = true;
fprintf(stderr, "Incorrect exit status: expected %d but %s\n", test.exit_status,
- ExitStatusToString(exit_status).c_str());
+ ExitStatusToString(status).c_str());
}
if (!CheckOutput("stdout", test_stdout.str(), test.expected_stdout, FILES)) failed = true;
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..5280121 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);
@@ -585,8 +597,8 @@
}
// TODO: Use seccomp to lock ourselves down.
- unwindstack::UnwinderFromPid unwinder(256, vm_pid);
- if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
+ if (!unwinder.Init()) {
LOG(FATAL) << "Failed to init unwinder object.";
}
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/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 9bcbdb3..e103c82 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -82,16 +82,12 @@
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
- unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
- thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
+ thread.registers.reset(
+ unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
// TODO: Create this once and store it in a global?
unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
- if (unwinder.Init(arch)) {
- dump_backtrace_thread(output_fd, &unwinder, thread);
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
- }
+ dump_backtrace_thread(output_fd, &unwinder, thread);
}
__linker_disable_fallback_allocator();
}
@@ -237,6 +233,8 @@
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "missing crash_dump_fallback() in selinux policy?");
goto exit;
}
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index f5a873c..c543a83 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -18,8 +18,9 @@
#include "libdebuggerd/backtrace.h"
-#include <errno.h>
#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
@@ -65,7 +66,11 @@
unwinder->SetRegs(thread.registers.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
- _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
+ _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
+ if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+ _LOG(&log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
+ _LOG(&log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
+ }
return;
}
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..d88c5a9 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) {
@@ -404,7 +407,11 @@
unwinder->SetRegs(regs_copy.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
- _LOG(log, logtype::THREAD, "Failed to unwind");
+ _LOG(log, logtype::THREAD, "Failed to unwind\n");
+ if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+ _LOG(log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
+ _LOG(log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
+ }
} else {
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
log_backtrace(log, unwinder, " ");
@@ -575,8 +582,8 @@
.siginfo = siginfo,
};
- unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
- if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
+ if (!unwinder.Init()) {
LOG(FATAL) << "Failed to init unwinder object.";
}
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index b4a1e71..c39f4e4 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -6,6 +6,3 @@
socket tombstoned_intercept seqpacket 0666 system system
socket tombstoned_java_trace seqpacket 0666 system system
writepid /dev/cpuset/system-background/tasks
-
-on post-fs-data
- start tombstoned
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 81ebf43..fe05553 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -126,7 +126,7 @@
shared_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
- "android.hardware.fastboot@1.0",
+ "android.hardware.fastboot@1.1",
"android.hardware.health@2.0",
"libasyncio",
"libbase",
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 2553353..4601960 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -164,6 +164,28 @@
return device->WriteOkay(message);
}
+bool OemPostWipeData(FastbootDevice* device) {
+ auto fastboot_hal = device->fastboot_hal();
+ if (!fastboot_hal) {
+ return false;
+ }
+
+ Result ret;
+ auto ret_val = fastboot_hal->doOemSpecificErase([&](Result result) { ret = result; });
+ if (!ret_val.isOk()) {
+ return false;
+ }
+ if (ret.status == Status::NOT_SUPPORTED) {
+ return false;
+ } else if (ret.status != Status::SUCCESS) {
+ device->WriteStatus(FastbootResult::FAIL, ret.message);
+ } else {
+ device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
+ }
+
+ return true;
+}
+
bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() < 2) {
return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
@@ -184,7 +206,18 @@
return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
}
if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
- return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
+ //Perform oem PostWipeData if Android userdata partition has been erased
+ bool support_oem_postwipedata = false;
+ if (partition_name == "userdata") {
+ support_oem_postwipedata = OemPostWipeData(device);
+ }
+
+ if (!support_oem_postwipedata) {
+ return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
+ } else {
+ //Write device status in OemPostWipeData(), so just return true
+ return true;
+ }
}
return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 1b0859f..52ea9f0 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -22,7 +22,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware/boot/1.0/IBootControl.h>
-#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <android/hardware/fastboot/1.1/IFastboot.h>
#include <fs_mgr.h>
#include <fs_mgr/roots.h>
#include <healthhalutils/HealthHalUtils.h>
@@ -37,7 +37,7 @@
using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::IBootControl;
using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::fastboot::V1_0::IFastboot;
+using ::android::hardware::fastboot::V1_1::IFastboot;
using ::android::hardware::health::V2_0::get_health_service;
namespace sph = std::placeholders;
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index bbe8172..23be721 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -24,7 +24,7 @@
#include <android/hardware/boot/1.0/IBootControl.h>
#include <android/hardware/boot/1.1/IBootControl.h>
-#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <android/hardware/fastboot/1.1/IFastboot.h>
#include <android/hardware/health/2.0/IHealth.h>
#include "commands.h"
@@ -53,7 +53,7 @@
return boot_control_hal_;
}
android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
- android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
+ android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal() {
return fastboot_hal_;
}
android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
@@ -67,7 +67,7 @@
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
- android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
+ android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
std::string active_slot_;
};
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index bdf1da6..db2e16c 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,11 +155,44 @@
"liblog",
],
static_libs: [
+ "libbrotli",
"libz",
],
ramdisk_available: true,
}
+cc_defaults {
+ name: "libsnapshot_snapuserd_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: ["include"],
+ srcs: [
+ "snapuserd_client.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libsnapshot_snapuserd",
+ defaults: [
+ "libsnapshot_snapuserd_defaults",
+ ],
+ recovery_available: true,
+ static_libs: [
+ "libcutils_sockets",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ ramdisk_available: true,
+}
+
cc_library_static {
name: "libsnapshot_test_helpers",
defaults: ["libsnapshot_defaults"],
@@ -312,8 +348,10 @@
"libprotobuf-mutator",
],
header_libs: [
+ "libchrome",
"libfiemap_headers",
"libstorage_literals_headers",
+ "libupdate_engine_headers",
],
proto: {
type: "full",
@@ -352,7 +390,9 @@
"fs_mgr_defaults",
],
srcs: [
+ "snapuserd_server.cpp",
"snapuserd.cpp",
+ "snapuserd_daemon.cpp",
],
cflags: [
@@ -362,10 +402,12 @@
static_libs: [
"libbase",
+ "libbrotli",
+ "libcutils_sockets",
"liblog",
"libdm",
- "libz",
- "libsnapshot_cow",
+ "libz",
+ "libsnapshot_cow",
],
}
@@ -403,12 +445,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 +538,14 @@
shared_libs: [
"libbase",
"liblog",
- "libz",
],
static_libs: [
+ "libbrotli",
"libgtest",
"libsnapshot_cow",
+ "libsnapshot_snapuserd",
+ "libcutils_sockets",
+ "libz",
],
header_libs: [
"libstorage_literals_headers",
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index d98fe59..40d5efb 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -30,12 +30,12 @@
class CowTest : public ::testing::Test {
protected:
- void SetUp() override {
+ virtual void SetUp() override {
cow_ = std::make_unique<TemporaryFile>();
ASSERT_GE(cow_->fd, 0) << strerror(errno);
}
- void TearDown() override { cow_ = nullptr; }
+ virtual void TearDown() override { cow_ = nullptr; }
std::unique_ptr<TemporaryFile> cow_;
};
@@ -70,7 +70,7 @@
ASSERT_TRUE(writer.AddCopy(10, 20));
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -105,7 +105,7 @@
ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(op->source, 104);
+ ASSERT_EQ(op->source, 106);
ASSERT_TRUE(reader.ReadData(*op, &sink));
ASSERT_EQ(sink.stream(), data);
@@ -145,7 +145,7 @@
data.resize(options.block_size, '\0');
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -163,7 +163,7 @@
ASSERT_EQ(op->compression, kCowCompressGz);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(op->source, 104);
+ ASSERT_EQ(op->source, 106);
ASSERT_TRUE(reader.ReadData(*op, &sink));
ASSERT_EQ(sink.stream(), data);
@@ -182,7 +182,7 @@
data.resize(options.block_size * 2, '\0');
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -211,9 +211,11 @@
void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
};
-TEST_F(CowTest, HorribleSink) {
+class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
+
+TEST_P(CompressionTest, HorribleSink) {
CowOptions options;
- options.compression = "gz";
+ options.compression = GetParam();
CowWriter writer(options);
ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -222,7 +224,7 @@
data.resize(options.block_size, '\0');
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -239,6 +241,8 @@
ASSERT_EQ(sink.stream(), data);
}
+INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
+
TEST_F(CowTest, GetSize) {
CowOptions options;
CowWriter writer(options);
@@ -255,7 +259,7 @@
ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
auto size_before = writer.GetCowSize();
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer.Flush());
auto size_after = writer.GetCowSize();
ASSERT_EQ(size_before, size_after);
struct stat buf;
@@ -267,6 +271,60 @@
ASSERT_EQ(buf.st_size, writer.GetCowSize());
}
+TEST_F(CowTest, Append) {
+ CowOptions options;
+ auto writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd, CowWriter::OpenMode::APPEND));
+
+ std::string data2 = "More data!";
+ data2.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+ ASSERT_TRUE(writer->Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ struct stat buf;
+ ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+ ASSERT_EQ(buf.st_size, writer->GetCowSize());
+
+ // Read back both operations.
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ StringSink sink;
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ sink.Reset();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data2);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp
index f480b85..faceafe 100644
--- a/fs_mgr/libsnapshot/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/cow_decompress.cpp
@@ -19,6 +19,7 @@
#include <utility>
#include <android-base/logging.h>
+#include <brotli/decode.h>
#include <zlib.h>
namespace android {
@@ -207,5 +208,57 @@
return std::unique_ptr<IDecompressor>(new GzDecompressor());
}
+class BrotliDecompressor final : public StreamDecompressor {
+ public:
+ ~BrotliDecompressor();
+
+ bool Init() override;
+ bool DecompressInput(const uint8_t* data, size_t length) override;
+ bool Done() override { return BrotliDecoderIsFinished(decoder_); }
+
+ private:
+ BrotliDecoderState* decoder_ = nullptr;
+};
+
+bool BrotliDecompressor::Init() {
+ decoder_ = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+ return true;
+}
+
+BrotliDecompressor::~BrotliDecompressor() {
+ if (decoder_) {
+ BrotliDecoderDestroyInstance(decoder_);
+ }
+}
+
+bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+ size_t available_in = length;
+ const uint8_t* next_in = data;
+
+ bool needs_more_output = false;
+ while (available_in || needs_more_output) {
+ if (!output_buffer_remaining_ && !GetFreshBuffer()) {
+ return false;
+ }
+
+ auto output_buffer = output_buffer_;
+ auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
+ &output_buffer_remaining_, &output_buffer_, nullptr);
+ if (r == BROTLI_DECODER_RESULT_ERROR) {
+ LOG(ERROR) << "brotli decode failed";
+ return false;
+ }
+ if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
+ return false;
+ }
+ needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
+ }
+ return true;
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
+ return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h
index 1c8c40d..f485256 100644
--- a/fs_mgr/libsnapshot/cow_decompress.h
+++ b/fs_mgr/libsnapshot/cow_decompress.h
@@ -40,6 +40,7 @@
// Factory methods for decompression methods.
static std::unique_ptr<IDecompressor> Uncompressed();
static std::unique_ptr<IDecompressor> Gz();
+ static std::unique_ptr<IDecompressor> Brotli();
// |output_bytes| is the expected total number of bytes to sink.
virtual bool Decompress(size_t output_bytes) = 0;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 1aea3a9..60093ab 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -78,6 +78,11 @@
<< "Expected: " << kCowMagicNumber;
return false;
}
+ if (header_.header_size != sizeof(CowHeader)) {
+ LOG(ERROR) << "Header size unknown, read " << header_.header_size << ", expected "
+ << sizeof(CowHeader);
+ return false;
+ }
if ((header_.major_version != kCowVersionMajor) ||
(header_.minor_version != kCowVersionMinor)) {
@@ -233,6 +238,9 @@
case kCowCompressGz:
decompressor = IDecompressor::Gz();
break;
+ case kCowCompressBrotli:
+ decompressor = IDecompressor::Brotli();
+ break;
default:
LOG(ERROR) << "Unknown compression type: " << op.compression;
return false;
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 6970ec1..75e54f7 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -26,6 +26,7 @@
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapuserd_client.h>
#include <storage_literals/storage_literals.h>
namespace android {
@@ -43,17 +44,29 @@
cow_product_ = std::make_unique<TemporaryFile>();
ASSERT_GE(cow_product_->fd, 0) << strerror(errno);
+ cow_system_1_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_system_1_->fd, 0) << strerror(errno);
+
+ cow_product_1_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_product_1_->fd, 0) << strerror(errno);
+
size_ = 100_MiB;
}
void TearDown() override {
cow_system_ = nullptr;
cow_product_ = nullptr;
+
+ cow_system_1_ = nullptr;
+ cow_product_1_ = nullptr;
}
std::unique_ptr<TemporaryFile> cow_system_;
std::unique_ptr<TemporaryFile> cow_product_;
+ std::unique_ptr<TemporaryFile> cow_system_1_;
+ std::unique_ptr<TemporaryFile> cow_product_1_;
+
unique_fd sys_fd_;
unique_fd product_fd_;
size_t size_;
@@ -71,12 +84,14 @@
void Init();
void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
- void CreateSystemDmUser();
- void CreateProductDmUser();
+ void CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow);
+ void CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow);
void StartSnapuserdDaemon();
void CreateSnapshotDevices();
+ void SwitchSnapshotDevices();
- void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf);
+ void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer);
+ SnapuserdClient client_;
};
void SnapuserdTest::Init() {
@@ -112,7 +127,7 @@
// Read from system partition from offset 0 of size 100MB
ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);
- // Read from system partition from offset 0 of size 100MB
+ // Read from product partition from offset 0 of size 100MB
ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
}
@@ -162,14 +177,15 @@
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);
}
-void SnapuserdTest::CreateSystemDmUser() {
+void SnapuserdTest::CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow) {
unique_fd system_a_fd;
std::string cmd;
+ system_device_name_.clear();
// Create a COW device. Number of sectors is chosen random which can
// hold at least 400MB of data
@@ -180,7 +196,7 @@
int err = ioctl(system_a_fd.get(), BLKGETSIZE, &system_blksize_);
ASSERT_GE(err, 0);
- std::string str(cow_system_->path);
+ std::string str(cow->path);
std::size_t found = str.find_last_of("/\\");
ASSERT_NE(found, std::string::npos);
system_device_name_ = str.substr(found + 1);
@@ -189,9 +205,10 @@
system(cmd.c_str());
}
-void SnapuserdTest::CreateProductDmUser() {
+void SnapuserdTest::CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow) {
unique_fd product_a_fd;
std::string cmd;
+ product_device_name_.clear();
// Create a COW device. Number of sectors is chosen random which can
// hold at least 400MB of data
@@ -202,7 +219,7 @@
int err = ioctl(product_a_fd.get(), BLKGETSIZE, &product_blksize_);
ASSERT_GE(err, 0);
- std::string str(cow_product_->path);
+ std::string str(cow->path);
std::size_t found = str.find_last_of("/\\");
ASSERT_NE(found, std::string::npos);
product_device_name_ = str.substr(found + 1);
@@ -212,15 +229,16 @@
}
void SnapuserdTest::StartSnapuserdDaemon() {
- // Start the snapuserd daemon
- if (fork() == 0) {
- 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);
- }
- }
+ int ret;
+
+ ret = client_.StartSnapuserd();
+ ASSERT_EQ(ret, 0);
+
+ ret = client_.InitializeSnapuserd(cow_system_->path, "/dev/block/mapper/system_a");
+ ASSERT_EQ(ret, 0);
+
+ ret = client_.InitializeSnapuserd(cow_product_->path, "/dev/block/mapper/product_a");
+ ASSERT_EQ(ret, 0);
}
void SnapuserdTest::CreateSnapshotDevices() {
@@ -243,9 +261,29 @@
system(cmd.c_str());
}
-void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf) {
+void SnapuserdTest::SwitchSnapshotDevices() {
+ std::string cmd;
+
+ cmd = "dmctl create system-snapshot-1 -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 product-snapshot-1 -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());
+}
+
+void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer) {
loff_t offset = 0;
- std::unique_ptr<uint8_t[]> buffer = std::move(buf);
+ // std::unique_ptr<uint8_t[]> buffer = std::move(buf);
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
@@ -326,8 +364,8 @@
CreateCowDevice(cow_system_);
CreateCowDevice(cow_product_);
- CreateSystemDmUser();
- CreateProductDmUser();
+ CreateSystemDmUser(cow_system_);
+ CreateProductDmUser(cow_product_);
StartSnapuserdDaemon();
@@ -335,11 +373,44 @@
snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
ASSERT_TRUE(snapshot_fd > 0);
- TestIO(snapshot_fd, std::move(system_buffer_));
+ TestIO(snapshot_fd, 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_));
+ TestIO(snapshot_fd, product_buffer_);
+
+ // Sequence of operations for transition
+ CreateCowDevice(cow_system_1_);
+ CreateCowDevice(cow_product_1_);
+
+ CreateSystemDmUser(cow_system_1_);
+ CreateProductDmUser(cow_product_1_);
+
+ std::vector<std::pair<std::string, std::string>> vec;
+ vec.push_back(std::make_pair(cow_system_1_->path, "/dev/block/mapper/system_a"));
+ vec.push_back(std::make_pair(cow_product_1_->path, "/dev/block/mapper/product_a"));
+
+ // Start the second stage deamon and send the devices
+ ASSERT_EQ(client_.RestartSnapuserd(vec), 0);
+
+ // TODO: This is not switching snapshot device but creates a new table;
+ // however, it should serve the testing purpose.
+ SwitchSnapshotDevices();
+
+ // Stop the first stage daemon
+ ASSERT_EQ(client_.StopSnapuserd(true), 0);
+
+ // Test the IO again with the second stage daemon
+ snapshot_fd.reset(open("/dev/block/mapper/system-snapshot-1", O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+ TestIO(snapshot_fd, system_buffer_);
+
+ snapshot_fd.reset(open("/dev/block/mapper/product-snapshot-1", O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+ TestIO(snapshot_fd, product_buffer_);
+
+ // Stop the second stage daemon
+ ASSERT_EQ(client_.StopSnapuserd(false), 0);
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 76238c2..70fdac1 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>
@@ -30,6 +32,45 @@
static_assert(sizeof(off_t) == sizeof(uint64_t));
+bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+ if (!ValidateNewBlock(new_block)) {
+ return false;
+ }
+ return EmitCopy(new_block, old_block);
+}
+
+bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitRawBlocks(new_block_start, data, size);
+}
+
+bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitZeroBlocks(new_block_start, num_blocks);
+}
+
+bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
+ if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
+ LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
+ << options_.max_blocks.value();
+ return false;
+ }
+ return true;
+}
+
CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
SetupHeaders();
}
@@ -39,17 +80,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,41 +133,56 @@
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++;
-
+bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
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;
}
-bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- if (size % header_.block_size != 0) {
- LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
- << header_.block_size;
- return false;
- }
-
+bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
uint64_t pos;
if (!GetDataPos(&pos)) {
return false;
@@ -103,8 +190,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 +205,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,26 +217,24 @@
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;
}
return true;
}
-bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+bool CowWriter::EmitZeroBlocks(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 +254,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 +290,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 +303,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 +316,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]);
+uint64_t CowWriter::GetCowSize() {
+ return header_.ops_offset + header_.num_ops * sizeof(CowOperation);
}
bool CowWriter::GetDataPos(uint64_t* pos) {
@@ -246,9 +338,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..245da0c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -16,6 +16,7 @@
#include <stdint.h>
+#include <optional>
#include <string>
#include <android-base/unique_fd.h>
@@ -27,6 +28,9 @@
struct CowOptions {
uint32_t block_size = 4096;
std::string compression;
+
+ // Maximum number of blocks that can be written.
+ std::optional<uint64_t> max_blocks;
};
// Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -39,21 +43,29 @@
// Encode an operation that copies the contents of |old_block| to the
// location of |new_block|.
- virtual bool AddCopy(uint64_t new_block, uint64_t old_block) = 0;
+ bool AddCopy(uint64_t new_block, uint64_t old_block);
// Encode a sequence of raw blocks. |size| must be a multiple of the block size.
- virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
// 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;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
- // 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();
- virtual size_t GetCowSize() = 0;
+ // Return number of bytes the cow image occupies on disk.
+ virtual uint64_t GetCowSize() = 0;
+
+ const CowOptions& options() { return options_; }
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+
+ bool ValidateNewBlock(uint64_t new_block);
protected:
CowOptions options_;
@@ -61,24 +73,31 @@
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 Flush() override;
- bool Finalize() override;
+ uint64_t GetCowSize() override;
- size_t GetCowSize() override;
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) 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 +109,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/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
index e757579..6331edb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -14,85 +14,94 @@
#pragma once
+#include <linux/types.h>
#include <stdint.h>
+#include <stdlib.h>
+
+#include <csignal>
+#include <cstring>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapuserd_kernel.h>
namespace android {
namespace snapshot {
-// Kernel COW header fields
-static constexpr uint32_t SNAP_MAGIC = 0x70416e53;
+using android::base::unique_fd;
-static constexpr uint32_t SNAPSHOT_DISK_VERSION = 1;
+class BufferSink : public IByteSink {
+ public:
+ void Initialize(size_t size);
+ void* GetBufPtr() { return buffer_.get(); }
+ void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
+ void* GetPayloadBuffer(size_t size);
+ void* GetBuffer(size_t requested, size_t* actual) override;
+ void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
+ struct dm_user_header* GetHeaderPtr();
+ bool ReturnData(void*, size_t) override { return true; }
+ void ResetBufferOffset() { buffer_offset_ = 0; }
-static constexpr uint32_t NUM_SNAPSHOT_HDR_CHUNKS = 1;
-
-static constexpr uint32_t SNAPSHOT_VALID = 1;
-
-/*
- * The basic unit of block I/O is a sector. It is used in a number of contexts
- * in Linux (blk, bio, genhd). The size of one sector is 512 = 2**9
- * bytes. Variables of type sector_t represent an offset or size that is a
- * multiple of 512 bytes. Hence these two constants.
- */
-static constexpr uint32_t SECTOR_SHIFT = 9;
-
-typedef __u64 sector_t;
-typedef sector_t chunk_t;
-
-static constexpr uint32_t CHUNK_SIZE = 8;
-static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);
-
-static constexpr uint32_t BLOCK_SIZE = 4096;
-static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SIZE) - 1);
-
-// This structure represents the kernel COW header.
-// All the below fields should be in Little Endian format.
-struct disk_header {
- uint32_t magic;
-
- /*
- * Is this snapshot valid. There is no way of recovering
- * an invalid snapshot.
- */
- uint32_t valid;
-
- /*
- * Simple, incrementing version. no backward
- * compatibility.
- */
- uint32_t version;
-
- /* In sectors */
- uint32_t chunk_size;
-} __packed;
-
-// A disk exception is a mapping of old_chunk to new_chunk
-// old_chunk is the chunk ID of a dm-snapshot device.
-// new_chunk is the chunk ID of the COW device.
-struct disk_exception {
- uint64_t old_chunk;
- uint64_t new_chunk;
-} __packed;
-
-// Control structures to communicate with dm-user
-// It comprises of header and a payload
-struct dm_user_header {
- __u64 seq;
- __u64 type;
- __u64 flags;
- __u64 sector;
- __u64 len;
- __u64 io_in_progress;
-} __attribute__((packed));
-
-struct dm_user_payload {
- __u8 buf[];
+ private:
+ std::unique_ptr<uint8_t[]> buffer_;
+ loff_t buffer_offset_;
+ size_t buffer_size_;
};
-// Message comprising both header and payload
-struct dm_user_message {
- struct dm_user_header header;
- struct dm_user_payload payload;
+class Snapuserd final {
+ public:
+ Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device)
+ : cow_device_(in_cow_device),
+ backing_store_device_(in_backing_store_device),
+ metadata_read_done_(false) {}
+
+ int Init();
+ int Run();
+ int ReadDmUserHeader();
+ int WriteDmUserPayload(size_t size);
+ int ConstructKernelCowHeader();
+ int ReadMetadata();
+ int ZerofillDiskExceptions(size_t read_size);
+ int ReadDiskExceptions(chunk_t chunk, size_t size);
+ int ReadData(chunk_t chunk, size_t size);
+
+ private:
+ int ProcessReplaceOp(const CowOperation* cow_op);
+ int ProcessCopyOp(const CowOperation* cow_op);
+ int ProcessZeroOp();
+
+ std::string cow_device_;
+ std::string backing_store_device_;
+
+ unique_fd cow_fd_;
+ unique_fd backing_store_fd_;
+ unique_fd ctrl_fd_;
+
+ uint32_t exceptions_per_area_;
+
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+ std::unique_ptr<CowReader> reader_;
+
+ // Vector of disk exception which is a
+ // mapping of old-chunk to new-chunk
+ std::vector<std::unique_ptr<uint8_t[]>> vec_;
+
+ // Index - Chunk ID
+ // Value - cow operation
+ std::vector<const CowOperation*> chunk_vec_;
+
+ bool metadata_read_done_;
+ BufferSink bufsink_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
new file mode 100644
index 0000000..2d9d729
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -0,0 +1,73 @@
+// 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.
+
+#pragma once
+
+#include <arpa/inet.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint32_t PACKET_SIZE = 512;
+static constexpr uint32_t MAX_CONNECT_RETRY_COUNT = 10;
+
+class SnapuserdClient {
+ private:
+ int sockfd_ = 0;
+
+ int Sendmsg(const char* msg, size_t size);
+ std::string Receivemsg();
+ int StartSnapuserdaemon(std::string socketname);
+ bool ConnectToServerSocket(std::string socketname);
+ bool ConnectToServer();
+
+ void DisconnectFromServer() { close(sockfd_); }
+
+ std::string GetSocketNameFirstStage() {
+ static std::string snapd_one("snapdone");
+ return snapd_one;
+ }
+
+ std::string GetSocketNameSecondStage() {
+ static std::string snapd_two("snapdtwo");
+ return snapd_two;
+ }
+
+ public:
+ int StartSnapuserd();
+ int StopSnapuserd(bool firstStageDaemon);
+ int RestartSnapuserd(std::vector<std::pair<std::string, std::string>>& vec);
+ int InitializeSnapuserd(std::string cow_device, std::string backing_device);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
new file mode 100644
index 0000000..c0d3c5e
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
@@ -0,0 +1,47 @@
+// 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.
+
+#pragma once
+
+#include <libsnapshot/snapuserd_server.h>
+
+namespace android {
+namespace snapshot {
+
+class Daemon {
+ // The Daemon class is a singleton to avoid
+ // instantiating more than once
+ public:
+ static Daemon& Instance() {
+ static Daemon instance;
+ return instance;
+ }
+
+ int StartServer(std::string socketname);
+ bool IsRunning();
+ void Run();
+
+ private:
+ bool is_running_;
+
+ Daemon();
+ Daemon(Daemon const&) = delete;
+ void operator=(Daemon const&) = delete;
+
+ SnapuserdServer server_;
+ static void SignalHandler(int signal);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
new file mode 100644
index 0000000..1a6ba8f
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
@@ -0,0 +1,97 @@
+// 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.
+
+#pragma once
+
+namespace android {
+namespace snapshot {
+
+// Kernel COW header fields
+static constexpr uint32_t SNAP_MAGIC = 0x70416e53;
+
+static constexpr uint32_t SNAPSHOT_DISK_VERSION = 1;
+
+static constexpr uint32_t NUM_SNAPSHOT_HDR_CHUNKS = 1;
+
+static constexpr uint32_t SNAPSHOT_VALID = 1;
+
+/*
+ * The basic unit of block I/O is a sector. It is used in a number of contexts
+ * in Linux (blk, bio, genhd). The size of one sector is 512 = 2**9
+ * bytes. Variables of type sector_t represent an offset or size that is a
+ * multiple of 512 bytes. Hence these two constants.
+ */
+static constexpr uint32_t SECTOR_SHIFT = 9;
+
+typedef __u64 sector_t;
+typedef sector_t chunk_t;
+
+static constexpr uint32_t CHUNK_SIZE = 8;
+static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);
+
+static constexpr uint32_t BLOCK_SIZE = 4096;
+static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SIZE) - 1);
+
+// This structure represents the kernel COW header.
+// All the below fields should be in Little Endian format.
+struct disk_header {
+ uint32_t magic;
+
+ /*
+ * Is this snapshot valid. There is no way of recovering
+ * an invalid snapshot.
+ */
+ uint32_t valid;
+
+ /*
+ * Simple, incrementing version. no backward
+ * compatibility.
+ */
+ uint32_t version;
+
+ /* In sectors */
+ uint32_t chunk_size;
+} __packed;
+
+// A disk exception is a mapping of old_chunk to new_chunk
+// old_chunk is the chunk ID of a dm-snapshot device.
+// new_chunk is the chunk ID of the COW device.
+struct disk_exception {
+ uint64_t old_chunk;
+ uint64_t new_chunk;
+} __packed;
+
+// Control structures to communicate with dm-user
+// It comprises of header and a payload
+struct dm_user_header {
+ __u64 seq;
+ __u64 type;
+ __u64 flags;
+ __u64 sector;
+ __u64 len;
+ __u64 io_in_progress;
+} __attribute__((packed));
+
+struct dm_user_payload {
+ __u8 buf[];
+};
+
+// Message comprising both header and payload
+struct dm_user_message {
+ struct dm_user_header header;
+ struct dm_user_payload payload;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
new file mode 100644
index 0000000..79b883a
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
@@ -0,0 +1,115 @@
+// 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.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <arpa/inet.h>
+#include <cutils/sockets.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <cstdio>
+#include <cstring>
+#include <functional>
+#include <future>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint32_t MAX_PACKET_SIZE = 512;
+
+enum class DaemonOperations {
+ START,
+ QUERY,
+ TERMINATING,
+ STOP,
+ INVALID,
+};
+
+class Client {
+ private:
+ std::unique_ptr<std::thread> threadHandler_;
+
+ public:
+ void SetThreadHandler(std::function<void(void)> func) {
+ threadHandler_ = std::make_unique<std::thread>(func);
+ }
+
+ std::unique_ptr<std::thread>& GetThreadHandler() { return threadHandler_; }
+};
+
+class Stoppable {
+ std::promise<void> exitSignal_;
+ std::future<void> futureObj_;
+
+ public:
+ Stoppable() : futureObj_(exitSignal_.get_future()) {}
+
+ virtual ~Stoppable() {}
+
+ virtual void ThreadStart(std::string cow_device, std::string backing_device) = 0;
+
+ bool StopRequested() {
+ // checks if value in future object is available
+ if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
+ return false;
+ return true;
+ }
+ // Request the thread to stop by setting value in promise object
+ void StopThreads() { exitSignal_.set_value(); }
+};
+
+class SnapuserdServer : public Stoppable {
+ private:
+ android::base::unique_fd sockfd_;
+ bool terminating_;
+ std::vector<std::unique_ptr<Client>> clients_vec_;
+ void ThreadStart(std::string cow_device, std::string backing_device) override;
+ void ShutdownThreads();
+ DaemonOperations Resolveop(std::string& input);
+ std::string GetDaemonStatus();
+ void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
+
+ void SetTerminating() { terminating_ = true; }
+
+ bool IsTerminating() { return terminating_; }
+
+ public:
+ ~SnapuserdServer() { clients_vec_.clear(); }
+
+ SnapuserdServer() { terminating_ = false; }
+
+ int Start(std::string socketname);
+ int AcceptClient();
+ int Receivemsg(int fd);
+ int Sendmsg(int fd, char* msg, size_t len);
+ std::string Recvmsg(int fd, int* ret);
+};
+
+} // namespace snapshot
+} // namespace android
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 d3f4f70..34481b7 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -14,25 +14,11 @@
* limitations under the License.
*/
-#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>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm.h>
-#include <libsnapshot/cow_reader.h>
-#include <libsnapshot/cow_writer.h>
#include <libsnapshot/snapuserd.h>
+#include <libsnapshot/snapuserd_daemon.h>
+#include <libsnapshot/snapuserd_server.h>
namespace android {
namespace snapshot {
@@ -60,140 +46,36 @@
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);
+void BufferSink::Initialize(size_t size) {
+ buffer_size_ = size;
+ buffer_offset_ = 0;
+ buffer_ = std::make_unique<uint8_t[]>(size);
}
-bool Daemon::IsRunning() {
- return is_running_;
+void* BufferSink::GetPayloadBuffer(size_t size) {
+ if ((buffer_size_ - buffer_offset_) < size) return nullptr;
+
+ char* buffer = reinterpret_cast<char*>(GetBufPtr());
+ struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+ return (char*)msg->payload.buf + buffer_offset_;
}
-void Daemon::SignalHandler(int signal) {
- LOG(DEBUG) << "Snapuserd received signal: " << signal;
- switch (signal) {
- case SIGINT:
- case SIGTERM: {
- Daemon::Instance().is_running_ = false;
- break;
- }
+void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
+ void* buf = GetPayloadBuffer(requested);
+ if (!buf) {
+ *actual = 0;
+ return nullptr;
}
+ *actual = requested;
+ return buf;
}
-class BufferSink : public IByteSink {
- public:
- void Initialize(size_t size) {
- buffer_size_ = size;
- buffer_offset_ = 0;
- buffer_ = std::make_unique<uint8_t[]>(size);
- }
-
- void* GetBufPtr() { return buffer_.get(); }
-
- void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
-
- void* GetPayloadBuffer(size_t size) {
- if ((buffer_size_ - buffer_offset_) < size) return nullptr;
-
- char* buffer = reinterpret_cast<char*>(GetBufPtr());
- struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
- return (char*)msg->payload.buf + buffer_offset_;
- }
-
- void* GetBuffer(size_t requested, size_t* actual) override {
- void* buf = GetPayloadBuffer(requested);
- if (!buf) {
- *actual = 0;
- return nullptr;
- }
- *actual = requested;
- return buf;
- }
-
- void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
-
- struct dm_user_header* GetHeaderPtr() {
- CHECK(sizeof(struct dm_user_header) <= buffer_size_);
- char* buf = reinterpret_cast<char*>(GetBufPtr());
- struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
- return header;
- }
-
- bool ReturnData(void*, size_t) override { return true; }
- void ResetBufferOffset() { buffer_offset_ = 0; }
-
- private:
- std::unique_ptr<uint8_t[]> buffer_;
- loff_t buffer_offset_;
- size_t buffer_size_;
-};
-
-class Snapuserd final {
- public:
- Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device)
- : in_cow_device_(in_cow_device),
- in_backing_store_device_(in_backing_store_device),
- metadata_read_done_(false) {}
-
- int Run();
- int ReadDmUserHeader();
- int WriteDmUserPayload(size_t size);
- int ConstructKernelCowHeader();
- int ReadMetadata();
- int ZerofillDiskExceptions(size_t read_size);
- int ReadDiskExceptions(chunk_t chunk, size_t size);
- int ReadData(chunk_t chunk, size_t size);
-
- private:
- int ProcessReplaceOp(const CowOperation* cow_op);
- int ProcessCopyOp(const CowOperation* cow_op);
- int ProcessZeroOp();
-
- std::string in_cow_device_;
- std::string in_backing_store_device_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
- unique_fd ctrl_fd_;
-
- uint32_t exceptions_per_area_;
-
- std::unique_ptr<ICowOpIter> cowop_iter_;
- std::unique_ptr<CowReader> reader_;
-
- // Vector of disk exception which is a
- // mapping of old-chunk to new-chunk
- std::vector<std::unique_ptr<uint8_t[]>> vec_;
-
- // Index - Chunk ID
- // Value - cow operation
- std::vector<const CowOperation*> chunk_vec_;
-
- bool metadata_read_done_;
- BufferSink bufsink_;
-};
+struct dm_user_header* BufferSink::GetHeaderPtr() {
+ CHECK(sizeof(struct dm_user_header) <= buffer_size_);
+ char* buf = reinterpret_cast<char*>(GetBufPtr());
+ struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
+ return header;
+}
// Construct kernel COW header in memory
// This header will be in sector 0. The IO
@@ -581,9 +463,12 @@
// Read Header from dm-user misc device. This gives
// us the sector number for which IO is issued by dm-snapshot device
int Snapuserd::ReadDmUserHeader() {
- if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
- PLOG(ERROR) << "Control read failed";
- return -1;
+ int ret;
+
+ ret = read(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header));
+ if (ret < 0) {
+ PLOG(ERROR) << "Control-read failed with: " << ret;
+ return ret;
}
return sizeof(struct dm_user_header);
@@ -600,22 +485,20 @@
return sizeof(struct dm_user_header) + size;
}
-// Start the daemon.
-// TODO: Handle signals
-int Snapuserd::Run() {
- backing_store_fd_.reset(open(in_backing_store_device_.c_str(), O_RDONLY));
+int Snapuserd::Init() {
+ backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
if (backing_store_fd_ < 0) {
- LOG(ERROR) << "Open Failed: " << in_backing_store_device_;
+ LOG(ERROR) << "Open Failed: " << backing_store_device_;
return 1;
}
- cow_fd_.reset(open(in_cow_device_.c_str(), O_RDWR));
+ cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
if (cow_fd_ < 0) {
- LOG(ERROR) << "Open Failed: " << in_cow_device_;
+ LOG(ERROR) << "Open Failed: " << cow_device_;
return 1;
}
- std::string str(in_cow_device_);
+ std::string str(cow_device_);
std::size_t found = str.find_last_of("/\\");
CHECK(found != std::string::npos);
std::string device_name = str.substr(found + 1);
@@ -625,7 +508,7 @@
auto& dm = dm::DeviceMapper::Instance();
std::string uuid;
if (!dm.GetDmDeviceUuidByName(device_name, &uuid)) {
- LOG(ERROR) << "Unable to find UUID for " << in_cow_device_;
+ LOG(ERROR) << "Unable to find UUID for " << cow_device_;
return 1;
}
@@ -638,8 +521,6 @@
return 1;
}
- int ret = 0;
-
// Allocate the buffer which is used to communicate between
// daemon and dm-user. The buffer comprises of header and a fixed payload.
// If the dm-user requests a big IO, the IO will be broken into chunks
@@ -647,138 +528,125 @@
size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
bufsink_.Initialize(buf_size);
- while (true) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
+ return 0;
+}
- bufsink_.Clear();
+int Snapuserd::Run() {
+ int ret = 0;
- ret = ReadDmUserHeader();
- if (ret < 0) return ret;
+ struct dm_user_header* header = bufsink_.GetHeaderPtr();
- LOG(DEBUG) << "dm-user returned " << ret << " bytes";
+ bufsink_.Clear();
- LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
- LOG(DEBUG) << "msg->type: " << std::hex << header->type;
- LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
- LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
- LOG(DEBUG) << "msg->len: " << std::hex << header->len;
+ ret = ReadDmUserHeader();
+ if (ret < 0) return ret;
- switch (header->type) {
- case DM_USER_MAP_READ: {
- size_t remaining_size = header->len;
- loff_t offset = 0;
- header->io_in_progress = 0;
- ret = 0;
- do {
- size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+ LOG(DEBUG) << "dm-user returned " << ret << " bytes";
- // Request to sector 0 is always for kernel
- // representation of COW header. This IO should be only
- // once during dm-snapshot device creation. We should
- // never see multiple IO requests. Additionally this IO
- // will always be a single 4k.
- if (header->sector == 0) {
- // Read the metadata from internal COW device
- // and build the in-memory data structures
- // for all the operations in the internal COW.
- if (!metadata_read_done_ && ReadMetadata()) {
- LOG(ERROR) << "Metadata read failed";
- return 1;
+ LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
+ LOG(DEBUG) << "msg->type: " << std::hex << header->type;
+ LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
+ LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
+ LOG(DEBUG) << "msg->len: " << std::hex << header->len;
+
+ switch (header->type) {
+ case DM_USER_MAP_READ: {
+ size_t remaining_size = header->len;
+ loff_t offset = 0;
+ header->io_in_progress = 0;
+ ret = 0;
+ do {
+ size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+
+ // Request to sector 0 is always for kernel
+ // representation of COW header. This IO should be only
+ // once during dm-snapshot device creation. We should
+ // never see multiple IO requests. Additionally this IO
+ // will always be a single 4k.
+ if (header->sector == 0) {
+ // Read the metadata from internal COW device
+ // and build the in-memory data structures
+ // for all the operations in the internal COW.
+ if (!metadata_read_done_ && ReadMetadata()) {
+ LOG(ERROR) << "Metadata read failed";
+ return 1;
+ }
+ metadata_read_done_ = true;
+
+ CHECK(read_size == BLOCK_SIZE);
+ ret = ConstructKernelCowHeader();
+ if (ret < 0) return ret;
+ } else {
+ // Convert the sector number to a chunk ID.
+ //
+ // Check if the chunk ID represents a metadata
+ // page. If the chunk ID is not found in the
+ // vector, then it points to a metadata page.
+ chunk_t chunk = (header->sector >> CHUNK_SHIFT);
+
+ if (chunk >= chunk_vec_.size()) {
+ ret = ZerofillDiskExceptions(read_size);
+ if (ret < 0) {
+ LOG(ERROR) << "ZerofillDiskExceptions failed";
+ return ret;
}
- metadata_read_done_ = true;
-
- CHECK(read_size == BLOCK_SIZE);
- ret = ConstructKernelCowHeader();
- if (ret < 0) return ret;
+ } else if (chunk_vec_[chunk] == nullptr) {
+ ret = ReadDiskExceptions(chunk, read_size);
+ if (ret < 0) {
+ LOG(ERROR) << "ReadDiskExceptions failed";
+ return ret;
+ }
} else {
- // Convert the sector number to a chunk ID.
- //
- // Check if the chunk ID represents a metadata
- // page. If the chunk ID is not found in the
- // vector, then it points to a metadata page.
- chunk_t chunk = (header->sector >> CHUNK_SHIFT);
-
- if (chunk >= chunk_vec_.size()) {
- ret = ZerofillDiskExceptions(read_size);
- if (ret < 0) {
- LOG(ERROR) << "ZerofillDiskExceptions failed";
- return ret;
- }
- } else if (chunk_vec_[chunk] == nullptr) {
- ret = ReadDiskExceptions(chunk, read_size);
- if (ret < 0) {
- LOG(ERROR) << "ReadDiskExceptions failed";
- return ret;
- }
- } else {
- chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
- ret = ReadData(chunk + num_chunks_read, read_size);
- if (ret < 0) {
- LOG(ERROR) << "ReadData failed";
- return ret;
- }
+ chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
+ ret = ReadData(chunk + num_chunks_read, read_size);
+ if (ret < 0) {
+ LOG(ERROR) << "ReadData failed";
+ return ret;
}
}
+ }
- ssize_t written = WriteDmUserPayload(ret);
- if (written < 0) return written;
+ ssize_t written = WriteDmUserPayload(ret);
+ if (written < 0) return written;
- remaining_size -= ret;
- offset += ret;
- if (remaining_size) {
- LOG(DEBUG) << "Write done ret: " << ret
- << " remaining size: " << remaining_size;
- bufsink_.GetHeaderPtr()->io_in_progress = 1;
- }
- } while (remaining_size);
+ remaining_size -= ret;
+ offset += ret;
+ if (remaining_size) {
+ LOG(DEBUG) << "Write done ret: " << ret
+ << " remaining size: " << remaining_size;
+ bufsink_.GetHeaderPtr()->io_in_progress = 1;
+ }
+ } while (remaining_size);
- break;
- }
-
- case DM_USER_MAP_WRITE: {
- // TODO: After merge operation is completed, kernel issues write
- // to flush all the exception mappings where the merge is
- // completed. If dm-user routes the WRITE IO, we need to clear
- // in-memory data structures representing those exception
- // mappings.
- abort();
- break;
- }
+ break;
}
- LOG(DEBUG) << "read() finished, next message";
+ case DM_USER_MAP_WRITE: {
+ // TODO: After merge operation is completed, kernel issues write
+ // to flush all the exception mappings where the merge is
+ // completed. If dm-user routes the WRITE IO, we need to clear
+ // in-memory data structures representing those exception
+ // mappings.
+ abort();
+ break;
+ }
}
+ LOG(DEBUG) << "read() finished, next message";
+
return 0;
}
} // 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::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();
- }
+ daemon.StartServer(argv[1]);
+ daemon.Run();
return 0;
}
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
new file mode 100644
index 0000000..b10de35
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -0,0 +1,261 @@
+#include <android-base/logging.h>
+#include <libsnapshot/snapuserd_client.h>
+
+namespace android {
+namespace snapshot {
+
+bool SnapuserdClient::ConnectToServerSocket(std::string socketname) {
+ sockfd_ = 0;
+
+ sockfd_ =
+ socket_local_client(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (sockfd_ < 0) {
+ LOG(ERROR) << "Failed to connect to " << socketname;
+ return false;
+ }
+
+ std::string msg = "query";
+
+ int sendRet = Sendmsg(msg.c_str(), msg.size());
+ if (sendRet < 0) {
+ LOG(ERROR) << "Failed to send query message to snapuserd daemon with socket " << socketname;
+ DisconnectFromServer();
+ return false;
+ }
+
+ std::string str = Receivemsg();
+
+ if (str.find("fail") != std::string::npos) {
+ LOG(ERROR) << "Failed to receive message from snapuserd daemon with socket " << socketname;
+ DisconnectFromServer();
+ return false;
+ }
+
+ // If the daemon is passive then fallback to secondary active daemon. Daemon
+ // is passive during transition phase. Please see RestartSnapuserd()
+ if (str.find("passive") != std::string::npos) {
+ LOG(DEBUG) << "Snapuserd is passive with socket " << socketname;
+ DisconnectFromServer();
+ return false;
+ }
+
+ CHECK(str.find("active") != std::string::npos);
+
+ return true;
+}
+
+bool SnapuserdClient::ConnectToServer() {
+ if (ConnectToServerSocket(GetSocketNameFirstStage())) return true;
+
+ if (ConnectToServerSocket(GetSocketNameSecondStage())) return true;
+
+ return false;
+}
+
+int SnapuserdClient::Sendmsg(const char* msg, size_t size) {
+ int numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg, size, 0));
+ if (numBytesSent < 0) {
+ LOG(ERROR) << "Send failed " << strerror(errno);
+ return -1;
+ }
+
+ if ((uint)numBytesSent < size) {
+ LOG(ERROR) << "Partial data sent " << strerror(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+std::string SnapuserdClient::Receivemsg() {
+ char msg[PACKET_SIZE];
+ std::string msgStr("fail");
+ int ret;
+
+ ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, PACKET_SIZE, 0));
+ if (ret <= 0) {
+ LOG(ERROR) << "recv failed " << strerror(errno);
+ return msgStr;
+ }
+
+ msgStr.clear();
+ msgStr = msg;
+ return msgStr;
+}
+
+int SnapuserdClient::StopSnapuserd(bool firstStageDaemon) {
+ if (firstStageDaemon) {
+ sockfd_ = socket_local_client(GetSocketNameFirstStage().c_str(),
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (sockfd_ < 0) {
+ LOG(ERROR) << "Failed to connect to " << GetSocketNameFirstStage();
+ return -1;
+ }
+ } else {
+ if (!ConnectToServer()) {
+ LOG(ERROR) << "Failed to connect to socket " << GetSocketNameSecondStage();
+ return -1;
+ }
+ }
+
+ std::string msg = "stop";
+
+ int sendRet = Sendmsg(msg.c_str(), msg.size());
+ if (sendRet < 0) {
+ LOG(ERROR) << "Failed to send stop message to snapuserd daemon";
+ return -1;
+ }
+
+ DisconnectFromServer();
+
+ return 0;
+}
+
+int SnapuserdClient::StartSnapuserdaemon(std::string socketname) {
+ int retry_count = 0;
+
+ if (fork() == 0) {
+ const char* argv[] = {"/system/bin/snapuserd", socketname.c_str(), nullptr};
+ if (execv(argv[0], const_cast<char**>(argv))) {
+ LOG(ERROR) << "Failed to exec snapuserd daemon";
+ return -1;
+ }
+ }
+
+ // snapuserd is a daemon and will never exit; parent can't wait here
+ // to get the return code. Since Snapuserd starts the socket server,
+ // give it some time to fully launch.
+ //
+ // Try to connect to server to verify snapuserd server is started
+ while (retry_count < MAX_CONNECT_RETRY_COUNT) {
+ if (!ConnectToServer()) {
+ retry_count++;
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ } else {
+ close(sockfd_);
+ return 0;
+ }
+ }
+
+ LOG(ERROR) << "Failed to start snapuserd daemon";
+ return -1;
+}
+
+int SnapuserdClient::StartSnapuserd() {
+ if (StartSnapuserdaemon(GetSocketNameFirstStage()) < 0) return -1;
+
+ return 0;
+}
+
+int SnapuserdClient::InitializeSnapuserd(std::string cow_device, std::string backing_device) {
+ int ret = 0;
+
+ if (!ConnectToServer()) {
+ LOG(ERROR) << "Failed to connect to server ";
+ return -1;
+ }
+
+ std::string msg = "start," + cow_device + "," + backing_device;
+
+ ret = Sendmsg(msg.c_str(), msg.size());
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
+ return -1;
+ }
+
+ std::string str = Receivemsg();
+
+ if (str.find("fail") != std::string::npos) {
+ LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
+ return -1;
+ }
+
+ DisconnectFromServer();
+
+ LOG(DEBUG) << "Snapuserd daemon initialized with " << msg;
+ return 0;
+}
+
+/*
+ * Transition from first stage snapuserd daemon to second stage daemon involves
+ * series of steps viz:
+ *
+ * 1: Create new dm-user devices - This is done by libsnapshot
+ *
+ * 2: Spawn the new snapuserd daemon - This is the second stage daemon which
+ * will start the server but the dm-user misc devices is not binded yet.
+ *
+ * 3: Vector to this function contains pair of cow_device and source device.
+ * Ex: {{system_cow,system_a}, {product_cow, product_a}, {vendor_cow,
+ * vendor_a}}. This vector will be populated by the libsnapshot.
+ *
+ * 4: Initialize the Second stage daemon passing the information from the
+ * vector. This will bind the daemon with dm-user misc device and will be ready
+ * to serve the IO. Up until this point, first stage daemon is still active.
+ * However, client library will mark the first stage daemon as passive and hence
+ * all the control message from hereon will be sent to active second stage
+ * daemon.
+ *
+ * 5: Create new dm-snapshot table. This is done by libsnapshot. When new table
+ * is created, kernel will issue metadata read once again which will be served
+ * by second stage daemon. However, any active IO will still be served by first
+ * stage daemon.
+ *
+ * 6: Swap the snapshot table atomically - This is done by libsnapshot. Once
+ * the swapping is done, all the IO will be served by second stage daemon.
+ *
+ * 7: Stop the first stage daemon. After this point second stage daemon is
+ * completely active to serve the IO and merging process.
+ *
+ */
+int SnapuserdClient::RestartSnapuserd(std::vector<std::pair<std::string, std::string>>& vec) {
+ // Connect to first-stage daemon and send a terminate-request control
+ // message. This will not terminate the daemon but will mark the daemon as
+ // passive.
+ if (!ConnectToServer()) {
+ LOG(ERROR) << "Failed to connect to server ";
+ return -1;
+ }
+
+ std::string msg = "terminate-request";
+
+ int sendRet = Sendmsg(msg.c_str(), msg.size());
+ if (sendRet < 0) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
+ return -1;
+ }
+
+ std::string str = Receivemsg();
+
+ if (str.find("fail") != std::string::npos) {
+ LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
+ return -1;
+ }
+
+ CHECK(str.find("success") != std::string::npos);
+
+ DisconnectFromServer();
+
+ // Start the new daemon
+ if (StartSnapuserdaemon(GetSocketNameSecondStage()) < 0) {
+ LOG(ERROR) << "Failed to start new daemon at socket " << GetSocketNameSecondStage();
+ return -1;
+ }
+
+ LOG(DEBUG) << "Second stage Snapuserd daemon created successfully at socket "
+ << GetSocketNameSecondStage();
+ CHECK(vec.size() % 2 == 0);
+
+ for (int i = 0; i < vec.size(); i++) {
+ std::string& cow_device = vec[i].first;
+ std::string& base_device = vec[i].second;
+
+ InitializeSnapuserd(cow_device, base_device);
+ LOG(DEBUG) << "Daemon initialized with " << cow_device << " and " << base_device;
+ }
+
+ return 0;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
new file mode 100644
index 0000000..c1008b9
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
@@ -0,0 +1,53 @@
+#include <android-base/logging.h>
+#include <libsnapshot/snapuserd_daemon.h>
+
+namespace android {
+namespace snapshot {
+
+int Daemon::StartServer(std::string socketname) {
+ int ret;
+
+ ret = server_.Start(socketname);
+ if (ret < 0) {
+ LOG(ERROR) << "Snapuserd daemon failed to start...";
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
+
+Daemon::Daemon() {
+ is_running_ = true;
+ // TODO: Mask other signals - Bug 168258493
+ signal(SIGINT, Daemon::SignalHandler);
+ signal(SIGTERM, Daemon::SignalHandler);
+}
+
+bool Daemon::IsRunning() {
+ return is_running_;
+}
+
+void Daemon::Run() {
+ while (IsRunning()) {
+ if (server_.AcceptClient() == static_cast<int>(DaemonOperations::STOP)) {
+ Daemon::Instance().is_running_ = false;
+ }
+ }
+}
+
+void Daemon::SignalHandler(int signal) {
+ LOG(DEBUG) << "Snapuserd received signal: " << signal;
+ switch (signal) {
+ case SIGINT:
+ case SIGTERM: {
+ Daemon::Instance().is_running_ = false;
+ break;
+ }
+ default:
+ LOG(ERROR) << "Received unknown signal " << signal;
+ break;
+ }
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
new file mode 100644
index 0000000..1e8b642
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -0,0 +1,215 @@
+#include <android-base/logging.h>
+#include <libsnapshot/snapuserd.h>
+#include <libsnapshot/snapuserd_server.h>
+
+namespace android {
+namespace snapshot {
+
+DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
+ if (input == "start") return DaemonOperations::START;
+ if (input == "stop") return DaemonOperations::STOP;
+ if (input == "terminate-request") return DaemonOperations::TERMINATING;
+ if (input == "query") return DaemonOperations::QUERY;
+
+ return DaemonOperations::INVALID;
+}
+
+std::string SnapuserdServer::GetDaemonStatus() {
+ std::string msg = "";
+
+ if (IsTerminating())
+ msg = "passive";
+ else
+ msg = "active";
+
+ return msg;
+}
+
+void SnapuserdServer::Parsemsg(std::string const& msg, const char delim,
+ std::vector<std::string>& out) {
+ std::stringstream ss(msg);
+ std::string s;
+
+ while (std::getline(ss, s, delim)) {
+ out.push_back(s);
+ }
+}
+
+// new thread
+void SnapuserdServer::ThreadStart(std::string cow_device, std::string backing_device) {
+ Snapuserd snapd(cow_device, backing_device);
+ if (snapd.Init()) {
+ PLOG(ERROR) << "Snapuserd: Init failed";
+ exit(EXIT_FAILURE);
+ }
+
+ while (StopRequested() == false) {
+ int ret = snapd.Run();
+
+ if (ret == -ETIMEDOUT) continue;
+
+ if (ret < 0) {
+ PLOG(ERROR) << "snapd.Run() failed..." << ret;
+ }
+ }
+}
+
+void SnapuserdServer::ShutdownThreads() {
+ StopThreads();
+
+ for (auto& client : clients_vec_) {
+ auto& th = client->GetThreadHandler();
+
+ if (th->joinable()) th->join();
+ }
+}
+
+int SnapuserdServer::Sendmsg(int fd, char* msg, size_t size) {
+ int ret = TEMP_FAILURE_RETRY(send(fd, (char*)msg, size, 0));
+ if (ret < 0) {
+ PLOG(ERROR) << "Snapuserd:server: send() failed";
+ return -1;
+ }
+
+ if (ret < size) {
+ PLOG(ERROR) << "Partial data sent";
+ return -1;
+ }
+
+ return 0;
+}
+
+std::string SnapuserdServer::Recvmsg(int fd, int* ret) {
+ struct timeval tv;
+ fd_set set;
+ char msg[MAX_PACKET_SIZE];
+
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ FD_ZERO(&set);
+ FD_SET(fd, &set);
+ *ret = select(fd + 1, &set, NULL, NULL, &tv);
+ if (*ret == -1) { // select failed
+ return {};
+ } else if (*ret == 0) { // timeout
+ return {};
+ } else {
+ *ret = TEMP_FAILURE_RETRY(recv(fd, msg, MAX_PACKET_SIZE, 0));
+ if (*ret < 0) {
+ PLOG(ERROR) << "Snapuserd:server: recv failed";
+ return {};
+ } else if (*ret == 0) {
+ LOG(DEBUG) << "Snapuserd client disconnected";
+ return {};
+ } else {
+ std::string str(msg);
+ return str;
+ }
+ }
+}
+
+int SnapuserdServer::Receivemsg(int fd) {
+ char msg[MAX_PACKET_SIZE];
+ std::unique_ptr<Client> newClient;
+ int ret = 0;
+
+ while (1) {
+ memset(msg, '\0', MAX_PACKET_SIZE);
+ std::string str = Recvmsg(fd, &ret);
+
+ if (ret <= 0) {
+ LOG(DEBUG) << "recv failed with ret: " << ret;
+ return 0;
+ }
+
+ const char delim = ',';
+
+ std::vector<std::string> out;
+ Parsemsg(str, delim, out);
+ DaemonOperations op = Resolveop(out[0]);
+ memset(msg, '\0', MAX_PACKET_SIZE);
+
+ switch (op) {
+ case DaemonOperations::START: {
+ // Message format:
+ // start,<cow_device_path>,<source_device_path>
+ //
+ // Start the new thread which binds to dm-user misc device
+ newClient = std::make_unique<Client>();
+ newClient->SetThreadHandler(
+ std::bind(&SnapuserdServer::ThreadStart, this, out[1], out[2]));
+ clients_vec_.push_back(std::move(newClient));
+ sprintf(msg, "success");
+ Sendmsg(fd, msg, MAX_PACKET_SIZE);
+ return 0;
+ }
+ case DaemonOperations::STOP: {
+ // Message format: stop
+ //
+ // Stop all the threads gracefully and then shutdown the
+ // main thread
+ ShutdownThreads();
+ return static_cast<int>(DaemonOperations::STOP);
+ }
+ case DaemonOperations::TERMINATING: {
+ // Message format: terminate-request
+ //
+ // This is invoked during transition. First stage
+ // daemon will receive this request. First stage daemon
+ // will be considered as a passive daemon from hereon.
+ SetTerminating();
+ sprintf(msg, "success");
+ Sendmsg(fd, msg, MAX_PACKET_SIZE);
+ return 0;
+ }
+ case DaemonOperations::QUERY: {
+ // Message format: query
+ //
+ // As part of transition, Second stage daemon will be
+ // created before terminating the first stage daemon. Hence,
+ // for a brief period client may have to distiguish between
+ // first stage daemon and second stage daemon.
+ //
+ // Second stage daemon is marked as active and hence will
+ // be ready to receive control message.
+ std::string dstr = GetDaemonStatus();
+ memcpy(msg, dstr.c_str(), dstr.size());
+ Sendmsg(fd, msg, MAX_PACKET_SIZE);
+ if (dstr == "active")
+ break;
+ else
+ return 0;
+ }
+ default: {
+ sprintf(msg, "fail");
+ Sendmsg(fd, msg, MAX_PACKET_SIZE);
+ return 0;
+ }
+ }
+ }
+}
+
+int SnapuserdServer::Start(std::string socketname) {
+ sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM));
+ if (sockfd_ < 0) {
+ PLOG(ERROR) << "Failed to create server socket " << socketname;
+ return -1;
+ }
+
+ LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
+ return 0;
+}
+
+int SnapuserdServer::AcceptClient() {
+ int fd = accept(sockfd_.get(), NULL, NULL);
+ if (fd < 0) {
+ PLOG(ERROR) << "Socket accept failed: " << strerror(errno);
+ return -1;
+ }
+
+ return Receivemsg(fd);
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
index 57adaba..4646efc 100644
--- a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
+++ b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
@@ -30,7 +30,7 @@
interface IGateKeeperService {
/**
* Enrolls a password, returning the handle to the enrollment to be stored locally.
- * @param uid The Android user ID associated to this enrollment
+ * @param userId The Android user ID associated to this enrollment
* @param currentPasswordHandle The previously enrolled handle, or null if none
* @param currentPassword The previously enrolled plaintext password, or null if none.
* If provided, must verify against the currentPasswordHandle.
@@ -38,22 +38,22 @@
* upon success.
* @return an EnrollResponse or null on failure
*/
- GateKeeperResponse enroll(int uid, in @nullable byte[] currentPasswordHandle,
+ GateKeeperResponse enroll(int userId, in @nullable byte[] currentPasswordHandle,
in @nullable byte[] currentPassword, in byte[] desiredPassword);
/**
* Verifies an enrolled handle against a provided, plaintext blob.
- * @param uid The Android user ID associated to this enrollment
+ * @param userId The Android user ID associated to this enrollment
* @param enrolledPasswordHandle The handle against which the provided password will be
* verified.
* @param The plaintext blob to verify against enrolledPassword.
* @return a VerifyResponse, or null on failure.
*/
- GateKeeperResponse verify(int uid, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
+ GateKeeperResponse verify(int userId, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
/**
* Verifies an enrolled handle against a provided, plaintext blob.
- * @param uid The Android user ID associated to this enrollment
+ * @param userId The Android user ID associated to this enrollment
* @param challenge a challenge to authenticate agaisnt the device credential. If successful
* authentication occurs, this value will be written to the returned
* authentication attestation.
@@ -62,22 +62,22 @@
* @param The plaintext blob to verify against enrolledPassword.
* @return a VerifyResponse with an attestation, or null on failure.
*/
- GateKeeperResponse verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
+ GateKeeperResponse verifyChallenge(int userId, long challenge, in byte[] enrolledPasswordHandle,
in byte[] providedPassword);
/**
* Retrieves the secure identifier for the user with the provided Android ID,
* or 0 if none is found.
- * @param uid the Android user id
+ * @param userId the Android user id
*/
- long getSecureUserId(int uid);
+ long getSecureUserId(int userId);
/**
* Clears secure user id associated with the provided Android ID.
* Must be called when password is set to NONE.
- * @param uid the Android user id.
+ * @param userId the Android user id.
*/
- void clearSecureUserId(int uid);
+ void clearSecureUserId(int userId);
/**
* Notifies gatekeeper that device setup has been completed and any potentially still existing
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index c81a80e..b982dbc 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -76,9 +76,9 @@
virtual ~GateKeeperProxy() {
}
- void store_sid(uint32_t uid, uint64_t sid) {
+ void store_sid(uint32_t userId, uint64_t sid) {
char filename[21];
- snprintf(filename, sizeof(filename), "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", userId);
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
ALOGE("could not open file: %s: %s", filename, strerror(errno));
@@ -117,18 +117,18 @@
return false;
}
- void maybe_store_sid(uint32_t uid, uint64_t sid) {
+ void maybe_store_sid(uint32_t userId, uint64_t sid) {
char filename[21];
- snprintf(filename, sizeof(filename), "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", userId);
if (access(filename, F_OK) == -1) {
- store_sid(uid, sid);
+ store_sid(userId, sid);
}
}
- uint64_t read_sid(uint32_t uid) {
+ uint64_t read_sid(uint32_t userId) {
char filename[21];
uint64_t sid;
- snprintf(filename, sizeof(filename), "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", userId);
int fd = open(filename, O_RDONLY);
if (fd < 0) return 0;
read(fd, &sid, sizeof(sid));
@@ -136,30 +136,30 @@
return sid;
}
- void clear_sid(uint32_t uid) {
+ void clear_sid(uint32_t userId) {
char filename[21];
- snprintf(filename, sizeof(filename), "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", userId);
if (remove(filename) < 0) {
ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
- store_sid(uid, 0);
+ store_sid(userId, 0);
}
}
- // This should only be called on uids being passed to the GateKeeper HAL. It ensures that
+ // This should only be called on userIds being passed to the GateKeeper HAL. It ensures that
// secure storage shared across a GSI image and a host image will not overlap.
- uint32_t adjust_uid(uint32_t uid) {
+ uint32_t adjust_userId(uint32_t userId) {
static constexpr uint32_t kGsiOffset = 1000000;
- CHECK(uid < kGsiOffset);
+ CHECK(userId < kGsiOffset);
CHECK(hw_device != nullptr);
if (is_running_gsi) {
- return uid + kGsiOffset;
+ return userId + kGsiOffset;
}
- return uid;
+ return userId;
}
#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
- Status enroll(int32_t uid, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
+ Status enroll(int32_t userId, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
const std::optional<std::vector<uint8_t>>& currentPassword,
const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override {
IPCThreadState* ipc = IPCThreadState::self();
@@ -198,9 +198,10 @@
android::hardware::hidl_vec<uint8_t> newPwd;
newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
- uint32_t hw_uid = adjust_uid(uid);
+ uint32_t hw_userId = adjust_userId(userId);
Return<void> hwRes = hw_device->enroll(
- hw_uid, curPwdHandle, curPwd, newPwd, [&gkResponse](const GatekeeperResponse& rsp) {
+ hw_userId, curPwdHandle, curPwd, newPwd,
+ [&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
*gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
} else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
@@ -225,12 +226,12 @@
const gatekeeper::password_handle_t* handle =
reinterpret_cast<const gatekeeper::password_handle_t*>(
gkResponse->payload().data());
- store_sid(uid, handle->user_id);
+ store_sid(userId, handle->user_id);
GKResponse verifyResponse;
// immediately verify this password so we don't ask the user to enter it again
// if they just created it.
- auto status = verify(uid, gkResponse->payload(), desiredPassword, &verifyResponse);
+ auto status = verify(userId, gkResponse->payload(), desiredPassword, &verifyResponse);
if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
LOG(ERROR) << "Failed to verify password after enrolling";
}
@@ -239,13 +240,13 @@
return Status::ok();
}
- Status verify(int32_t uid, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+ Status verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override {
- return verifyChallenge(uid, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
+ return verifyChallenge(userId, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
gkResponse);
}
- Status verifyChallenge(int32_t uid, int64_t challenge,
+ Status verifyChallenge(int32_t userId, int64_t challenge,
const std::vector<uint8_t>& enrolledPasswordHandle,
const std::vector<uint8_t>& providedPassword,
GKResponse* gkResponse) override {
@@ -269,7 +270,7 @@
reinterpret_cast<const gatekeeper::password_handle_t*>(
enrolledPasswordHandle.data());
- uint32_t hw_uid = adjust_uid(uid);
+ uint32_t hw_userId = adjust_userId(userId);
android::hardware::hidl_vec<uint8_t> curPwdHandle;
curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
enrolledPasswordHandle.size());
@@ -278,7 +279,7 @@
providedPassword.size());
Return<void> hwRes = hw_device->verify(
- hw_uid, challenge, curPwdHandle, enteredPwd,
+ hw_userId, challenge, curPwdHandle, enteredPwd,
[&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
*gkResponse = GKResponse::ok(
@@ -315,18 +316,18 @@
}
}
- maybe_store_sid(uid, handle->user_id);
+ maybe_store_sid(userId, handle->user_id);
}
return Status::ok();
}
- Status getSecureUserId(int32_t uid, int64_t* sid) override {
- *sid = read_sid(uid);
+ Status getSecureUserId(int32_t userId, int64_t* sid) override {
+ *sid = read_sid(userId);
return Status::ok();
}
- Status clearSecureUserId(int32_t uid) override {
+ Status clearSecureUserId(int32_t userId) override {
IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
const int calling_uid = ipc->getCallingUid();
@@ -334,11 +335,11 @@
ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
return Status::ok();
}
- clear_sid(uid);
+ clear_sid(userId);
if (hw_device) {
- uint32_t hw_uid = adjust_uid(uid);
- hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
+ uint32_t hw_userId = adjust_userId(userId);
+ hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});
}
return Status::ok();
}
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
deleted file mode 120000
index f28a564..0000000
--- a/include/private/android_filesystem_config.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libcutils/include/private/android_filesystem_config.h
\ No newline at end of file
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.md b/init/README.md
index c3b64f6..6439393 100644
--- a/init/README.md
+++ b/init/README.md
@@ -31,14 +31,13 @@
extension. There are typically multiple of these in multiple
locations on the system, described below.
-/init.rc is the primary .rc file and is loaded by the init executable
-at the beginning of its execution. It is responsible for the initial
-set up of the system.
+`/system/etc/init/hw/init.rc` is the primary .rc file and is loaded by the init executable at the
+beginning of its execution. It is responsible for the initial set up of the system.
Init loads all of the files contained within the
-/{system,vendor,odm}/etc/init/ directories immediately after loading
-the primary /init.rc. This is explained in more details in the
-Imports section of this file.
+`/{system,system_ext,vendor,odm,product}/etc/init/` directories immediately after loading
+the primary `/system/etc/init/hw/init.rc`. This is explained in more details in the
+[Imports](#imports) section of this file.
Legacy devices without the first stage mount mechanism previously were
able to import init scripts during mount_all, however that is deprecated
@@ -689,29 +688,22 @@
There are only three times where the init executable imports .rc files:
- 1. When it imports /init.rc or the script indicated by the property
+ 1. When it imports `/system/etc/init/hw/init.rc` or the script indicated by the property
`ro.boot.init_rc` during initial boot.
- 2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
- devices immediately after importing /init.rc.
+ 2. When it imports `/{system,system_ext,vendor,odm,product}/etc/init/` immediately after
+ importing `/system/etc/init/hw/init.rc`.
3. (Deprecated) When it imports /{system,vendor,odm}/etc/init/ or .rc files
at specified paths during mount_all, not allowed for devices launching
after Q.
-The order that files are imported is a bit complex for legacy reasons
-and to keep backwards compatibility. It is not strictly guaranteed.
+The order that files are imported is a bit complex for legacy reasons. The below is guaranteed:
-The only correct way to guarantee that a command has been run before a
-different command is to either 1) place it in an Action with an
-earlier executed trigger, or 2) place it in an Action with the same
-trigger within the same file at an earlier line.
-
-Nonetheless, the de facto order for first stage mount devices is:
-1. /init.rc is parsed then recursively each of its imports are
+1. `/system/etc/init/hw/init.rc` is parsed then recursively each of its imports are
parsed.
-2. The contents of /system/etc/init/ are alphabetized and parsed
- sequentially, with imports happening recursively after each file is
- parsed.
-3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
+2. The contents of `/system/etc/init/` are alphabetized and parsed sequentially, with imports
+ happening recursively after each file is parsed.
+3. Step 2 is repeated for `/system_ext/etc/init`, `/vendor/etc/init`, `/odm/etc/init`,
+ `/product/etc/init`
The below pseudocode may explain this more clearly:
@@ -720,13 +712,17 @@
for (import : file.imports)
Import(import)
- Import(/init.rc)
- Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
+ Import(/system/etc/init/hw/init.rc)
+ Directories = [/system/etc/init, /system_ext/etc/init, /vendor/etc/init, /odm/etc/init, /product/etc/init]
for (directory : Directories)
files = <Alphabetical order of directory's contents>
for (file : files)
Import(file)
+Actions are executed in the order that they are parsed. For example the `post-fs-data` action(s)
+in `/system/etc/init/hw/init.rc` are always the first `post-fs-data` action(s) to be executed in
+order of how they appear in that file. Then the `post-fs-data` actions of the imports of
+`/system/etc/init/hw/init.rc` in the order that they're imported, etc.
Properties
----------
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/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 038b59e..a506575 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -37,6 +37,12 @@
#include "ThreadEntry.h"
bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
+#if defined(__aarch64__)
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ ptr &= (1ULL << 56) - 1;
+#endif
+
if (!VerifyReadWordArgs(ptr, out_value)) {
return false;
}
@@ -54,6 +60,12 @@
}
size_t BacktraceCurrent::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__aarch64__)
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ addr &= (1ULL << 56) - 1;
+#endif
+
backtrace_map_t map;
FillInMap(addr, &map);
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 624711f..82ff21c 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -52,11 +52,11 @@
unwinder.SetResolveNames(stack_map->ResolveNames());
stack_map->SetArch(regs->Arch());
if (stack_map->GetJitDebug() != nullptr) {
- unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
+ unwinder.SetJitDebug(stack_map->GetJitDebug());
}
#if !defined(NO_LIBDEXFILE_SUPPORT)
if (stack_map->GetDexFiles() != nullptr) {
- unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
+ unwinder.SetDexFiles(stack_map->GetDexFiles());
}
#endif
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
@@ -180,5 +180,10 @@
}
size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__aarch64__)
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ addr &= (1ULL << 56) - 1;
+#endif
return memory_->Read(addr, buffer, bytes);
}
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 04b8f66..b749d87 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -14,6 +14,11 @@
// limitations under the License.
//
+filegroup {
+ name: "android_filesystem_config_header",
+ srcs: ["include/private/android_filesystem_config.h"],
+}
+
// some files must not be compiled when building against Mingw
// they correspond to features not used by our host development tools
// which are also hard or even impossible to port to native Win32
@@ -28,6 +33,7 @@
name: "libcutils_headers",
vendor_available: true,
recovery_available: true,
+ ramdisk_available: true,
host_supported: true,
apex_available: [
"//apex_available:platform",
@@ -54,6 +60,7 @@
name: "libcutils_sockets",
vendor_available: true,
recovery_available: true,
+ ramdisk_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [
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/liblog/Android.bp b/liblog/Android.bp
index 59ab250..8f15541 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -98,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/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 8cc780a..75a419c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -131,7 +131,6 @@
support_system_process: true,
},
defaults: ["libunwindstack_defaults"],
-
srcs: ["DexFile.cpp"],
cflags: ["-DDEXFILE_SUPPORT"],
shared_libs: ["libdexfile_support"],
@@ -168,6 +167,7 @@
defaults: ["libunwindstack_defaults"],
visibility: [
+ "//external/gwp_asan",
"//system/core/debuggerd",
"//system/core/init",
"//system/core/libbacktrace",
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/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 57806c1..bcdbde8 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,6 +27,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <unwindstack/DexFiles.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
@@ -34,7 +35,7 @@
#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
-#include <unwindstack/DexFiles.h>
+#include "Check.h"
// Use the demangler from libc++.
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
@@ -142,13 +143,11 @@
void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
- frames_.clear();
- warnings_ = WARNING_NONE;
- last_error_.code = ERROR_NONE;
- last_error_.address = 0;
- elf_from_memory_not_file_ = false;
+ CHECK(arch_ != ARCH_UNKNOWN);
+ ClearErrors();
- ArchEnum arch = regs_->Arch();
+ frames_.clear();
+ elf_from_memory_not_file_ = false;
bool return_address_attempt = false;
bool adjust_pc = false;
@@ -169,7 +168,7 @@
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
}
- elf = map_info->GetElf(process_memory_, arch);
+ elf = map_info->GetElf(process_memory_, arch_);
// If this elf is memory backed, and there is a valid file, then set
// an indicator that we couldn't open the file.
if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
@@ -183,7 +182,7 @@
step_pc = rel_pc;
}
if (adjust_pc) {
- pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
+ pc_adjustment = GetPcAdjustment(rel_pc, elf, arch_);
} else {
pc_adjustment = 0;
}
@@ -311,7 +310,7 @@
std::string Unwinder::FormatFrame(const FrameData& frame) const {
std::string data;
- if (regs_->Is32Bit()) {
+ if (ArchIs32Bit(arch_)) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
} else {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
@@ -362,23 +361,33 @@
return FormatFrame(frames_[frame_num]);
}
-void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
- jit_debug->SetArch(arch);
+void Unwinder::SetJitDebug(JitDebug* jit_debug) {
+ CHECK(arch_ != ARCH_UNKNOWN);
+ jit_debug->SetArch(arch_);
jit_debug_ = jit_debug;
}
-void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
- dex_files->SetArch(arch);
+void Unwinder::SetDexFiles(DexFiles* dex_files) {
+ CHECK(arch_ != ARCH_UNKNOWN);
+ dex_files->SetArch(arch_);
dex_files_ = dex_files;
}
-bool UnwinderFromPid::Init(ArchEnum arch) {
+bool UnwinderFromPid::Init() {
+ CHECK(arch_ != ARCH_UNKNOWN);
+ if (initted_) {
+ return true;
+ }
+ initted_ = true;
+
if (pid_ == getpid()) {
maps_ptr_.reset(new LocalMaps());
} else {
maps_ptr_.reset(new RemoteMaps(pid_));
}
if (!maps_ptr_->Parse()) {
+ ClearErrors();
+ last_error_.code = ERROR_INVALID_MAP;
return false;
}
maps_ = maps_ptr_.get();
@@ -387,16 +396,24 @@
jit_debug_ptr_.reset(new JitDebug(process_memory_));
jit_debug_ = jit_debug_ptr_.get();
- SetJitDebug(jit_debug_, arch);
+ SetJitDebug(jit_debug_);
#if defined(DEXFILE_SUPPORT)
dex_files_ptr_.reset(new DexFiles(process_memory_));
dex_files_ = dex_files_ptr_.get();
- SetDexFiles(dex_files_, arch);
+ SetDexFiles(dex_files_);
#endif
return true;
}
+void UnwinderFromPid::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
+ const std::vector<std::string>* map_suffixes_to_ignore) {
+ if (!Init()) {
+ return;
+ }
+ Unwinder::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore);
+}
+
FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc, ArchEnum arch, Maps* maps,
JitDebug* jit_debug,
std::shared_ptr<Memory> process_memory,
@@ -449,8 +466,7 @@
}
FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
- return BuildFrameFromPcOnly(pc, regs_ ? regs_->Arch() : ARCH_UNKNOWN, maps_, jit_debug_,
- process_memory_, resolve_names_);
+ return BuildFrameFromPcOnly(pc, arch_, maps_, jit_debug_, process_memory_, resolve_names_);
}
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Arch.h b/libunwindstack/include/unwindstack/Arch.h
new file mode 100644
index 0000000..7060004
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Arch.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_ARCH_H
+#define _LIBUNWINDSTACK_ARCH_H
+
+#include <stddef.h>
+
+namespace unwindstack {
+
+enum ArchEnum : uint8_t {
+ ARCH_UNKNOWN = 0,
+ ARCH_ARM,
+ ARCH_ARM64,
+ ARCH_X86,
+ ARCH_X86_64,
+ ARCH_MIPS,
+ ARCH_MIPS64,
+};
+
+static inline bool ArchIs32Bit(ArchEnum arch) {
+ switch (arch) {
+ case ARCH_ARM:
+ case ARCH_X86:
+ case ARCH_MIPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ARCH_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 472ed92..97614b1 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -25,6 +25,7 @@
#include <unordered_map>
#include <utility>
+#include <unwindstack/Arch.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Memory.h>
@@ -38,16 +39,6 @@
struct MapInfo;
class Regs;
-enum ArchEnum : uint8_t {
- ARCH_UNKNOWN = 0,
- ARCH_ARM,
- ARCH_ARM64,
- ARCH_X86,
- ARCH_X86_64,
- ARCH_MIPS,
- ARCH_MIPS64,
-};
-
class Elf {
public:
Elf(Memory* memory) : memory_(memory) {}
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 66fefe7..0be4572 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -39,6 +39,27 @@
ERROR_INVALID_ELF, // Unwind in an invalid elf.
};
+static inline const char* GetErrorCodeString(ErrorCode error) {
+ switch (error) {
+ case ERROR_NONE:
+ return "None";
+ case ERROR_MEMORY_INVALID:
+ return "Memory Invalid";
+ case ERROR_UNWIND_INFO:
+ return "Unwind Info";
+ case ERROR_UNSUPPORTED:
+ return "Unsupported";
+ case ERROR_INVALID_MAP:
+ return "Invalid Map";
+ case ERROR_MAX_FRAMES_EXCEEDED:
+ return "Maximum Frames Exceeded";
+ case ERROR_REPEATED_FRAME:
+ return "Repeated Frame";
+ case ERROR_INVALID_ELF:
+ return "Invalid Elf";
+ }
+}
+
struct ErrorData {
ErrorCode code;
uint64_t address; // Only valid when code is ERROR_MEMORY_INVALID.
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 5f42565..1a2a704 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -24,11 +24,12 @@
#include <string>
#include <vector>
+#include <unwindstack/Arch.h>
+
namespace unwindstack {
// Forward declarations.
class Elf;
-enum ArchEnum : uint8_t;
class Memory;
class Regs {
@@ -52,7 +53,7 @@
virtual ArchEnum Arch() = 0;
- virtual bool Is32Bit() = 0;
+ bool Is32Bit() { return ArchIs32Bit(Arch()); }
virtual void* RawData() = 0;
virtual uint64_t pc() = 0;
@@ -96,8 +97,6 @@
: Regs(total_regs, return_loc), regs_(total_regs) {}
virtual ~RegsImpl() = default;
- bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
-
inline AddressType& operator[](size_t reg) { return regs_[reg]; }
void* RawData() override { return regs_.data(); }
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 3df8aad..b274c4c 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,6 +24,7 @@
#include <string>
#include <vector>
+#include <unwindstack/Arch.h>
#include <unwindstack/DexFiles.h>
#include <unwindstack/Error.h>
#include <unwindstack/JitDebug.h>
@@ -35,7 +36,6 @@
// Forward declarations.
class Elf;
-enum ArchEnum : uint8_t;
struct FrameData {
size_t num;
@@ -64,7 +64,11 @@
class Unwinder {
public:
Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
- : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+ : max_frames_(max_frames),
+ maps_(maps),
+ regs_(regs),
+ process_memory_(process_memory),
+ arch_(regs->Arch()) {
frames_.reserve(max_frames);
}
Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
@@ -74,8 +78,8 @@
virtual ~Unwinder() = default;
- void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
- const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
+ virtual void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+ const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
size_t NumFrames() const { return frames_.size(); }
@@ -90,9 +94,14 @@
std::string FormatFrame(size_t frame_num) const;
std::string FormatFrame(const FrameData& frame) const;
- void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
+ void SetArch(ArchEnum arch) { arch_ = arch; };
- void SetRegs(Regs* regs) { regs_ = regs; }
+ void SetJitDebug(JitDebug* jit_debug);
+
+ void SetRegs(Regs* regs) {
+ regs_ = regs;
+ arch_ = regs_ != nullptr ? regs->Arch() : ARCH_UNKNOWN;
+ }
Maps* GetMaps() { return maps_; }
std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
@@ -107,11 +116,12 @@
void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
- void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
+ void SetDexFiles(DexFiles* dex_files);
bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
ErrorCode LastErrorCode() { return last_error_.code; }
+ const char* LastErrorCodeString() { return GetErrorCodeString(last_error_.code); }
uint64_t LastErrorAddress() { return last_error_.address; }
uint64_t warnings() { return warnings_; }
@@ -126,6 +136,15 @@
protected:
Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
+ Unwinder(size_t max_frames, ArchEnum arch) : max_frames_(max_frames), arch_(arch) {
+ frames_.reserve(max_frames);
+ }
+
+ void ClearErrors() {
+ warnings_ = WARNING_NONE;
+ last_error_.code = ERROR_NONE;
+ last_error_.address = 0;
+ }
void FillInDexFrame();
FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
@@ -145,20 +164,27 @@
bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
uint64_t warnings_;
+ ArchEnum arch_ = ARCH_UNKNOWN;
};
class UnwinderFromPid : public Unwinder {
public:
UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
+ UnwinderFromPid(size_t max_frames, pid_t pid, ArchEnum arch)
+ : Unwinder(max_frames, arch), pid_(pid) {}
virtual ~UnwinderFromPid() = default;
- bool Init(ArchEnum arch);
+ bool Init();
+
+ void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+ const std::vector<std::string>* map_suffixes_to_ignore = nullptr) override;
private:
pid_t pid_;
std::unique_ptr<Maps> maps_ptr_;
std::unique_ptr<JitDebug> jit_debug_ptr_;
std::unique_ptr<DexFiles> dex_files_ptr_;
+ bool initted_ = false;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index c2bd836..0c6f9f8 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -314,7 +314,7 @@
JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
@@ -616,7 +616,7 @@
JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
@@ -939,7 +939,7 @@
std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
JitDebug jit_debug(leak_data->process_memory);
Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
- unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
ASSERT_EQ(76U, unwinder.NumFrames());
}
@@ -1062,7 +1062,7 @@
JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index f76a101..b11d213 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -170,7 +170,6 @@
unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
} else {
UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
- ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
unwinder_from_pid->SetRegs(regs.get());
unwinder.reset(unwinder_from_pid);
}
@@ -283,7 +282,6 @@
ASSERT_TRUE(regs.get() != nullptr);
UnwinderFromPid unwinder(512, pid);
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
unwinder.SetRegs(regs.get());
VerifyUnwind(&unwinder, kFunctionOrder);
@@ -335,7 +333,6 @@
ASSERT_TRUE(regs.get() != nullptr);
UnwinderFromPid unwinder(512, *pid);
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
unwinder.SetRegs(regs.get());
VerifyUnwind(&unwinder, kFunctionOrder);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 915f248..8bae242 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -1182,7 +1182,7 @@
DexFiles dex_files(process_memory_);
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
- unwinder.SetDexFiles(&dex_files, ARCH_ARM);
+ unwinder.SetDexFiles(&dex_files);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
@@ -1735,7 +1735,7 @@
regs.FakeSetArch(ARCH_ARM);
JitDebug jit_debug(process_memory_);
Unwinder unwinder(10, maps_.get(), ®s, process_memory_);
- unwinder.SetJitDebug(&jit_debug, ARCH_ARM);
+ unwinder.SetJitDebug(&jit_debug);
FrameData frame = unwinder.BuildFrameFromPcOnly(0x100310);
EXPECT_EQ(0x10030eU, frame.pc);
@@ -1751,4 +1751,21 @@
EXPECT_EQ(0xeU, frame.function_offset);
}
+TEST_F(UnwinderTest, unwinder_from_pid_init_error) {
+ UnwinderFromPid unwinder(10, getpid());
+ ASSERT_DEATH(unwinder.Init(), "");
+}
+
+TEST_F(UnwinderTest, set_jit_debug_error) {
+ Unwinder unwinder(10, maps_.get(), process_memory_);
+ JitDebug jit_debug(process_memory_);
+ ASSERT_DEATH(unwinder.SetJitDebug(&jit_debug), "");
+}
+
+TEST_F(UnwinderTest, set_dex_files_error) {
+ Unwinder unwinder(10, maps_.get(), process_memory_);
+ DexFiles dex_files(process_memory_);
+ ASSERT_DEATH(unwinder.SetDexFiles(&dex_files), "");
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index eb2b01d..3e67dc9 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -94,7 +94,6 @@
std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
UnwinderFromPid unwinder(512, getpid());
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
unwinder.SetRegs(regs.get());
RegsGetLocal(regs.get());
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
index 9c5374a..65052b6 100644
--- a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
@@ -116,8 +116,12 @@
static constexpr size_t kPageSize = 4096;
-static constexpr uint64_t AlignToPage(uint64_t address) {
- return (address + kPageSize - 1) & ~(kPageSize - 1);
+static inline bool AlignToPage(uint64_t address, uint64_t* aligned_address) {
+ if (__builtin_add_overflow(address, kPageSize - 1, aligned_address)) {
+ return false;
+ }
+ *aligned_address &= ~(kPageSize - 1);
+ return true;
}
std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) {
@@ -125,8 +129,16 @@
std::map<uint64_t, uint64_t> map_ends;
uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount);
for (uint8_t i = 0; i < entry_count; i++) {
- uint64_t start = AlignToPage(data_provider->ConsumeIntegral<uint64_t>());
- uint64_t end = AlignToPage(data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX));
+ uint64_t start;
+ if (!AlignToPage(data_provider->ConsumeIntegral<uint64_t>(), &start)) {
+ // Overflowed.
+ continue;
+ }
+ uint64_t end;
+ if (!AlignToPage(data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX), &end)) {
+ // Overflowed.
+ continue;
+ }
if (start == end) {
// It's impossible to see start == end in the real world, so
// make sure the map contains at least one page of data.
@@ -142,7 +154,11 @@
}
map_ends[end] = start;
- uint64_t offset = AlignToPage(data_provider->ConsumeIntegral<uint64_t>());
+ uint64_t offset;
+ if (!AlignToPage(data_provider->ConsumeIntegral<uint64_t>(), &offset)) {
+ // Overflowed.
+ continue;
+ }
std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen);
uint8_t flags = PROT_READ | PROT_WRITE;
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
index 2f4986a..1600547 100644
--- a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -85,7 +85,7 @@
// Create instance
Unwinder unwinder(max_frames, maps.get(), regs.get(), memory);
- unwinder.SetJitDebug(jit_debug_ptr.get(), arch);
+ unwinder.SetJitDebug(jit_debug_ptr.get());
unwinder.SetResolveNames(data_provider.ConsumeBool());
// Call unwind
PerformUnwind(&data_provider, &unwinder);
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 1812e50..ae45f06 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -90,11 +90,6 @@
printf("\n");
unwindstack::UnwinderFromPid unwinder(1024, pid);
- if (!unwinder.Init(regs->Arch())) {
- printf("Failed to init unwinder object.\n");
- return;
- }
-
unwinder.SetRegs(regs);
unwinder.Unwind();
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 64b58a8..c44a121 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -248,10 +248,6 @@
// Do an unwind so we know how much of the stack to save, and what
// elf files are involved.
unwindstack::UnwinderFromPid unwinder(1024, pid);
- if (!unwinder.Init(regs->Arch())) {
- printf("Unable to init unwinder object.\n");
- return 1;
- }
unwinder.SetRegs(regs);
uint64_t sp = regs->sp();
unwinder.Unwind();
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 926e3d7..dd9fea0 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -269,12 +269,6 @@
}
cc_fuzz {
- name: "libutils_fuzz_rwlock",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["RWLock_fuzz.cpp"],
-}
-
-cc_fuzz {
name: "libutils_fuzz_refbase",
defaults: ["libutils_fuzz_defaults"],
srcs: ["RefBase_fuzz.cpp"],
diff --git a/libutils/FuzzFormatTypes.h b/libutils/FuzzFormatTypes.h
new file mode 100644
index 0000000..aa9e503
--- /dev/null
+++ b/libutils/FuzzFormatTypes.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+#include <string>
+
+static const std::string kFormatChars = std::string("duoxXfFeEgGaAcsp");
+static constexpr int32_t kMaxFormatFlagValue = INT16_MAX;
+enum FormatChar : uint8_t {
+ SIGNED_DECIMAL = 0,
+ UNSIGNED_DECIMAL = 1,
+ UNSIGNED_OCTAL = 2,
+ UNSIGNED_HEX_LOWER = 3,
+ UNSIGNED_HEX_UPPER = 4,
+ // Uppercase/lowercase floating point impacts 'inf', 'infinity', and 'nan'
+ FLOAT_LOWER = 5,
+ FLOAT_UPPER = 6,
+ // Upper/lower impacts the "e" in exponents.
+ EXPONENT_LOWER = 7,
+ EXPONENT_UPPER = 8,
+ // %g will use %e or %f, whichever is shortest
+ SHORT_EXP_LOWER = 9,
+ // %G will use %E or %F, whichever is shortest
+ SHORT_EXP_UPPER = 10,
+ HEX_FLOAT_LOWER = 11,
+ HEX_FLOAT_UPPER = 12,
+ CHAR = 13,
+ STRING = 14,
+ POINTER = 15,
+ // Used by libfuzzer
+ kMaxValue = POINTER
+};
+
+bool canApplyFlag(FormatChar formatChar, char modifier) {
+ if (modifier == '#') {
+ return formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
+ formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
+ formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
+ formatChar == SHORT_EXP_UPPER;
+ } else if (modifier == '.') {
+ return formatChar == SIGNED_DECIMAL || formatChar == UNSIGNED_DECIMAL ||
+ formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
+ formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
+ formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
+ formatChar == SHORT_EXP_UPPER || formatChar == STRING;
+ }
+ return true;
+}
diff --git a/libutils/RWLock_fuzz.cpp b/libutils/RWLock_fuzz.cpp
deleted file mode 100755
index e075905..0000000
--- a/libutils/RWLock_fuzz.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 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 <functional>
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/RWLock.h"
-
-static constexpr int MAX_OPERATIONS = 100;
-static constexpr int MAX_NAME_LEN = 2048;
-
-static const std::vector<std::function<void(android::RWLock*)>> operations = {
- [](android::RWLock* lock) -> void {
- // This might return a non-zero value if already locked
- // Either way we are definitely locked now.
- lock->tryWriteLock();
- },
- [](android::RWLock* lock) -> void { lock->tryReadLock(); },
- [](android::RWLock* lock) -> void { lock->unlock(); },
-};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
- int type = dataProvider.ConsumeIntegral<int>();
- android::RWLock rwLock = android::RWLock(type, nameStr.c_str());
- std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
- int opsRun = 0;
- for (auto it : opsToRun) {
- if (opsRun++ >= MAX_OPERATIONS) {
- break;
- }
- it = it % operations.size();
- operations[it](&rwLock);
- }
- rwLock.unlock();
- return 0;
-}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index 2adfe98..b02683c 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -15,97 +15,199 @@
*/
#include <functional>
#include <iostream>
+#include <memory>
+#include "FuzzFormatTypes.h"
#include "fuzzer/FuzzedDataProvider.h"
#include "utils/String8.h"
static constexpr int MAX_STRING_BYTES = 256;
static constexpr uint8_t MAX_OPERATIONS = 50;
+// Interestingly, 2147483614 (INT32_MAX - 33) seems to be the max value that is handled for format
+// flags. Unfortunately we need to use a smaller value so we avoid consuming too much memory.
-std::vector<std::function<void(FuzzedDataProvider&, android::String8, android::String8)>>
+void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend);
+std::vector<std::function<void(FuzzedDataProvider*, android::String8*, android::String8*)>>
operations = {
-
// Bytes and size
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.bytes();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->bytes();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.isEmpty();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->isEmpty();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.length();
- },
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.size();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->length();
},
// Casing
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.toUpper();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->toUpper();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.toLower();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->toLower();
},
-
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.removeAll(str2.c_str());
+ [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ str1->removeAll(str2->c_str());
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.compare(str2);
+ [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ const android::String8& constRef(*str2);
+ str1->compare(constRef);
},
// Append and format
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.append(str2);
+ [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ str1->append(str2->c_str());
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.appendFormat(str1.c_str(), str2.c_str());
- },
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.format(str1.c_str(), str2.c_str());
- },
+ [](FuzzedDataProvider* dataProvider, android::String8* str1, android::String8*)
+ -> void { fuzzFormat(dataProvider, str1, dataProvider->ConsumeBool()); },
// Find operation
- [](FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8) -> void {
+ [](FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8* str2) -> void {
// We need to get a value from our fuzzer here.
- int start_index = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
- str1.find(str1.c_str(), start_index);
+ int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
+ str1->find(str2->c_str(), start_index);
},
// Path handling
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getBasePath();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getBasePath();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getPathExtension();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getPathExtension();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getPathLeaf();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getPathLeaf();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getPathDir();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getPathDir();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.convertToResPath();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->convertToResPath();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- android::String8 path_out_str = android::String8();
- str1.walkPath(&path_out_str);
- path_out_str.clear();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ std::shared_ptr<android::String8> path_out_str =
+ std::make_shared<android::String8>();
+ str1->walkPath(path_out_str.get());
+ path_out_str->clear();
},
- [](FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8) -> void {
- str1.setPathName(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ [](FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8*) -> void {
+ str1->setPathName(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
},
- [](FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8) -> void {
- str1.appendPath(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ [](FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8*) -> void {
+ str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
},
};
-void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8 str2) {
+void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
+ FormatChar formatType = dataProvider->ConsumeEnum<FormatChar>();
+
+ std::string formatString("%");
+ // Width specifier
+ if (dataProvider->ConsumeBool()) {
+ // Left pad with zeroes
+ if (dataProvider->ConsumeBool()) {
+ formatString.push_back('0');
+ }
+ // Right justify (or left justify if negative)
+ int32_t justify = dataProvider->ConsumeIntegralInRange<int32_t>(-kMaxFormatFlagValue,
+ kMaxFormatFlagValue);
+ formatString += std::to_string(justify);
+ }
+
+ // The # specifier only works with o, x, X, a, A, e, E, f, F, g, and G
+ if (canApplyFlag(formatType, '#') && dataProvider->ConsumeBool()) {
+ formatString.push_back('#');
+ }
+
+ // Precision specifier
+ if (canApplyFlag(formatType, '.') && dataProvider->ConsumeBool()) {
+ formatString.push_back('.');
+ formatString +=
+ std::to_string(dataProvider->ConsumeIntegralInRange<int>(0, kMaxFormatFlagValue));
+ }
+
+ formatString.push_back(kFormatChars.at(static_cast<uint8_t>(formatType)));
+
+ switch (formatType) {
+ case SIGNED_DECIMAL: {
+ int val = dataProvider->ConsumeIntegral<int>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), dataProvider->ConsumeIntegral<int>());
+ }
+ break;
+ }
+
+ case UNSIGNED_DECIMAL:
+ case UNSIGNED_OCTAL:
+ case UNSIGNED_HEX_LOWER:
+ case UNSIGNED_HEX_UPPER: {
+ // Unsigned integers for u, o, x, and X
+ uint val = dataProvider->ConsumeIntegral<uint>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+
+ case FLOAT_LOWER:
+ case FLOAT_UPPER:
+ case EXPONENT_LOWER:
+ case EXPONENT_UPPER:
+ case SHORT_EXP_LOWER:
+ case SHORT_EXP_UPPER:
+ case HEX_FLOAT_LOWER:
+ case HEX_FLOAT_UPPER: {
+ // Floating points for f, F, e, E, g, G, a, and A
+ float val = dataProvider->ConsumeFloatingPoint<float>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+
+ case CHAR: {
+ char val = dataProvider->ConsumeIntegral<char>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+
+ case STRING: {
+ std::string val = dataProvider->ConsumeRandomLengthString(MAX_STRING_BYTES);
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val.c_str());
+ } else {
+ str1->format(formatString.c_str(), val.c_str());
+ }
+ break;
+ }
+ case POINTER: {
+ uintptr_t val = dataProvider->ConsumeIntegral<uintptr_t>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+ }
+}
+
+void callFunc(uint8_t index, FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8* str2) {
operations[index](dataProvider, str1, str2);
}
@@ -120,14 +222,12 @@
// Create UTF-8 pointers
android::String8 str_one_utf8 = android::String8(vec.data());
android::String8 str_two_utf8 = android::String8(vec_two.data());
-
// Run operations against strings
int opsRun = 0;
while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
- callFunc(op, dataProvider, str_one_utf8, str_two_utf8);
+ operations[op](&dataProvider, &str_one_utf8, &str_two_utf8);
}
-
// Just to be extra sure these can be freed, we're going to explicitly clear
// them
str_one_utf8.clear();
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 55eadb0..540dcf4 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -302,8 +302,8 @@
}
#if defined(__ANDROID__)
-namespace {
-int androidSetThreadPriorityInternal(pid_t tid, int pri, bool change_policy) {
+int androidSetThreadPriority(pid_t tid, int pri)
+{
int rc = 0;
int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
@@ -312,19 +312,17 @@
return rc;
}
- if (change_policy) {
- if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
- } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
- SchedPolicy policy = SP_FOREGROUND;
- // Change to the sched policy group of the process.
- get_sched_policy(getpid(), &policy);
- rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
- }
+ if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
+ } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
+ SchedPolicy policy = SP_FOREGROUND;
+ // Change to the sched policy group of the process.
+ get_sched_policy(getpid(), &policy);
+ rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
+ }
- if (rc) {
- lasterr = errno;
- }
+ if (rc) {
+ lasterr = errno;
}
if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
@@ -335,15 +333,6 @@
return rc;
}
-} // namespace
-
-int androidSetThreadPriority(pid_t tid, int pri) {
- return androidSetThreadPriorityInternal(tid, pri, true);
-}
-
-int androidSetThreadPriorityAndPolicy(pid_t tid, int pri, bool change_policy) {
- return androidSetThreadPriorityInternal(tid, pri, change_policy);
-}
int androidGetThreadPriority(pid_t tid) {
return getpriority(PRIO_PROCESS, tid);
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index cdb5442..a8d7851 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -78,13 +78,8 @@
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
// if the priority set failed, else another value if just the group set failed;
// in either case errno is set. Thread ID zero means current thread.
-// This is equivalent to androidSetThreadPriorityAndPolicy(tid, prio, true);
extern int androidSetThreadPriority(pid_t tid, int prio);
-// Parameter "change_policy" indicates if sched policy should be changed. It needs
-// not be checked again if the change is done elsewhere like activity manager.
-extern int androidSetThreadPriorityAndPolicy(pid_t tid, int prio, bool change_policy);
-
// Get the current priority of a particular thread. Returns one of the
// ANDROID_PRIORITY constants or a negative result in case of error.
extern int androidGetThreadPriority(pid_t tid);
diff --git a/logd/Android.bp b/logd/Android.bp
index fe22d1c..335a174 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",
@@ -44,6 +45,9 @@
lto: {
thin: true,
},
+ sanitize: {
+ cfi: true,
+ },
cpp_std: "experimental",
}
@@ -146,7 +150,9 @@
"SerializedLogChunkTest.cpp",
"SerializedFlushToStateTest.cpp",
],
-
+ sanitize: {
+ cfi: true,
+ },
static_libs: [
"libbase",
"libcutils",
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..fa90878 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -113,8 +113,8 @@
if (total_size > max_size_[log_id]) {
Prune(log_id, total_size - max_size_[log_id], 0);
after_size = GetSizeUsed(log_id);
- LOG(INFO) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
- << " after size: " << after_size;
+ LOG(VERBOSE) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
+ << " after size: " << after_size;
}
stats_->set_overhead(log_id, after_size);
@@ -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.cpp b/logd/SerializedLogChunk.cpp
index e4d8945..1ffe7a8 100644
--- a/logd/SerializedLogChunk.cpp
+++ b/logd/SerializedLogChunk.cpp
@@ -27,8 +27,9 @@
void SerializedLogChunk::Compress() {
CHECK_EQ(compressed_log_.size(), 0U);
CompressionEngine::GetInstance().Compress(contents_, write_offset_, compressed_log_);
- LOG(INFO) << "Compressed Log, buffer max size: " << contents_.size()
- << " size used: " << write_offset_ << " compressed size: " << compressed_log_.size();
+ LOG(VERBOSE) << "Compressed Log, buffer max size: " << contents_.size()
+ << " size used: " << write_offset_
+ << " compressed size: " << compressed_log_.size();
}
// TODO: Develop a better reference counting strategy to guard against the case where the writer is
@@ -111,4 +112,4 @@
write_offset_ += entry->total_len();
highest_sequence_number_ = sequence;
return entry;
-}
\ No newline at end of file
+}
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/init.rc b/rootdir/init.rc
index 91f2c57..108f003 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -561,6 +561,12 @@
# Make sure that apexd is started in the default namespace
enter_default_mount_ns
+ # Start tombstoned early to be able to store tombstones.
+ mkdir /data/tombstones 0771 system system encryption=Require
+ mkdir /data/vendor/tombstones 0771 root root
+ mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
+ start tombstoned
+
# /data/apex is now available. Start apexd to scan and activate APEXes.
mkdir /data/apex 0755 root system encryption=None
mkdir /data/apex/active 0755 root system
@@ -661,9 +667,6 @@
mkdir /data/app-lib 0771 system system encryption=Require
mkdir /data/app 0771 system system encryption=Require
mkdir /data/property 0700 root root encryption=Require
- mkdir /data/tombstones 0771 system system encryption=Require
- mkdir /data/vendor/tombstones 0771 root root
- mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
# Create directories to push tests to for each linker namespace.
# Create the subdirectories in case the first test is run as root
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/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
index 5de1efa..2025621 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.c
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -283,6 +283,7 @@
{
.func = rpmb_dev_data_read,
.resp = RPMB_RESP_DATA_READ,
+ .check_key_programmed = true,
.check_addr = true,
.multi_packet_res = true,
.res_mac = true,