Merge "Fix OkOrFail<status_t> conversion ambiguities"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index edcea44..a541f6e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -391,6 +391,9 @@
apex_available: [
"com.android.runtime",
],
+
+ // Required for tests.
+ required: ["crash_dump.policy"],
}
cc_binary {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 0737612..85adbea 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1409,6 +1409,16 @@
return true;
}
+extern "C" void foo() {
+ LOG(INFO) << "foo";
+ std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+ LOG(INFO) << "bar";
+ std::this_thread::sleep_for(1s);
+}
+
TEST_F(CrasherTest, seccomp_tombstone) {
int intercept_result;
unique_fd output_fd;
@@ -1416,6 +1426,11 @@
static const auto dump_type = kDebuggerdTombstone;
StartProcess(
[]() {
+ std::thread a(foo);
+ std::thread b(bar);
+
+ std::this_thread::sleep_for(100ms);
+
raise_debugger_signal(dump_type);
_exit(0);
},
@@ -1430,16 +1445,8 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
-}
-
-extern "C" void foo() {
- LOG(INFO) << "foo";
- std::this_thread::sleep_for(1s);
-}
-
-extern "C" void bar() {
- LOG(INFO) << "bar";
- std::this_thread::sleep_for(1s);
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
}
TEST_F(CrasherTest, seccomp_backtrace) {
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index baf994f..4c1f9eb 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -98,32 +98,6 @@
__linker_disable_fallback_allocator();
}
-static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
- pid_t current_tid = gettid();
- char buf[BUFSIZ];
- snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
- DIR* dir = opendir(buf);
-
- if (!dir) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
- return;
- }
-
- struct dirent* ent;
- while ((ent = readdir(dir))) {
- char* end;
- long tid = strtol(ent->d_name, &end, 10);
- if (end == ent->d_name || *end != '\0') {
- continue;
- }
-
- if (tid != current_tid) {
- callback(tid, output_fd);
- }
- }
- closedir(dir);
-}
-
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
// Make sure the thread actually got the signal.
struct pollfd pfd = {
@@ -216,21 +190,21 @@
}
// Only allow one thread to perform a trace at a time.
- static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
- int ret = pthread_mutex_trylock(&trace_mutex);
- if (ret != 0) {
- async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
- strerror(ret));
+ static std::mutex trace_mutex;
+ if (!trace_mutex.try_lock()) {
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed");
return;
}
+ std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);
+
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
kDebuggerdNativeBacktrace)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"missing crash_dump_fallback() in selinux policy?");
- goto exit;
+ return;
}
dump_backtrace_header(output_fd.get());
@@ -239,15 +213,15 @@
debuggerd_fallback_trace(output_fd.get(), ucontext);
// Send a signal to all of our siblings, asking them to dump their stack.
- iterate_siblings(
- [](pid_t tid, int output_fd) {
+ pid_t current_tid = gettid();
+ if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
// Use a pipe, to be able to detect situations where the thread gracefully exits before
// receiving our signal.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
strerror(errno));
- return false;
+ return;
}
uint64_t expected = pack_thread_fd(-1, -1);
@@ -257,7 +231,7 @@
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"thread %d is already outputting to fd %d?", tid, fd);
close(sent_fd);
- return false;
+ return;
}
siginfo_t siginfo = {};
@@ -269,10 +243,10 @@
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
tid, strerror(errno));
- return false;
+ return;
}
- bool success = forward_output(pipe_read.get(), output_fd, tid);
+ bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
if (!success) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"timeout expired while waiting for thread %d to dump", tid);
@@ -288,15 +262,14 @@
}
}
- return true;
- },
- output_fd.get());
+ return;
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
+ current_tid, strerror(errno));
+ }
dump_backtrace_footer(output_fd.get());
tombstoned_notify_completion(tombstone_socket.get());
-
-exit:
- pthread_mutex_unlock(&trace_mutex);
}
static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index f21a203..14caaf6 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -23,6 +23,7 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>
@@ -73,22 +74,40 @@
std::map<pid_t, ThreadInfo> threads;
threads[tid] = ThreadInfo{
- .registers = std::move(regs),
- .uid = uid,
- .tid = tid,
- .thread_name = std::move(thread_name),
- .pid = pid,
- .command_line = std::move(command_line),
- .selinux_label = std::move(selinux_label),
- .siginfo = siginfo,
+ .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
+ .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
+ .siginfo = siginfo,
+#if defined(__aarch64__)
+ // Only supported on aarch64 for now.
+ .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
+ .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
+#endif
};
+ if (pid == tid) {
+ const ThreadInfo& thread = threads[pid];
+ if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
+ threads[tid] = ThreadInfo{
+ .uid = thread.uid,
+ .tid = tid,
+ .pid = thread.pid,
+ .command_line = thread.command_line,
+ .thread_name = get_thread_name(tid),
+ .tagged_addr_ctrl = thread.tagged_addr_ctrl,
+ .pac_enabled_keys = thread.pac_enabled_keys,
+ };
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
+ strerror(errno));
+ }
+ }
unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
auto process_memory =
unwindstack::Memory::CreateProcessMemoryCached(getpid());
unwinder.SetProcessMemory(process_memory);
if (!unwinder.Init()) {
- async_safe_fatal("failed to init unwinder object");
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+ return;
}
ProcessInfo process_info;
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index b7d5bc4..3e31bb7 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -48,6 +48,7 @@
#include <android/log.h>
#include <bionic/macros.h>
+#include <bionic/reserved_signals.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@@ -346,6 +347,93 @@
f->set_build_id(frame.map_info->GetPrintableBuildID());
}
+static void dump_registers(unwindstack::Unwinder* unwinder,
+ const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
+ bool memory_dump) {
+ if (regs == nullptr) {
+ return;
+ }
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+
+ regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
+ Register r;
+ r.set_name(name);
+ r.set_u64(value);
+ *thread.add_registers() = r;
+
+ if (memory_dump) {
+ MemoryDump dump;
+
+ dump.set_register_name(name);
+ std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
+ if (map_info) {
+ dump.set_mapping_name(map_info->name());
+ }
+
+ constexpr size_t kNumBytesAroundRegister = 256;
+ constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
+ char buf[kNumBytesAroundRegister];
+ uint8_t tags[kNumTagsAroundRegister];
+ ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
+ if (bytes == -1) {
+ return;
+ }
+ dump.set_begin_address(value);
+ dump.set_memory(buf, bytes);
+
+ bool has_tags = false;
+#if defined(__aarch64__)
+ for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
+ if (tags[i] != 0) {
+ has_tags = true;
+ }
+ }
+#endif // defined(__aarch64__)
+
+ if (has_tags) {
+ dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
+ }
+
+ *thread.add_memory_dump() = std::move(dump);
+ }
+ });
+}
+
+static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
+ if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
+ return;
+ }
+
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
+ unwinder->LastErrorCodeString());
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
+ unwinder->LastErrorAddress());
+}
+
+static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
+ if (unwinder->NumFrames() == 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
+ log_unwinder_error(unwinder);
+ return;
+ }
+
+ if (unwinder->elf_from_memory_not_file()) {
+ auto backtrace_note = thread.mutable_backtrace_note();
+ *backtrace_note->Add() =
+ "Function names and BuildId information is missing for some frames due";
+ *backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
+ *backtrace_note->Add() = "found under the lib/ directory are readable.";
+ *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
+ }
+ unwinder->SetDisplayBuildID(true);
+ for (const auto& frame : unwinder->frames()) {
+ BacktraceFrame* f = thread.add_current_backtrace();
+ fill_in_backtrace_frame(f, frame);
+ }
+}
+
static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
const ThreadInfo& thread_info, bool memory_dump = false) {
Thread thread;
@@ -355,97 +443,32 @@
thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
- unwindstack::Maps* maps = unwinder->GetMaps();
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
- thread_info.registers->IterateRegisters(
- [&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
- Register r;
- r.set_name(name);
- r.set_u64(value);
- *thread.add_registers() = r;
-
- if (memory_dump) {
- MemoryDump dump;
-
- dump.set_register_name(name);
- std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
- if (map_info) {
- dump.set_mapping_name(map_info->name());
- }
-
- constexpr size_t kNumBytesAroundRegister = 256;
- constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
- char buf[kNumBytesAroundRegister];
- uint8_t tags[kNumTagsAroundRegister];
- size_t start_offset = 0;
- ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
- if (bytes == -1) {
- return;
- }
- dump.set_begin_address(value);
-
- if (start_offset + bytes > sizeof(buf)) {
- async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
- start_offset, bytes);
- }
-
- dump.set_memory(buf, bytes);
-
- bool has_tags = false;
-#if defined(__aarch64__)
- for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
- if (tags[i] != 0) {
- has_tags = true;
- }
- }
-#endif // defined(__aarch64__)
-
- if (has_tags) {
- dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
- }
-
- *thread.add_memory_dump() = std::move(dump);
- }
- });
-
- std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
- unwinder->SetRegs(regs_copy.get());
- unwinder->Unwind();
- if (unwinder->NumFrames() == 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
- unwinder->LastErrorCodeString());
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
- unwinder->LastErrorAddress());
+ if (thread_info.pid == getpid() && thread_info.pid != thread_info.tid) {
+ // Fallback path for non-main thread, doing unwind from running process.
+ unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
+ if (!thread_unwinder.Init()) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Unable to initialize ThreadUnwinder object.");
+ log_unwinder_error(&thread_unwinder);
+ return;
}
+
+ std::unique_ptr<unwindstack::Regs> initial_regs;
+ thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
+ dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
+ dump_thread_backtrace(&thread_unwinder, thread);
} else {
- if (unwinder->elf_from_memory_not_file()) {
- auto backtrace_note = thread.mutable_backtrace_note();
- *backtrace_note->Add() =
- "Function names and BuildId information is missing for some frames due";
- *backtrace_note->Add() =
- "to unreadable libraries. For unwinds of apps, only shared libraries";
- *backtrace_note->Add() = "found under the lib/ directory are readable.";
- *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
- }
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
- BacktraceFrame* f = thread.add_current_backtrace();
- fill_in_backtrace_frame(f, frame);
- }
+ dump_registers(unwinder, thread_info.registers, thread, memory_dump);
+ std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+ unwinder->SetRegs(regs_copy.get());
+ unwinder->Unwind();
+ dump_thread_backtrace(unwinder, thread);
}
auto& threads = *tombstone->mutable_threads();
threads[thread_info.tid] = thread;
}
-static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ThreadInfo& thread_info) {
- dump_thread(tombstone, unwinder, thread_info, true);
-}
-
static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
unwindstack::Maps* maps = unwinder->GetMaps();
std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
@@ -663,7 +686,8 @@
dump_abort_message(&result, unwinder, process_info);
- dump_main_thread(&result, unwinder, main_thread);
+ // Dump the main thread, but save the memory around the registers.
+ dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
for (const auto& [tid, thread_info] : threads) {
if (tid != target_thread) {
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 858a338..4e8fdf9 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -25,7 +25,7 @@
rt_sigprocmask: 1
rt_sigaction: 1
rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
madvise: 1
mprotect: arg2 in 0x1|0x2
munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 152697c..4eb996e 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -37,7 +37,7 @@
#define PR_SET_VMA 0x53564d41
#if defined(__aarch64__)
// PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
#else
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
#endif
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index ce0fd30..5c6abc9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,6 +18,7 @@
#include <time.h>
+#include <functional>
#include <string>
#include <utility>
@@ -74,3 +75,24 @@
n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
return buf;
}
+
+bool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(buf), closedir);
+ if (dir == nullptr) {
+ return false;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ pid_t tid = atoi(entry->d_name);
+ if (tid == 0) {
+ continue;
+ }
+ if (pid != tid) {
+ callback(tid);
+ }
+ }
+ return true;
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index ec2862a..4375870 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <string>
#include <vector>
@@ -27,3 +28,4 @@
std::string get_thread_name(pid_t tid);
std::string get_timestamp();
+bool iterate_tids(pid_t, std::function<void(pid_t)>);
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 44dc81f..22f8363 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -186,6 +186,11 @@
return result;
}
+static void RemoveScratchPartition() {
+ AutoMountMetadata mount_metadata;
+ android::fs_mgr::TeardownAllOverlayForMountPoint();
+}
+
bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
std::vector<char> data = std::move(device->download_data());
if (data.empty()) {
@@ -218,7 +223,7 @@
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully flashed partition table");
}
@@ -262,7 +267,7 @@
if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0ca1946..143e980 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -657,7 +657,12 @@
if (partition_ext4 == fstab->end()) {
auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
new_entry.fs_type = "ext4";
- fstab->emplace_back(new_entry);
+ auto it = std::find_if(fstab->rbegin(), fstab->rend(),
+ [&mount_point](const auto& entry) {
+ return entry.mount_point == mount_point;
+ });
+ auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
+ fstab->insert(end_of_mount_point_group, new_entry);
}
}
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2da5b0f..996fa5e 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -33,6 +33,7 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -1121,6 +1122,23 @@
return true;
}
+static inline uint64_t GetIdealDataScratchSize() {
+ BlockDeviceInfo super_info;
+ PartitionOpener opener;
+ if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
+ LERROR << "could not get block device info for super";
+ return 0;
+ }
+
+ struct statvfs s;
+ if (statvfs("/data", &s) < 0) {
+ PERROR << "could not statfs /data";
+ return 0;
+ }
+
+ return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+}
+
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
*partition_exists = false;
if (change) *change = false;
@@ -1136,13 +1154,6 @@
return true;
}
- BlockDeviceInfo info;
- PartitionOpener opener;
- if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
- LERROR << "could not get block device info for super";
- return false;
- }
-
if (change) *change = true;
// Note: calling RemoveDisabledImages here ensures that we do not race with
@@ -1152,10 +1163,11 @@
return false;
}
if (!images->BackingImageExists(partition_name)) {
- static constexpr uint64_t kMinimumSize = 64_MiB;
- static constexpr uint64_t kMaximumSize = 2_GiB;
+ uint64_t size = GetIdealDataScratchSize();
+ if (!size) {
+ size = 2_GiB;
+ }
- uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
if (!images->CreateBackingImage(partition_name, size, flags)) {
@@ -1396,18 +1408,35 @@
return ret;
}
+struct MapInfo {
+ // If set, partition is owned by ImageManager.
+ std::unique_ptr<IImageManager> images;
+ // If set, and images is null, this is a DAP partition.
+ std::string name;
+ // If set, and images and name are empty, this is a non-dynamic partition.
+ std::string device;
+
+ MapInfo() = default;
+ MapInfo(MapInfo&&) = default;
+ ~MapInfo() {
+ if (images) {
+ images->UnmapImageDevice(name);
+ } else if (!name.empty()) {
+ DestroyLogicalPartition(name);
+ }
+ }
+};
+
// Note: This function never returns the DSU scratch device in recovery or fastbootd,
// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static bool EnsureScratchMapped(std::string* device, bool* mapped) {
- *mapped = false;
- *device = GetBootScratchDevice();
- if (!device->empty()) {
- return true;
+static std::optional<MapInfo> EnsureScratchMapped() {
+ MapInfo info;
+ info.device = GetBootScratchDevice();
+ if (!info.device.empty()) {
+ return {std::move(info)};
}
-
if (!fs_mgr_in_recovery()) {
- errno = EINVAL;
- return false;
+ return {};
}
auto partition_name = android::base::Basename(kScratchMountPoint);
@@ -1417,11 +1446,15 @@
// would otherwise always be mapped.
auto images = IImageManager::Open("remount", 10s);
if (images && images->BackingImageExists(partition_name)) {
- if (!images->MapImageDevice(partition_name, 10s, device)) {
- return false;
+ if (images->IsImageDisabled(partition_name)) {
+ return {};
}
- *mapped = true;
- return true;
+ if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
+ return {};
+ }
+ info.name = partition_name;
+ info.images = std::move(images);
+ return {std::move(info)};
}
// Avoid uart spam by first checking for a scratch partition.
@@ -1429,12 +1462,12 @@
auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
auto metadata = ReadCurrentMetadata(super_device);
if (!metadata) {
- return false;
+ return {};
}
auto partition = FindPartition(*metadata.get(), partition_name);
if (!partition) {
- return false;
+ return {};
}
CreateLogicalPartitionParams params = {
@@ -1444,11 +1477,11 @@
.force_writable = true,
.timeout_ms = 10s,
};
- if (!CreateLogicalPartition(params, device)) {
- return false;
+ if (!CreateLogicalPartition(params, &info.device)) {
+ return {};
}
- *mapped = true;
- return true;
+ info.name = partition_name;
+ return {std::move(info)};
}
// This should only be reachable in recovery, where DSU scratch is not
@@ -1602,26 +1635,35 @@
fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
}
- // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
- bool mapped = false;
- std::string scratch_device;
- if (EnsureScratchMapped(&scratch_device, &mapped)) {
+ if (mount_point.empty()) {
+ // Throw away the entire partition.
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ auto images = IImageManager::Open("remount", 10s);
+ if (images && images->BackingImageExists(partition_name)) {
+ if (images->DisableImage(partition_name)) {
+ LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
+ } else {
+ LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
+ }
+ }
+ }
+
+ if (auto info = EnsureScratchMapped(); info.has_value()) {
+ // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+ if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
bool should_destroy_scratch = false;
fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
&should_destroy_scratch);
+ fs_mgr_overlayfs_umount_scratch();
if (should_destroy_scratch) {
fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
}
- fs_mgr_overlayfs_umount_scratch();
- }
- if (mapped) {
- DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
}
}
// Teardown DSU overlay if present.
+ std::string scratch_device;
if (MapDsuScratchDevice(&scratch_device)) {
fs_mgr_overlayfs_umount_scratch();
if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 31a57a8..003e6ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -66,6 +66,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages() override;
@@ -219,6 +220,17 @@
return !device->empty();
}
+bool ImageManagerBinder::IsImageDisabled(const std::string& name) {
+ bool retval;
+ auto status = manager_->isImageDisabled(name, &retval);
+ if (!status.isOk()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__
+ << " binder returned: " << status.exceptionMessage().string();
+ return false;
+ }
+ return retval;
+}
+
bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
return false;
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index e3f5716..c416f4d 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -79,7 +79,7 @@
partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
// Allow overriding whether ImageManager thinks it's in recovery, for testing.
-#ifdef __ANDROID_RECOVERY__
+#ifdef __ANDROID_RAMDISK__
device_info_.is_recovery = {true};
#else
if (!device_info_.is_recovery.has_value()) {
@@ -523,7 +523,7 @@
auto image_header = GetImageHeaderPath(name);
-#if !defined __ANDROID_RECOVERY__
+#ifndef __ANDROID_RAMDISK__
// If there is a device-mapper node wrapping the block device, then we're
// able to create another node around it; the dm layer does not carry the
// exclusion lock down the stack when a mount occurs.
@@ -854,6 +854,24 @@
return true;
}
+bool ImageManager::IsImageDisabled(const std::string& name) {
+ if (!MetadataExists(metadata_dir_)) {
+ return true;
+ }
+
+ auto metadata = OpenMetadata(metadata_dir_);
+ if (!metadata) {
+ return false;
+ }
+
+ auto partition = FindPartition(*metadata.get(), name);
+ if (!partition) {
+ return false;
+ }
+
+ return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);
+}
+
std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
const std::chrono::milliseconds& timeout_ms,
const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6d09751..7472949 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -119,6 +119,7 @@
ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
ASSERT_TRUE(manager_->BackingImageExists(base_name_));
ASSERT_TRUE(manager_->DisableImage(base_name_));
+ ASSERT_TRUE(manager_->IsImageDisabled(base_name_));
ASSERT_TRUE(manager_->RemoveDisabledImages());
ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
}
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index b23a7f7..00dd661 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -131,6 +131,9 @@
virtual bool RemoveAllImages() = 0;
virtual bool UnmapImageIfExists(const std::string& name);
+
+ // Returns whether DisableImage() was called.
+ virtual bool IsImageDisabled(const std::string& name) = 0;
};
class ImageManager final : public IImageManager {
@@ -162,6 +165,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages();
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 8b269cd..6db8f13 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -275,6 +275,24 @@
],
}
+cc_test {
+ name: "vts_ota_config_test",
+ srcs: [
+ "vts_ota_config_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ test_suites: [
+ "vts",
+ ],
+ test_options: {
+ min_shipping_api_level: 33,
+ },
+ auto_gen_config: true,
+ require_root: true,
+}
+
cc_binary {
name: "snapshotctl",
srcs: [
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 9b5fd2a..746feeb 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -550,6 +550,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::vector<CowOperation>::iterator op_iter_;
@@ -560,6 +563,15 @@
op_iter_ = ops_->begin();
}
+bool CowOpIter::RDone() {
+ return op_iter_ == ops_->begin();
+}
+
+void CowOpIter::Prev() {
+ CHECK(!RDone());
+ op_iter_--;
+}
+
bool CowOpIter::Done() {
return op_iter_ == ops_->end();
}
@@ -585,6 +597,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
@@ -603,6 +618,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
@@ -623,6 +641,15 @@
block_iter_ = merge_op_blocks->begin() + start;
}
+bool CowMergeOpIter::RDone() {
+ return block_iter_ == merge_op_blocks_->begin();
+}
+
+void CowMergeOpIter::Prev() {
+ CHECK(!RDone());
+ block_iter_--;
+}
+
bool CowMergeOpIter::Done() {
return block_iter_ == merge_op_blocks_->end();
}
@@ -649,6 +676,15 @@
block_riter_ = merge_op_blocks->rbegin();
}
+bool CowRevMergeOpIter::RDone() {
+ return block_riter_ == merge_op_blocks_->rbegin();
+}
+
+void CowRevMergeOpIter::Prev() {
+ CHECK(!RDone());
+ block_riter_--;
+}
+
bool CowRevMergeOpIter::Done() {
return block_riter_ == merge_op_blocks_->rend() - start_;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index d5b4335..8e6bbd9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -92,7 +92,7 @@
public:
virtual ~ICowOpIter() {}
- // True if there are more items to read, false otherwise.
+ // True if there are no more items to read forward, false otherwise.
virtual bool Done() = 0;
// Read the current operation.
@@ -100,6 +100,12 @@
// Advance to the next item.
virtual void Next() = 0;
+
+ // Advance to the previous item.
+ virtual void Prev() = 0;
+
+ // True if there are no more items to read backwards, false otherwise
+ virtual bool RDone() = 0;
};
class CowReader final : public ICowReader {
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index c1a5af7..a648384 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -199,6 +199,7 @@
bool UnmapImageIfExists(const std::string& name) override {
return impl_->UnmapImageIfExists(name);
}
+ bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); }
private:
std::unique_ptr<android::fiemap::IImageManager> impl_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index d76558b..04d228d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -46,8 +46,6 @@
#include "partition_cow_creator.h"
#include "utility.h"
-#include <android-base/properties.h>
-
// Mock classes are not used. Header included to ensure mocked class definition aligns with the
// class itself.
#include <libsnapshot/mock_device_info.h>
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 9dd52b4..692cb74 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -492,22 +492,17 @@
return;
}
- if (IsIouringSupported()) {
- std::async(std::launch::async, &SnapshotHandler::ReadBlocksAsync, this, dm_block_device,
- partition_name, dev_sz);
- } else {
- int num_threads = 2;
- size_t num_blocks = dev_sz >> BLOCK_SHIFT;
- size_t num_blocks_per_thread = num_blocks / num_threads;
- size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
- off_t offset = 0;
+ int num_threads = 2;
+ size_t num_blocks = dev_sz >> BLOCK_SHIFT;
+ size_t num_blocks_per_thread = num_blocks / num_threads;
+ size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
+ off_t offset = 0;
- for (int i = 0; i < num_threads; i++) {
- std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this,
- dm_block_device, partition_name, offset, read_sz_per_thread);
+ for (int i = 0; i < num_threads; i++) {
+ std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
+ partition_name, offset, read_sz_per_thread);
- offset += read_sz_per_thread;
- }
+ offset += read_sz_per_thread;
}
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index cc82985..83d40f6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -99,6 +99,7 @@
void InitializeRAIter();
bool RAIterDone();
void RAIterNext();
+ void RAResetIter(uint64_t num_blocks);
const CowOperation* GetRAOpIter();
void InitializeBuffer();
@@ -151,12 +152,16 @@
std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
BufferSink bufsink_;
+ uint64_t total_ra_blocks_completed_ = 0;
bool read_ahead_async_ = false;
- // Queue depth of 32 seems optimal. We don't want
+ // Queue depth of 8 seems optimal. We don't want
// to have a huge depth as it may put more memory pressure
// on the kernel worker threads given that we use
- // IOSQE_ASYNC flag.
- int queue_depth_ = 32;
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
std::unique_ptr<struct io_uring> ring_;
};
@@ -210,11 +215,12 @@
// Merge related ops
bool Merge();
- bool MergeOrderedOps(const std::unique_ptr<ICowOpIter>& cowop_iter);
- bool MergeOrderedOpsAsync(const std::unique_ptr<ICowOpIter>& cowop_iter);
- bool MergeReplaceZeroOps(const std::unique_ptr<ICowOpIter>& cowop_iter);
+ bool AsyncMerge();
+ bool SyncMerge();
+ bool MergeOrderedOps();
+ bool MergeOrderedOpsAsync();
+ bool MergeReplaceZeroOps();
int PrepareMerge(uint64_t* source_offset, int* pending_ops,
- const std::unique_ptr<ICowOpIter>& cowop_iter,
std::vector<const CowOperation*>* replace_zero_vec = nullptr);
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
@@ -238,12 +244,18 @@
unique_fd base_path_merge_fd_;
unique_fd ctrl_fd_;
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+ size_t ra_block_index_ = 0;
+ uint64_t blocks_merged_in_group_ = 0;
bool merge_async_ = false;
- // Queue depth of 32 seems optimal. We don't want
+ // Queue depth of 8 seems optimal. We don't want
// to have a huge depth as it may put more memory pressure
// on the kernel worker threads given that we use
- // IOSQE_ASYNC flag.
- int queue_depth_ = 32;
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
std::unique_ptr<struct io_uring> ring_;
std::shared_ptr<SnapshotHandler> snapuserd_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index d4d4efe..0cb41d3 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -24,15 +24,14 @@
using android::base::unique_fd;
int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
- const std::unique_ptr<ICowOpIter>& cowop_iter,
std::vector<const CowOperation*>* replace_zero_vec) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
bool checkOrderedOp = (replace_zero_vec == nullptr);
do {
- if (!cowop_iter->Done() && num_ops) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ if (!cowop_iter_->Done() && num_ops) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
break;
}
@@ -42,12 +41,12 @@
replace_zero_vec->push_back(cow_op);
}
- cowop_iter->Next();
+ cowop_iter_->Next();
num_ops -= 1;
nr_consecutive = 1;
- while (!cowop_iter->Done() && num_ops) {
- const CowOperation* op = &cowop_iter->Get();
+ while (!cowop_iter_->Done() && num_ops) {
+ const CowOperation* op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*op)) {
break;
}
@@ -63,7 +62,7 @@
nr_consecutive += 1;
num_ops -= 1;
- cowop_iter->Next();
+ cowop_iter_->Next();
}
}
} while (0);
@@ -71,7 +70,7 @@
return nr_consecutive;
}
-bool Worker::MergeReplaceZeroOps(const std::unique_ptr<ICowOpIter>& cowop_iter) {
+bool Worker::MergeReplaceZeroOps() {
// Flush every 8192 ops. Since all ops are independent and there is no
// dependency between COW ops, we will flush the data and the number
// of ops merged in COW file for every 8192 ops. If there is a crash,
@@ -84,15 +83,17 @@
int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
int num_ops_merged = 0;
- while (!cowop_iter->Done()) {
+ SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
+
+ while (!cowop_iter_->Done()) {
int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;
std::vector<const CowOperation*> replace_zero_vec;
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter, &replace_zero_vec);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);
if (linear_blocks == 0) {
// Merge complete
- CHECK(cowop_iter->Done());
+ CHECK(cowop_iter_->Done());
break;
}
@@ -117,8 +118,8 @@
size_t io_size = linear_blocks * BLOCK_SZ;
// Merge - Write the contents back to base device
- int ret = pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(), io_size,
- source_offset);
+ int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(),
+ io_size, source_offset));
if (ret < 0 || ret != io_size) {
SNAP_LOG(ERROR)
<< "Merge: ReplaceZeroOps: Failed to write to backing device while merging "
@@ -172,16 +173,15 @@
return true;
}
-bool Worker::MergeOrderedOpsAsync(const std::unique_ptr<ICowOpIter>& cowop_iter) {
+bool Worker::MergeOrderedOpsAsync() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
- size_t block_index = 0;
SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
- while (!cowop_iter->Done()) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ while (!cowop_iter_->Done()) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -190,11 +190,10 @@
// Wait for RA thread to notify that the merge window
// is ready for merging.
if (!snapuserd_->WaitForMergeBegin()) {
- snapuserd_->SetMergeFailed(block_index);
return false;
}
- snapuserd_->SetMergeInProgress(block_index);
+ snapuserd_->SetMergeInProgress(ra_block_index_);
loff_t offset = 0;
int num_ops = snapuserd_->GetTotalBlocksToMerge();
@@ -202,12 +201,13 @@
int pending_sqe = queue_depth_;
int pending_ios_to_submit = 0;
bool flush_required = false;
+ blocks_merged_in_group_ = 0;
SNAP_LOG(DEBUG) << "Merging copy-ops of size: " << num_ops;
while (num_ops) {
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops);
if (linear_blocks != 0) {
size_t io_size = (linear_blocks * BLOCK_SZ);
@@ -216,7 +216,6 @@
struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
if (!sqe) {
SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during merge-ordered ops";
- snapuserd_->SetMergeFailed(block_index);
return false;
}
@@ -225,10 +224,18 @@
offset += io_size;
num_ops -= linear_blocks;
+ blocks_merged_in_group_ += linear_blocks;
pending_sqe -= 1;
pending_ios_to_submit += 1;
- sqe->flags |= IOSQE_ASYNC;
+ // These flags are important - We need to make sure that the
+ // blocks are linked and are written in the same order as
+ // populated. This is because of overlapping block writes.
+ //
+ // If there are no dependency, we can optimize this further by
+ // allowing parallel writes; but for now, just link all the SQ
+ // entries.
+ sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
}
// Ring is full or no more COW ops to be merged in this batch
@@ -256,7 +263,7 @@
pending_sqe -= 1;
flush_required = false;
pending_ios_to_submit += 1;
- sqe->flags |= IOSQE_ASYNC;
+ sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
}
} else {
flush_required = true;
@@ -269,35 +276,45 @@
SNAP_PLOG(ERROR)
<< "io_uring_submit failed for read-ahead: "
<< " io submit: " << ret << " expected: " << pending_ios_to_submit;
- snapuserd_->SetMergeFailed(block_index);
return false;
}
int pending_ios_to_complete = pending_ios_to_submit;
pending_ios_to_submit = 0;
+ bool status = true;
+
// Reap I/O completions
while (pending_ios_to_complete) {
struct io_uring_cqe* cqe;
+ // We need to make sure to reap all the I/O's submitted
+ // even if there are any errors observed.
+ //
+ // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
+ // these error codes are not truly I/O errors; we can retry them
+ // by re-populating the SQE entries and submitting the I/O
+ // request back. However, we don't do that now; instead we
+ // will fallback to synchronous I/O.
ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
- SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
- snapuserd_->SetMergeFailed(block_index);
- return false;
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
+ status = false;
}
if (cqe->res < 0) {
- SNAP_LOG(ERROR)
- << "Read-ahead - io_uring_Wait_cqe failed with res: " << cqe->res;
- snapuserd_->SetMergeFailed(block_index);
- return false;
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed with res: " << cqe->res;
+ status = false;
}
io_uring_cqe_seen(ring_.get(), cqe);
pending_ios_to_complete -= 1;
}
+ if (!status) {
+ return false;
+ }
+
pending_sqe = queue_depth_;
}
@@ -312,7 +329,6 @@
// Flush the data
if (flush_required && (fsync(base_path_merge_fd_.get()) < 0)) {
SNAP_LOG(ERROR) << " Failed to fsync merged data";
- snapuserd_->SetMergeFailed(block_index);
return false;
}
@@ -320,35 +336,34 @@
// the merge completion
if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
- snapuserd_->SetMergeFailed(block_index);
return false;
}
SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
+
// Mark the block as merge complete
- snapuserd_->SetMergeCompleted(block_index);
+ snapuserd_->SetMergeCompleted(ra_block_index_);
// Notify RA thread that the merge thread is ready to merge the next
// window
snapuserd_->NotifyRAForMergeReady();
// Get the next block
- block_index += 1;
+ ra_block_index_ += 1;
}
return true;
}
-bool Worker::MergeOrderedOps(const std::unique_ptr<ICowOpIter>& cowop_iter) {
+bool Worker::MergeOrderedOps() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
- size_t block_index = 0;
SNAP_LOG(INFO) << "MergeOrderedOps started....";
- while (!cowop_iter->Done()) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ while (!cowop_iter_->Done()) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -357,11 +372,11 @@
// Wait for RA thread to notify that the merge window
// is ready for merging.
if (!snapuserd_->WaitForMergeBegin()) {
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
- snapuserd_->SetMergeInProgress(block_index);
+ snapuserd_->SetMergeInProgress(ra_block_index_);
loff_t offset = 0;
int num_ops = snapuserd_->GetTotalBlocksToMerge();
@@ -369,7 +384,7 @@
while (num_ops) {
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops);
if (linear_blocks == 0) {
break;
}
@@ -378,12 +393,13 @@
// Write to the base device. Data is already in the RA buffer. Note
// that XOR ops is already handled by the RA thread. We just write
// the contents out.
- int ret = pwrite(base_path_merge_fd_.get(), (char*)read_ahead_buffer + offset, io_size,
- source_offset);
+ int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(),
+ (char*)read_ahead_buffer + offset, io_size,
+ source_offset));
if (ret < 0 || ret != io_size) {
SNAP_LOG(ERROR) << "Failed to write to backing device while merging "
<< " at offset: " << source_offset << " io_size: " << io_size;
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
@@ -397,7 +413,7 @@
// Flush the data
if (fsync(base_path_merge_fd_.get()) < 0) {
SNAP_LOG(ERROR) << " Failed to fsync merged data";
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
@@ -405,47 +421,87 @@
// the merge completion
if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
// Mark the block as merge complete
- snapuserd_->SetMergeCompleted(block_index);
+ snapuserd_->SetMergeCompleted(ra_block_index_);
// Notify RA thread that the merge thread is ready to merge the next
// window
snapuserd_->NotifyRAForMergeReady();
// Get the next block
- block_index += 1;
+ ra_block_index_ += 1;
}
return true;
}
-bool Worker::Merge() {
- std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetMergeOpIter();
+bool Worker::AsyncMerge() {
+ if (!MergeOrderedOpsAsync()) {
+ SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
+ // Reset the iter so that we retry the merge
+ while (blocks_merged_in_group_ && !cowop_iter_->RDone()) {
+ cowop_iter_->Prev();
+ blocks_merged_in_group_ -= 1;
+ }
+ return false;
+ }
+
+ SNAP_LOG(INFO) << "MergeOrderedOpsAsync completed";
+ return true;
+}
+
+bool Worker::SyncMerge() {
+ if (!MergeOrderedOps()) {
+ SNAP_LOG(ERROR) << "Merge failed for ordered ops";
+ return false;
+ }
+
+ SNAP_LOG(INFO) << "MergeOrderedOps completed";
+ return true;
+}
+
+bool Worker::Merge() {
+ cowop_iter_ = reader_->GetMergeOpIter();
+
+ bool retry = false;
+ bool ordered_ops_merge_status;
+
+ // Start Async Merge
if (merge_async_) {
- if (!MergeOrderedOpsAsync(cowop_iter)) {
+ ordered_ops_merge_status = AsyncMerge();
+ if (!ordered_ops_merge_status) {
+ FinalizeIouring();
+ retry = true;
+ merge_async_ = false;
+ }
+ }
+
+ // Check if we need to fallback and retry the merge
+ //
+ // If the device doesn't support async merge, we
+ // will directly enter here (aka devices with 4.x kernels)
+ const bool sync_merge_required = (retry || !merge_async_);
+
+ if (sync_merge_required) {
+ ordered_ops_merge_status = SyncMerge();
+ if (!ordered_ops_merge_status) {
+ // Merge failed. Device will continue to be mounted
+ // off snapshots; merge will be retried during
+ // next reboot
SNAP_LOG(ERROR) << "Merge failed for ordered ops";
snapuserd_->MergeFailed();
return false;
}
- SNAP_LOG(INFO) << "MergeOrderedOpsAsync completed.....";
- } else {
- // Start with Copy and Xor ops
- if (!MergeOrderedOps(cowop_iter)) {
- SNAP_LOG(ERROR) << "Merge failed for ordered ops";
- snapuserd_->MergeFailed();
- return false;
- }
- SNAP_LOG(INFO) << "MergeOrderedOps completed.....";
}
// Replace and Zero ops
- if (!MergeReplaceZeroOps(cowop_iter)) {
+ if (!MergeReplaceZeroOps()) {
SNAP_LOG(ERROR) << "Merge failed for replace/zero ops";
snapuserd_->MergeFailed();
return false;
@@ -506,7 +562,7 @@
CloseFds();
reader_->CloseCowFd();
- SNAP_LOG(INFO) << "Merge finish";
+ SNAP_LOG(INFO) << "Snapshot-Merge completed";
return true;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 26c5f19..7d9d392 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -279,7 +279,6 @@
sqe = io_uring_get_sqe(ring_.get());
if (!sqe) {
SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during read-ahead";
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -309,7 +308,6 @@
if (ret != pending_ios_to_submit) {
SNAP_PLOG(ERROR) << "io_uring_submit failed for read-ahead: "
<< " io submit: " << ret << " expected: " << pending_ios_to_submit;
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -321,14 +319,12 @@
// Read XOR data from COW file in parallel when I/O's are in-flight
if (xor_processing_required && !ReadXorData(block_index, xor_op_index, xor_op_vec)) {
SNAP_LOG(ERROR) << "ReadXorData failed";
- snapuserd_->ReadAheadIOFailed();
return false;
}
// Fetch I/O completions
if (!ReapIoCompletions(pending_ios_to_complete)) {
SNAP_LOG(ERROR) << "ReapIoCompletions failed";
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -393,26 +389,36 @@
}
bool ReadAhead::ReapIoCompletions(int pending_ios_to_complete) {
+ bool status = true;
+
// Reap I/O completions
while (pending_ios_to_complete) {
struct io_uring_cqe* cqe;
+ // We need to make sure to reap all the I/O's submitted
+ // even if there are any errors observed.
+ //
+ // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
+ // these error codes are not truly I/O errors; we can retry them
+ // by re-populating the SQE entries and submitting the I/O
+ // request back. However, we don't do that now; instead we
+ // will fallback to synchronous I/O.
int ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
- return false;
+ status = false;
}
if (cqe->res < 0) {
SNAP_LOG(ERROR) << "Read-ahead - io_uring_Wait_cqe failed with res: " << cqe->res;
- return false;
+ status = false;
}
io_uring_cqe_seen(ring_.get(), cqe);
pending_ios_to_complete -= 1;
}
- return true;
+ return status;
}
void ReadAhead::ProcessXorData(size_t& block_xor_index, size_t& xor_index,
@@ -610,18 +616,38 @@
return ReconstructDataFromCow();
}
+ bool retry = false;
+ bool ra_status;
+
+ // Start Async read-ahead
if (read_ahead_async_) {
- if (!ReadAheadAsyncIO()) {
- SNAP_LOG(ERROR) << "ReadAheadAsyncIO failed - io_uring processing failure.";
- return false;
+ ra_status = ReadAheadAsyncIO();
+ if (!ra_status) {
+ SNAP_LOG(ERROR) << "ReadAheadAsyncIO failed - Falling back synchronous I/O";
+ FinalizeIouring();
+ RAResetIter(total_blocks_merged_);
+ retry = true;
+ read_ahead_async_ = false;
}
- } else {
- if (!ReadAheadSyncIO()) {
+ }
+
+ // Check if we need to fallback and retry the merge
+ //
+ // If the device doesn't support async operations, we
+ // will directly enter here (aka devices with 4.x kernels)
+
+ const bool ra_sync_required = (retry || !read_ahead_async_);
+
+ if (ra_sync_required) {
+ ra_status = ReadAheadSyncIO();
+ if (!ra_status) {
SNAP_LOG(ERROR) << "ReadAheadSyncIO failed";
return false;
}
}
+ SNAP_LOG(DEBUG) << "Read-ahead: total_ra_blocks_merged: " << total_ra_blocks_completed_;
+
// Wait for the merge to finish for the previous RA window. We shouldn't
// be touching the scratch space until merge is complete of previous RA
// window. If there is a crash during this time frame, merge should resume
@@ -646,6 +672,7 @@
offset += BLOCK_SZ;
}
+ total_ra_blocks_completed_ += total_blocks_merged_;
snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);
// Flush the data only if we have a overlapping blocks in the region
@@ -763,6 +790,13 @@
cowop_iter_->Next();
}
+void ReadAhead::RAResetIter(uint64_t num_blocks) {
+ while (num_blocks && !cowop_iter_->RDone()) {
+ cowop_iter_->Prev();
+ num_blocks -= 1;
+ }
+}
+
const CowOperation* ReadAhead::GetRAOpIter() {
const CowOperation* cow_op = &cowop_iter_->Get();
return cow_op;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 6dec1e2..d4e1d7c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -531,6 +531,13 @@
{
std::unique_lock<std::mutex> lock(blk_state->m_lock);
+ // We may have fallback from Async-merge to synchronous merging
+ // on the existing block. There is no need to reset as the
+ // merge is already in progress.
+ if (blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS) {
+ return;
+ }
+
CHECK(blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_PENDING);
// First set the state to RA_READY so that in-flight I/O will drain
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
new file mode 100644
index 0000000..afc2d81
--- /dev/null
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2022 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 <android-base/properties.h>
+#include <gtest/gtest.h>
+
+TEST(VAB, Enabled) {
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false));
+}
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 262beaa..7e3924a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -96,7 +96,7 @@
"device-tests",
],
test_options: {
- min_shipping_api_level: 33,
+ min_shipping_api_level: 31,
},
require_root: true,
auto_gen_config: true,
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index eccb902..1dbee75 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1104,3 +1104,76 @@
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
EXPECT_EQ(0, entry->readahead_size_kb);
}
+
+TEST(fs_mgr, TransformFstabForDsu) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+system /system ext4 ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index 77900df..415e67e 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -29,7 +29,7 @@
TEST(fs, ErofsSupported) {
// S and higher for this test.
- if (GetVsrLevel() < 31) {
+ if (GetVsrLevel() < __ANDROID_API_S__) {
GTEST_SKIP();
}
@@ -85,10 +85,10 @@
continue;
}
- if (vsr_level <= 32) {
+ if (vsr_level < __ANDROID_API_T__) {
continue;
}
- if (vsr_level == 33 && parent_bdev != super_bdev) {
+ if (vsr_level == __ANDROID_API_T__ && parent_bdev != super_bdev) {
// Only check for dynamic partitions at this VSR level.
continue;
}
@@ -100,3 +100,12 @@
}
}
}
+
+TEST(fs, NoDtFstab) {
+ if (GetVsrLevel() <= __ANDROID_API_S__) {
+ GTEST_SKIP();
+ }
+
+ android::fs_mgr::Fstab fstab;
+ EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
+}
diff --git a/init/service.cpp b/init/service.cpp
index f7318cb..f6dd9b9 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -397,6 +397,14 @@
return {};
}
+static void ClosePipe(const std::array<int, 2>* pipe) {
+ for (const auto fd : *pipe) {
+ if (fd >= 0) {
+ close(fd);
+ }
+ }
+}
+
Result<void> Service::Start() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
@@ -428,6 +436,12 @@
return {};
}
+ std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
+ ClosePipe);
+ if (pipe(pipefd->data()) < 0) {
+ return ErrnoError() << "pipe()";
+ }
+
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console) {
if (proc_attr_.console.empty()) {
@@ -532,6 +546,13 @@
LOG(ERROR) << "failed to write pid to files: " << result.error();
}
+ // Wait until the cgroups have been created and until the cgroup controllers have been
+ // activated.
+ if (std::byte byte; read((*pipefd)[0], &byte, 1) < 0) {
+ PLOG(ERROR) << "failed to read from notification channel";
+ }
+ pipefd.reset();
+
if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
LOG(ERROR) << "failed to set task profiles";
}
@@ -618,6 +639,10 @@
LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
}
+ if (write((*pipefd)[1], "", 1) < 0) {
+ return ErrnoError() << "sending notification failed";
+ }
+
NotifyStateChange("running");
reboot_on_failure.Disable();
return {};
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index 3f5e41f..a5ffb03 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -34,24 +34,6 @@
extern int qtaguid_untagSocket(int sockfd);
/*
- * For the given uid, switch counter sets.
- * The kernel only keeps a limited number of sets.
- * 2 for now.
- */
-extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid);
-
-/*
- * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freed.
- * The delete data also affects active tagged sockets, which are
- * then untagged.
- * The calling process can only operate on its own tags.
- * Unless it is part of the happy AID_NET_BW_ACCT group.
- * In which case it can clobber everything.
- */
-extern int qtaguid_deleteTagData(int tag, uid_t uid);
-
-/*
* Enable/disable qtaguid functionnality at a lower level.
* When pacified, the kernel will accept commands but do nothing.
*/
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 97e93f8..17a0070 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_LAST ATRACE_TAG_RRO
+#define ATRACE_TAG_THERMAL (1 << 27)
+#define ATRACE_TAG_LAST ATRACE_TAG_THERMAL
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index 2fe877c..a987b85 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -34,8 +34,6 @@
public:
int (*netdTagSocket)(int, uint32_t, uid_t);
int (*netdUntagSocket)(int);
- int (*netdSetCounterSet)(uint32_t, uid_t);
- int (*netdDeleteTagData)(uint32_t, uid_t);
};
int stubTagSocket(int, uint32_t, uid_t) {
@@ -46,16 +44,8 @@
return -EREMOTEIO;
}
-int stubSetCounterSet(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
-int stubDeleteTagData(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
netdHandler initHandler(void) {
- netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
+ netdHandler handler = {stubTagSocket, stubUntagSocket};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
@@ -73,15 +63,6 @@
ALOGE("load netdUntagSocket handler failed: %s", dlerror());
}
- handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
- if (!handler.netdSetCounterSet) {
- ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
- }
-
- handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
- if (!handler.netdDeleteTagData) {
- ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
- }
return handler;
}
@@ -114,13 +95,3 @@
ALOGV("Untagging socket %d", sockfd);
return getHandler().netdUntagSocket(sockfd);
}
-
-int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
- ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
- return getHandler().netdSetCounterSet(counterSetNum, uid);
-}
-
-int qtaguid_deleteTagData(int tag, uid_t uid) {
- ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
- return getHandler().netdDeleteTagData(tag, uid);
-}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 352847a..8c00326 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -231,7 +231,8 @@
const ACgroupController* controller = ACgroupFile_getController(i);
if (ACgroupController_getFlags(controller) &
CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
- std::string str = std::string("+") + ACgroupController_getName(controller);
+ std::string str("+");
+ str.append(ACgroupController_getName(controller));
if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
return -errno;
}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index cb2fe0a..76d5e13 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -85,7 +85,7 @@
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -100,7 +100,7 @@
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -211,7 +211,7 @@
for (std::string cgroup_root_path : cgroups) {
std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
if (root == NULL) {
- PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+ PLOG(ERROR) << __func__ << " failed to open " << cgroup_root_path;
} else {
dirent* dir;
while ((dir = readdir(root.get())) != nullptr) {
@@ -297,7 +297,8 @@
// This happens when process is already dead
return 0;
}
- PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+ PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
+ << initialPid;
return -1;
}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 74ba7f6..73aa2af 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -112,6 +112,8 @@
return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
}
+IProfileAttribute::~IProfileAttribute() = default;
+
void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
controller_ = controller;
file_name_ = file_name;
@@ -183,6 +185,12 @@
return true;
}
+#else
+
+bool SetTimerSlackAction::ExecuteForTask(int) const {
+ return true;
+};
+
#endif
bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
@@ -469,6 +477,7 @@
bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& element : elements_) {
if (!element->ExecuteForProcess(uid, pid)) {
+ LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
return false;
}
}
@@ -481,6 +490,7 @@
}
for (const auto& element : elements_) {
if (!element->ExecuteForTask(tid)) {
+ LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
return false;
}
}
@@ -592,7 +602,7 @@
std::string profile_name = profile_val["Name"].asString();
const Json::Value& actions = profile_val["Actions"];
- auto profile = std::make_shared<TaskProfile>();
+ auto profile = std::make_shared<TaskProfile>(profile_name);
for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
const Json::Value& action_val = actions[act_idx];
@@ -702,7 +712,7 @@
}
}
if (ret) {
- auto profile = std::make_shared<TaskProfile>();
+ auto profile = std::make_shared<TaskProfile>(aggregateprofile_name);
profile->Add(std::make_unique<ApplyProfileAction>(profiles));
profiles_[aggregateprofile_name] = profile;
}
@@ -720,7 +730,7 @@
return nullptr;
}
-const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
auto iter = attributes_.find(name);
if (iter != attributes_.end()) {
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 1aaa196..e6e65fb 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -26,16 +26,26 @@
#include <android-base/unique_fd.h>
#include <cgroup_map.h>
-class ProfileAttribute {
+class IProfileAttribute {
+ public:
+ virtual ~IProfileAttribute() = 0;
+ virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+ virtual const CgroupController* controller() const = 0;
+ virtual const std::string& file_name() const = 0;
+ virtual bool GetPathForTask(int tid, std::string* path) const = 0;
+};
+
+class ProfileAttribute : public IProfileAttribute {
public:
ProfileAttribute(const CgroupController& controller, const std::string& file_name)
: controller_(controller), file_name_(file_name) {}
+ ~ProfileAttribute() = default;
- const CgroupController* controller() const { return &controller_; }
- const std::string& file_name() const { return file_name_; }
- void Reset(const CgroupController& controller, const std::string& file_name);
+ const CgroupController* controller() const override { return &controller_; }
+ const std::string& file_name() const override { return file_name_; }
+ void Reset(const CgroupController& controller, const std::string& file_name) override;
- bool GetPathForTask(int tid, std::string* path) const;
+ bool GetPathForTask(int tid, std::string* path) const override;
private:
CgroupController controller_;
@@ -49,6 +59,8 @@
virtual ~ProfileAction() {}
+ virtual const char* Name() const = 0;
+
// Default implementations will fail
virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
virtual bool ExecuteForTask(int) const { return false; };
@@ -65,22 +77,21 @@
public:
SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetClamps"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
protected:
int boost_;
int clamp_;
};
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
class SetTimerSlackAction : public ProfileAction {
public:
SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetTimerSlack"; }
+ bool ExecuteForTask(int tid) const override;
private:
unsigned long slack_;
@@ -88,28 +99,18 @@
static bool IsTimerSlackSupported(int tid);
};
-#else
-
-class SetTimerSlackAction : public ProfileAction {
- public:
- SetTimerSlackAction(unsigned long) noexcept {}
-
- virtual bool ExecuteForTask(int) const { return true; }
-};
-
-#endif
-
// Set attribute profile element
class SetAttributeAction : public ProfileAction {
public:
- SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
+ SetAttributeAction(const IProfileAttribute* attribute, const std::string& value)
: attribute_(attribute), value_(value) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetAttribute"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
private:
- const ProfileAttribute* attribute_;
+ const IProfileAttribute* attribute_;
std::string value_;
};
@@ -118,10 +119,11 @@
public:
SetCgroupAction(const CgroupController& c, const std::string& p);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching(ResourceCacheType cache_type);
- virtual void DropResourceCaching(ResourceCacheType cache_type);
+ const char* Name() const override { return "SetCgroup"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
const CgroupController* controller() const { return &controller_; }
@@ -140,10 +142,11 @@
public:
WriteFileAction(const std::string& path, const std::string& value, bool logfailures);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching(ResourceCacheType cache_type);
- virtual void DropResourceCaching(ResourceCacheType cache_type);
+ const char* Name() const override { return "WriteFile"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
private:
std::string path_, value_;
@@ -158,8 +161,9 @@
class TaskProfile {
public:
- TaskProfile() : res_cached_(false) {}
+ TaskProfile(const std::string& name) : name_(name), res_cached_(false) {}
+ const std::string& Name() const { return name_; }
void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
void MoveTo(TaskProfile* profile);
@@ -169,6 +173,7 @@
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
private:
+ const std::string name_;
bool res_cached_;
std::vector<std::unique_ptr<ProfileAction>> elements_;
};
@@ -179,10 +184,11 @@
ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
: profiles_(profiles) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
- virtual void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
+ const char* Name() const override { return "ApplyProfileAction"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
private:
std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -194,7 +200,7 @@
static TaskProfiles& GetInstance();
TaskProfile* GetProfile(const std::string& name) const;
- const ProfileAttribute* GetAttribute(const std::string& name) const;
+ const IProfileAttribute* GetAttribute(const std::string& name) const;
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
bool use_fd_cache);
@@ -202,7 +208,7 @@
private:
std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
- std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
+ std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
TaskProfiles();
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d592366..11f414f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,5 +1,7 @@
LOCAL_PATH:= $(call my-dir)
+$(eval $(call declare-1p-copy-files,system/core/rootdir,))
+
#######################################
# init-debug.rc
include $(CLEAR_VARS)
@@ -197,15 +199,9 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(addsuffix .so,\
- $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(UBSAN_RUNTIME_LIBRARY) \
- $(TSAN_RUNTIME_LIBRARY) \
- $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_UBSAN_RUNTIME_LIBRARY) \
- $(2ND_TSAN_RUNTIME_LIBRARY))
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := \
+ $(SANITIZER_STEMS) \
+ $(2ND_SANITIZER_STEMS)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
diff --git a/rootdir/init.no_zygote.rc b/rootdir/init.no_zygote.rc
new file mode 100644
index 0000000..8e0afcb
--- /dev/null
+++ b/rootdir/init.no_zygote.rc
@@ -0,0 +1,2 @@
+# This is an empty file that does not specify any zygote services.
+# It exists to support targets that do not include a zygote.
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
index 5fd628f..c8d8932 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
@@ -27,6 +27,7 @@
using ::keymaster::TrustyKeymaster;
using ::ndk::ScopedAStatus;
using secureclock::TimeStampToken;
+using ::std::array;
using ::std::optional;
using ::std::shared_ptr;
using ::std::vector;
@@ -77,8 +78,13 @@
const optional<TimeStampToken>& timestampToken) override;
ScopedAStatus earlyBootEnded() override;
- ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
- std::vector<uint8_t>* ephemeralKeyBlob) override;
+ ScopedAStatus convertStorageKeyToEphemeral(const vector<uint8_t>& storageKeyBlob,
+ vector<uint8_t>* ephemeralKeyBlob) override;
+
+ ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override;
+ ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge,
+ vector<uint8_t>* rootOfTrust) override;
+ ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override;
protected:
std::shared_ptr<TrustyKeymaster> impl_;
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index 68a7912..44780e8 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -306,7 +306,7 @@
}
ScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(
- const std::vector<uint8_t>& storageKeyBlob, std::vector<uint8_t>* ephemeralKeyBlob) {
+ const vector<uint8_t>& storageKeyBlob, vector<uint8_t>* ephemeralKeyBlob) {
keymaster::ExportKeyRequest request(impl_->message_version());
request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());
request.key_format = KM_KEY_FORMAT_RAW;
@@ -321,4 +321,17 @@
return ScopedAStatus::ok();
}
+ScopedAStatus TrustyKeyMintDevice::getRootOfTrustChallenge(array<uint8_t, 16>* /* challenge */) {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus TrustyKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& /* challenge */,
+ vector<uint8_t>* /* rootOfTrust */) {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus TrustyKeyMintDevice::sendRootOfTrust(const vector<uint8_t>& /* rootOfTrust */) {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
index 5664829..099f189 100644
--- a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -71,9 +71,10 @@
} // namespace
ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
- info->versionNumber = 1;
+ info->versionNumber = 2;
info->rpcAuthorName = "Google";
info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
+ info->uniqueId = "Trusty: My password is ******";
return ScopedAStatus::ok();
}
diff --git a/trusty/test/driver/Android.bp b/trusty/test/driver/Android.bp
new file mode 100644
index 0000000..b813a04
--- /dev/null
+++ b/trusty/test/driver/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 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.
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test {
+ name: "trusty_driver_test",
+ srcs: [
+ "**/*.py",
+ ],
+ test_suites: ["general-tests"],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ enabled: true,
+ },
+ },
+}
diff --git a/trusty/test/driver/trusty_driver_test.py b/trusty/test/driver/trusty_driver_test.py
new file mode 100644
index 0000000..608fd47
--- /dev/null
+++ b/trusty/test/driver/trusty_driver_test.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+#
+# Copyright 2022 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.
+
+"""Test cases for Trusty Linux Driver."""
+
+import os
+import unittest
+
+def ReadFile(file_path):
+ with open(file_path, 'r') as f:
+ # Strip all trailing spaces, newline and null characters.
+ return f.read().rstrip(' \n\x00')
+
+def WriteFile(file_path, s):
+ with open(file_path, 'w') as f:
+ f.write(s)
+
+def IsTrustySupported():
+ return os.path.exists("/dev/trusty-ipc-dev0")
+
+@unittest.skipIf(not IsTrustySupported(), "Device does not support Trusty")
+class TrustyDriverTest(unittest.TestCase):
+ def testIrqDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-irq/unbind", "trusty:irq")
+ WriteFile("/sys/bus/platform/drivers/trusty-irq/bind", "trusty:irq")
+
+ def testLogDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-log/unbind", "trusty:log")
+ WriteFile("/sys/bus/platform/drivers/trusty-log/bind", "trusty:log")
+
+ @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
+ def testVirtioDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-virtio/unbind",
+ "trusty:virtio")
+ WriteFile("/sys/bus/platform/drivers/trusty-virtio/bind",
+ "trusty:virtio")
+
+ @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
+ def testTrustyDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty/unbind", "trusty")
+ WriteFile("/sys/bus/platform/drivers/trusty/bind", "trusty")
+
+ def testTrustyDriverVersion(self):
+ ver = ReadFile("/sys/bus/platform/devices/trusty/trusty_version")
+ self.assertTrue(ver.startswith("Project:"))
+
+ def testUntaintedLinux(self):
+ tainted = ReadFile("/proc/sys/kernel/tainted")
+ self.assertEqual(tainted, "0")
+
+ # stdcall test with shared memory buffers.
+ # Each test run takes up to 4 arguments:
+ # <obj_size>,<obj_count=1>,<repeat_share=1>,<repeat_access=3>
+ #
+ # Test single 4K shared memory object.
+ # Test 10 8MB objects, shared twice, each accessed twice. (8MB non-
+ # contiguous object is large enough to need several 4KB messages to
+ # describe)
+ # Test sharing 2 8MB objects 100 times without accessing it.
+ # Test 10 4K shared memory objects, shared 10 times, each accessed
+ # 10 times.
+ def testStdCall(self):
+ test = "/sys/devices/platform/trusty/trusty:test/trusty_test_run"
+ args = "0x1000 0x800000,10,2,2 0x800000,2,100,0 0x1000,10,10,10"
+ WriteFile(test, args)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index 74106ec..3a43774 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -13,7 +13,7 @@
# limitations under the License.
PRODUCT_PACKAGES += \
- spiproxyd \
- trusty_keymaster_set_attestation_key \
keymaster_soft_attestation_keys.xml \
-
+ spiproxyd \
+ trusty_driver_test \
+ trusty_keymaster_set_attestation_key \