Merge "Allow mapping of raw block devices to partition names"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 198e4de..d7e8f32 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,29 +1,5 @@
package {
- default_applicable_licenses: ["system_core_debuggerd_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
- name: "system_core_debuggerd_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-BSD",
- ],
- // large-scale-change unable to identify any license_text files
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_defaults {
@@ -32,6 +8,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wno-gcc-compat",
"-Wno-unused-argument",
"-Wno-unused-function",
"-Wno-nullability-completeness",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 530e0e8..b302918 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -47,19 +47,30 @@
using android::base::ReadFileToString;
using android::base::SendFileDescriptors;
+using android::base::StringAppendV;
using android::base::unique_fd;
using android::base::WriteStringToFd;
-static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
- const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;
- sigval val;
- val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
+#define TAG "libdebuggerd_client: "
- if (sigqueue(pid, signal, val) != 0) {
- PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
- return false;
+// Log an error both to the log (via LOG(ERROR)) and to the given fd.
+static void log_error(int fd, int errno_value, const char* format, ...) __printflike(3, 4) {
+ std::string message(TAG);
+
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(&message, format, ap);
+ va_end(ap);
+
+ if (errno_value != 0) {
+ message = message + ": " + strerror(errno_value);
}
- return true;
+
+ if (fd != -1) {
+ dprintf(fd, "%s\n", message.c_str());
+ }
+
+ LOG(ERROR) << message;
}
template <typename Duration>
@@ -74,13 +85,11 @@
* Returns the wchan data for each thread in the process,
* or empty string if unable to obtain any data.
*/
-static std::string get_wchan_data(pid_t pid) {
- std::stringstream buffer;
+static std::string get_wchan_data(int fd, pid_t pid) {
std::vector<pid_t> tids;
-
if (!android::procinfo::GetProcessTids(pid, &tids)) {
- LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
- return buffer.str();
+ log_error(fd, 0, "failed to get process tids");
+ return "";
}
std::stringstream data;
@@ -88,12 +97,13 @@
std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
std::string wchan_str;
if (!ReadFileToString(path, &wchan_str, true)) {
- PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
+ log_error(fd, errno, "failed to read \"%s\"", path.c_str());
continue;
}
data << "sysTid=" << std::left << std::setw(10) << tid << wchan_str << "\n";
}
+ std::stringstream buffer;
if (std::string str = data.str(); !str.empty()) {
buffer << "\n----- Waiting Channels: pid " << pid << " at " << get_timestamp() << " -----\n"
<< "Cmd line: " << android::base::Join(get_command_line(pid), " ") << "\n";
@@ -101,16 +111,9 @@
buffer << "----- end " << std::to_string(pid) << " -----\n";
buffer << "\n";
}
-
return buffer.str();
}
-static void dump_wchan_data(const std::string& data, int fd, pid_t pid) {
- if (!WriteStringToFd(data, fd)) {
- LOG(WARNING) << "libdebuggerd_client: Failed to dump wchan data for pid: " << pid;
- }
-}
-
bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
unique_fd output_fd) {
pid_t pid = tid;
@@ -119,51 +122,51 @@
android::procinfo::ProcessInfo procinfo;
std::string error;
if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {
- LOG(ERROR) << "libdebugged_client: failed to get process info: " << error;
+ log_error(output_fd, 0, "failed to get process info: %s", error.c_str());
return false;
}
pid = procinfo.pid;
}
- LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
- unique_fd sockfd;
- const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
- auto time_left = [&end]() { return end - std::chrono::steady_clock::now(); };
- auto set_timeout = [timeout_ms, &time_left](int sockfd) {
- if (timeout_ms <= 0) {
- return sockfd;
- }
+ LOG(INFO) << TAG "started dumping process " << pid;
- auto remaining = time_left();
+ // Rather than try to deal with poll() all the way through the flow, we update
+ // the socket timeout between each step (and only use poll() during the final
+ // copy loop).
+ const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
+ auto update_timeout = [timeout_ms, &output_fd](int sockfd, auto end) {
+ if (timeout_ms <= 0) return true;
+
+ auto remaining = end - std::chrono::steady_clock::now();
if (remaining < decltype(remaining)::zero()) {
- LOG(ERROR) << "libdebuggerd_client: timeout expired";
- return -1;
+ log_error(output_fd, 0, "timeout expired");
+ return false;
}
struct timeval timeout;
populate_timeval(&timeout, remaining);
-
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
- PLOG(ERROR) << "libdebuggerd_client: failed to set receive timeout";
- return -1;
+ log_error(output_fd, errno, "failed to set receive timeout");
+ return false;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
- PLOG(ERROR) << "libdebuggerd_client: failed to set send timeout";
- return -1;
+ log_error(output_fd, errno, "failed to set send timeout");
+ return false;
}
-
- return sockfd;
+ return true;
};
- sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
+ unique_fd sockfd(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
if (sockfd == -1) {
- PLOG(ERROR) << "libdebugger_client: failed to create socket";
+ log_error(output_fd, errno, "failed to create socket");
return false;
}
- if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
+ if (!update_timeout(sockfd, end)) return false;
+
+ if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
- PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
+ log_error(output_fd, errno, "failed to connect to tombstoned");
return false;
}
@@ -171,15 +174,11 @@
.dump_type = dump_type,
.pid = pid,
};
- if (!set_timeout(sockfd)) {
- PLOG(ERROR) << "libdebugger_client: failed to set timeout";
- return false;
- }
// Create an intermediate pipe to pass to the other end.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write)) {
- PLOG(ERROR) << "libdebuggerd_client: failed to create pipe";
+ log_error(output_fd, errno, "failed to create pipe");
return false;
}
@@ -194,71 +193,69 @@
}
if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
- PLOG(ERROR) << "failed to set pipe buffer size";
+ log_error(output_fd, errno, "failed to set pipe buffer size");
}
- ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+ if (!update_timeout(sockfd, end)) return false;
+ ssize_t rc = SendFileDescriptors(sockfd, &req, sizeof(req), pipe_write.get());
pipe_write.reset();
if (rc != sizeof(req)) {
- PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
+ log_error(output_fd, errno, "failed to send output fd to tombstoned");
return false;
}
+ auto get_response = [&output_fd](const char* kind, int sockfd, InterceptResponse* response) {
+ ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd, response, sizeof(*response), MSG_TRUNC));
+ if (rc == 0) {
+ log_error(output_fd, 0, "failed to read %s response from tombstoned: timeout reached?", kind);
+ return false;
+ } else if (rc == -1) {
+ log_error(output_fd, errno, "failed to read %s response from tombstoned", kind);
+ return false;
+ } else if (rc != sizeof(*response)) {
+ log_error(output_fd, 0,
+ "received packet of unexpected length from tombstoned while reading %s response: "
+ "expected %zd, received %zd",
+ kind, sizeof(response), rc);
+ return false;
+ }
+ return true;
+ };
+
// Check to make sure we've successfully registered.
InterceptResponse response;
- rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
- if (rc == 0) {
- LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
- << "timeout reached?";
- return false;
- } else if (rc == -1) {
- PLOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned";
- return false;
- } else if (rc != sizeof(response)) {
- LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
- "reading initial response: expected "
- << sizeof(response) << ", received " << rc;
- return false;
- }
-
+ if (!update_timeout(sockfd, end)) return false;
+ if (!get_response("initial", sockfd, &response)) return false;
if (response.status != InterceptStatus::kRegistered) {
- LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
- << static_cast<int>(response.status);
+ log_error(output_fd, 0, "unexpected registration response: %d",
+ static_cast<int>(response.status));
return false;
}
- if (!send_signal(tid, dump_type)) {
+ // Send the signal.
+ const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;
+ sigval val = {.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0};
+ if (sigqueue(pid, signal, val) != 0) {
+ log_error(output_fd, errno, "failed to send signal to pid %d", pid);
return false;
}
- rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
- if (rc == 0) {
- LOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned: "
- "timeout reached?";
- return false;
- } else if (rc == -1) {
- PLOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned";
- return false;
- } else if (rc != sizeof(response)) {
- LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
- "reading confirmation response: expected "
- << sizeof(response) << ", received " << rc;
- return false;
- }
-
+ if (!update_timeout(sockfd, end)) return false;
+ if (!get_response("status", sockfd, &response)) return false;
if (response.status != InterceptStatus::kStarted) {
response.error_message[sizeof(response.error_message) - 1] = '\0';
- LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+ log_error(output_fd, 0, "tombstoned reported failure: %s", response.error_message);
return false;
}
// Forward output from the pipe to the output fd.
while (true) {
- auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left()).count();
+ auto remaining = end - std::chrono::steady_clock::now();
+ auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count();
if (timeout_ms <= 0) {
remaining_ms = -1;
} else if (remaining_ms < 0) {
- LOG(ERROR) << "libdebuggerd_client: timeout expired";
+ log_error(output_fd, 0, "timeout expired");
return false;
}
@@ -271,11 +268,11 @@
if (errno == EINTR) {
continue;
} else {
- PLOG(ERROR) << "libdebuggerd_client: error while polling";
+ log_error(output_fd, errno, "error while polling");
return false;
}
} else if (rc == 0) {
- LOG(ERROR) << "libdebuggerd_client: timeout expired";
+ log_error(output_fd, 0, "timeout expired");
return false;
}
@@ -285,17 +282,17 @@
// Done.
break;
} else if (rc == -1) {
- PLOG(ERROR) << "libdebuggerd_client: error while reading";
+ log_error(output_fd, errno, "error while reading");
return false;
}
if (!android::base::WriteFully(output_fd.get(), buf, rc)) {
- PLOG(ERROR) << "libdebuggerd_client: error while writing";
+ log_error(output_fd, errno, "error while writing");
return false;
}
}
- LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
+ LOG(INFO) << TAG "done dumping process " << pid;
return true;
}
@@ -313,14 +310,16 @@
// debuggerd_trigger_dump results in every thread in the process being interrupted
// by a signal, so we need to fetch the wchan data before calling that.
- std::string wchan_data = get_wchan_data(tid);
+ std::string wchan_data = get_wchan_data(fd, tid);
int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
// Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read
// kernel stack traces (/proc/*/stack).
- dump_wchan_data(wchan_data, fd, tid);
+ if (!WriteStringToFd(wchan_data, fd)) {
+ LOG(WARNING) << TAG "Failed to dump wchan data for pid: " << tid;
+ }
return ret;
}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 360ea95..e20e8d9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -93,8 +93,18 @@
errx(1, "process %d is a zombie", pid);
}
- if (kill(pid, 0) != 0) {
- err(1, "cannot send signal to process %d", pid);
+ // Send a signal to the main thread pid, not a side thread. The signal
+ // handler always sets the crashing tid to the main thread pid when sent this
+ // signal. This is to avoid a problem where the signal is sent to a process,
+ // but happens on a side thread and the intercept mismatches since it
+ // is looking for the main thread pid, not the tid of this random thread.
+ // See b/194346289 for extra details.
+ if (kill(proc_info.pid, 0) != 0) {
+ if (pid == proc_info.pid) {
+ err(1, "cannot send signal to process %d", pid);
+ } else {
+ err(1, "cannot send signal to main thread %d (requested thread %d)", proc_info.pid, pid);
+ }
}
unique_fd piperead, pipewrite;
@@ -103,9 +113,13 @@
}
std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
+ if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) {
redirect_thread.join();
- errx(1, "failed to dump process %d", pid);
+ if (pid == proc_info.pid) {
+ errx(1, "failed to dump process %d", pid);
+ } else {
+ errx(1, "failed to dump main thread %d (requested thread %d)", proc_info.pid, pid);
+ }
}
redirect_thread.join();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 24804d0..f24c4fc 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -58,6 +58,7 @@
#include <scoped_minijail.h>
#include "debuggerd/handler.h"
+#include "libdebuggerd/utility.h"
#include "protocol.h"
#include "tombstoned/tombstoned.h"
#include "util.h"
@@ -526,6 +527,8 @@
std::vector<std::string> log_sources(2);
ConsumeFd(std::move(output_fd), &log_sources[0]);
logcat_collector.Collect(&log_sources[1]);
+ // Tag dump only available in the tombstone, not logcat.
+ ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
for (const auto& result : log_sources) {
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
@@ -597,6 +600,12 @@
ConsumeFd(std::move(output_fd), &log_sources[0]);
logcat_collector.Collect(&log_sources[1]);
+ // Tag dump only in tombstone, not logcat, and tagging is not used for
+ // overflow protection in the scudo secondary (guard pages are used instead).
+ if (GetParam() < 0x10000) {
+ ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
+ }
+
for (const auto& result : log_sources) {
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
@@ -637,6 +646,7 @@
std::to_string(GetParam()) + R"(-byte allocation)");
ASSERT_MATCH(result, R"((^|\s)allocated by thread .*
#00 pc)");
+ ASSERT_MATCH(result, "Memory tags around the fault address");
#else
GTEST_SKIP() << "Requires aarch64";
#endif
@@ -686,6 +696,9 @@
ConsumeFd(std::move(output_fd), &log_sources[0]);
logcat_collector.Collect(&log_sources[1]);
+ // Tag dump only in the tombstone, not logcat.
+ ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
+
for (const auto& result : log_sources) {
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
ASSERT_THAT(result, HasSubstr("Note: multiple potential causes for this crash were detected, "
@@ -706,21 +719,26 @@
#if defined(__aarch64__)
static uintptr_t CreateTagMapping() {
- uintptr_t mapping =
- reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
- if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+ // Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right
+ // of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.
+ size_t page_size = getpagesize();
+ void* mapping = mmap(nullptr, page_size * 3, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ uintptr_t mapping_uptr = reinterpret_cast<uintptr_t>(mapping);
+ if (mapping == MAP_FAILED) {
return 0;
}
- __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
- :
- : "r"(mapping + (1ULL << 56))
- : "memory");
- return mapping;
+ mprotect(reinterpret_cast<void*>(mapping_uptr + page_size), page_size,
+ PROT_READ | PROT_WRITE | PROT_MTE);
+ // Stripe the mapping, where even granules get tag '1', and odd granules get tag '0'.
+ for (uintptr_t offset = 0; offset < page_size; offset += 2 * kTagGranuleSize) {
+ uintptr_t tagged_addr = mapping_uptr + page_size + offset + (1ULL << 56);
+ __asm__ __volatile__(".arch_extension mte; stg %0, [%0]" : : "r"(tagged_addr) : "memory");
+ }
+ return mapping_uptr + page_size;
}
#endif
-TEST_F(CrasherTest, mte_tag_dump) {
+TEST_F(CrasherTest, mte_register_tag_dump) {
#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
@@ -753,6 +771,107 @@
#endif
}
+TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) {
+#if defined(__aarch64__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
+ p[0] = 0; // Untagged pointer, tagged memory.
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(Memory tags around the fault address.*
+\s*=>0x[0-9a-f]+000:\[1\] 0 1 0)");
+#else
+ GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
+TEST_F(CrasherTest, mte_fault_tag_dump) {
+#if defined(__aarch64__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
+ p[320] = 0; // Untagged pointer, tagged memory.
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(Memory tags around the fault address.*
+\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
+\s*=>0x[0-9a-f]+: 1 0 1 0 \[1\] 0 1 0 1 0 1 0 1 0 1 0
+\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
+)");
+#else
+ GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
+TEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) {
+#if defined(__aarch64__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ size_t page_size = getpagesize();
+ volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
+ p[page_size - kTagGranuleSize * 2] = 0; // Untagged pointer, tagged memory.
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(Memory tags around the fault address)");
+ ASSERT_MATCH(result,
+ R"(\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
+\s*=>0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 \[1\] 0
+
+)"); // Ensure truncation happened and there's a newline after the tag fault.
+#else
+ GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
TEST_F(CrasherTest, LD_PRELOAD) {
int intercept_result;
unique_fd output_fd;
@@ -1706,3 +1825,29 @@
ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output));
ASSERT_EQ("foo", output);
}
+
+// Verify that when an intercept is present for the main thread, and the signal
+// is received on a different thread, the intercept still works.
+TEST_F(CrasherTest, intercept_for_main_thread_signal_on_side_thread) {
+ StartProcess([]() {
+ std::thread thread([]() {
+ // Raise the signal on the side thread.
+ raise_debugger_signal(kDebuggerdNativeBacktrace);
+ });
+ thread.join();
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
+ FinishCrasher();
+ AssertDeath(0);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 375dbed..35be2bf 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -155,18 +155,14 @@
* could allocate memory or hold a lock.
*/
static void log_signal_summary(const siginfo_t* info) {
- char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
- if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
- strcpy(thread_name, "<name unknown>");
- } else {
- // short names are null terminated by prctl, but the man page
- // implies that 16 byte names are not.
- thread_name[MAX_TASK_NAME_LEN] = 0;
+ char main_thread_name[MAX_TASK_NAME_LEN + 1];
+ if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+ strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
}
if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
- async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
- thread_name);
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for pid %d (%s)", __getpid(),
+ main_thread_name);
return;
}
@@ -181,9 +177,13 @@
get_signal_sender(sender_desc, sizeof(sender_desc), info);
}
- char main_thread_name[MAX_TASK_NAME_LEN + 1];
- if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
- strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+ char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
+ if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+ strcpy(thread_name, "<name unknown>");
+ } else {
+ // short names are null terminated by prctl, but the man page
+ // implies that 16 byte names are not.
+ thread_name[MAX_TASK_NAME_LEN] = 0;
}
async_safe_format_log(ANDROID_LOG_FATAL, "libc",
@@ -532,8 +532,13 @@
log_signal_summary(info);
+ // If we got here due to the signal BIONIC_SIGNAL_DEBUGGER, it's possible
+ // this is not the main thread, which can cause the intercept logic to fail
+ // since the intercept is only looking for the main thread. In this case,
+ // setting crashing_tid to pid instead of the current thread's tid avoids
+ // the problem.
debugger_thread_info thread_info = {
- .crashing_tid = __gettid(),
+ .crashing_tid = (signal_number == BIONIC_SIGNAL_DEBUGGER) ? __getpid() : __gettid(),
.pseudothread_tid = -1,
.siginfo = info,
.ucontext = context,
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index c490fb1..24ae169 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -96,4 +96,8 @@
// Number of bytes per MTE granule.
constexpr size_t kTagGranuleSize = 16;
+// Number of rows and columns to display in an MTE tag dump.
+constexpr size_t kNumTagColumns = 16;
+constexpr size_t kNumTagRows = 16;
+
#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index ad903ce..9c01f15 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -593,6 +593,9 @@
};
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");
}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index abd1f12..ff12017 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -362,8 +362,10 @@
dump.set_mapping_name(map_info->name());
}
- char buf[256];
- uint8_t tags[256 / kTagGranuleSize];
+ 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) {
@@ -377,7 +379,19 @@
}
dump.set_memory(buf, bytes);
- dump.set_tags(tags, bytes / kTagGranuleSize);
+
+ 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);
}
@@ -531,6 +545,50 @@
dump_log_file(tombstone, "main", pid);
}
+static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
+ unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
+ if (tombstone.arch() != Architecture::ARM64) return;
+
+ fault_addr = untag_address(fault_addr);
+ constexpr size_t kNumGranules = kNumTagRows * kNumTagColumns;
+ constexpr size_t kBytesToRead = kNumGranules * kTagGranuleSize;
+
+ // If the low part of the tag dump would underflow to the high address space, it's probably not
+ // a valid address for us to dump tags from.
+ if (fault_addr < kBytesToRead / 2) return;
+
+ unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+
+ constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
+ size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;
+ MemoryDump tag_dump;
+ size_t granules_to_read = kNumGranules;
+
+ // Attempt to read the first tag. If reading fails, this likely indicates the
+ // lowest touched page is inaccessible or not marked with PROT_MTE.
+ // Fast-forward over pages until one has tags, or we exhaust the search range.
+ while (memory->ReadTag(start_address) < 0) {
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ size_t bytes_to_next_page = page_size - (start_address % page_size);
+ if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;
+ start_address += bytes_to_next_page;
+ granules_to_read -= bytes_to_next_page / kTagGranuleSize;
+ }
+ tag_dump.set_begin_address(start_address);
+
+ std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();
+
+ for (size_t i = 0; i < granules_to_read; ++i) {
+ long tag = memory->ReadTag(start_address + i * kTagGranuleSize);
+ if (tag < 0) break;
+ mte_tags->push_back(static_cast<uint8_t>(tag));
+ }
+
+ if (!mte_tags->empty()) {
+ *signal->mutable_fault_adjacent_metadata() = tag_dump;
+ }
+}
+
static std::optional<uint64_t> read_uptime_secs() {
std::string uptime;
if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
@@ -594,7 +652,9 @@
if (process_info.has_fault_address) {
sig.set_has_fault_address(true);
- sig.set_fault_address(process_info.maybe_tagged_fault_address);
+ uintptr_t fault_addr = process_info.maybe_tagged_fault_address;
+ sig.set_fault_address(fault_addr);
+ dump_tags_around_fault_addr(&sig, result, unwinder, fault_addr);
}
*result.mutable_signal_info() = sig;
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index a932d48..053299a 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -29,6 +29,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
+#include <bionic/macros.h>
#include "tombstone.pb.h"
@@ -193,8 +194,11 @@
uint64_t addr = mem.begin_address();
for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {
uint64_t tagged_addr = addr;
- if (mem.tags().size() > offset / kTagGranuleSize) {
- tagged_addr |= static_cast<uint64_t>(mem.tags()[offset / kTagGranuleSize]) << 56;
+ if (mem.has_arm_mte_metadata() &&
+ mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {
+ tagged_addr |=
+ static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])
+ << 56;
}
std::string line = StringPrintf(" %0*" PRIx64, word_size * 2, tagged_addr + offset);
@@ -232,6 +236,60 @@
print_thread_memory_dump(callback, tombstone, thread);
}
+static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {
+ if (!tombstone.has_signal_info()) return;
+
+ const Signal& signal = tombstone.signal_info();
+
+ if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {
+ return;
+ }
+
+ const MemoryDump& memory_dump = signal.fault_adjacent_metadata();
+
+ if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {
+ return;
+ }
+
+ const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();
+
+ CBS("");
+ CBS("Memory tags around the fault address (0x%" PRIx64 "), one tag per %zu bytes:",
+ signal.fault_address(), kTagGranuleSize);
+ constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
+
+ size_t tag_index = 0;
+ size_t num_tags = tags.length();
+ uintptr_t fault_granule = untag_address(signal.fault_address()) & ~(kTagGranuleSize - 1);
+ for (size_t row = 0; tag_index < num_tags; ++row) {
+ uintptr_t row_addr =
+ (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;
+ std::string row_contents;
+ bool row_has_fault = false;
+
+ for (size_t column = 0; column < kNumTagColumns; ++column) {
+ uintptr_t granule_addr = row_addr + column * kTagGranuleSize;
+ if (granule_addr < memory_dump.begin_address() ||
+ granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {
+ row_contents += " . ";
+ } else if (granule_addr == fault_granule) {
+ row_contents += StringPrintf("[%1hhx]", tags[tag_index++]);
+ row_has_fault = true;
+ } else {
+ row_contents += StringPrintf(" %1hhx ", tags[tag_index++]);
+ }
+ }
+
+ if (row_contents.back() == ' ') row_contents.pop_back();
+
+ if (row_has_fault) {
+ CBS(" =>0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
+ } else {
+ CBS(" 0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
+ }
+ }
+}
+
static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
const Thread& thread) {
print_thread_header(callback, tombstone, thread, true);
@@ -299,6 +357,8 @@
}
}
+ print_tag_dump(callback, tombstone);
+
print_thread_memory_dump(callback, tombstone, thread);
CBS("");
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 2c645b5..a7506b7 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -402,6 +402,8 @@
case TRAP_HWBKPT: return "TRAP_HWBKPT";
case TRAP_UNK:
return "TRAP_UNDIAGNOSED";
+ case TRAP_PERF:
+ return "TRAP_PERF";
}
if ((si->si_code & 0xff) == SIGTRAP) {
switch ((si->si_code >> 8) & 0xff) {
@@ -423,7 +425,7 @@
return "PTRACE_EVENT_STOP";
}
}
- static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
+ static_assert(NSIGTRAP == TRAP_PERF, "missing TRAP_* si_code");
break;
}
// Then the other codes...
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 22fc30e..a701212 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -56,8 +56,11 @@
bool has_fault_address = 8;
uint64 fault_address = 9;
+ // Note, may or may not contain the dump of the actual memory contents. Currently, on arm64, we
+ // only include metadata, and not the contents.
+ MemoryDump fault_adjacent_metadata = 10;
- reserved 10 to 999;
+ reserved 11 to 999;
}
message HeapObject {
@@ -142,14 +145,22 @@
reserved 9 to 999;
}
+message ArmMTEMetadata {
+ // One memory tag per granule (e.g. every 16 bytes) of regular memory.
+ bytes memory_tags = 1;
+ reserved 2 to 999;
+}
+
message MemoryDump {
string register_name = 1;
string mapping_name = 2;
uint64 begin_address = 3;
bytes memory = 4;
- bytes tags = 5;
+ oneof metadata {
+ ArmMTEMetadata arm_mte_metadata = 6;
+ }
- reserved 6 to 999;
+ reserved 5, 7 to 999;
}
message MemoryMapping {
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index c39f4e4..fc43f4e 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -5,4 +5,4 @@
socket tombstoned_crash seqpacket 0666 system system
socket tombstoned_intercept seqpacket 0666 system system
socket tombstoned_java_trace seqpacket 0666 system system
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3d63a44..d4fc2b9 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -15,7 +15,10 @@
//
package {
- default_applicable_licenses: ["system_core_fs_mgr_license"],
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ "system_core_fs_mgr_license",
+ ],
}
// Added automatically by a large-scale-change that took the approach of
@@ -36,10 +39,9 @@
name: "system_core_fs_mgr_license",
visibility: [":__subpackages__"],
license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
"SPDX-license-identifier-MIT",
],
- // large-scale-change unable to identify any license_text files
+ license_text: ["NOTICE"],
}
cc_defaults {
@@ -142,6 +144,7 @@
// It does not have a stable interface.
name: "libfs_mgr",
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
defaults: [
"libfs_mgr_defaults",
@@ -167,6 +170,7 @@
name: "libfstab",
vendor_available: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
defaults: ["fs_mgr_defaults"],
diff --git a/fs_mgr/NOTICE b/fs_mgr/NOTICE
new file mode 100644
index 0000000..3972a40
--- /dev/null
+++ b/fs_mgr/NOTICE
@@ -0,0 +1,21 @@
+Copyright (C) 2016 The Android Open Source Project
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
index cbf6845..af0699b 100644
--- a/fs_mgr/file_wait.cpp
+++ b/fs_mgr/file_wait.cpp
@@ -206,6 +206,9 @@
}
int64_t OneShotInotify::RemainingMs() const {
+ if (relative_timeout_ == std::chrono::milliseconds::max()) {
+ return std::chrono::milliseconds::max().count();
+ }
auto remaining = (std::chrono::steady_clock::now() - start_time_);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
return (relative_timeout_ - elapsed).count();
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 01c8ad3..4826ccf 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -790,20 +790,26 @@
int save_errno = 0;
int gc_allowance = 0;
std::string opts;
+ std::string checkpoint_opts;
bool try_f2fs_gc_allowance = is_f2fs(entry.fs_type) && entry.fs_checkpoint_opts.length() > 0;
+ bool try_f2fs_fallback = false;
Timer t;
do {
- if (save_errno == EINVAL && try_f2fs_gc_allowance) {
- PINFO << "Kernel does not support checkpoint=disable:[n]%, trying without.";
+ if (save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)) {
+ PINFO << "Kernel does not support " << checkpoint_opts << ", trying without.";
try_f2fs_gc_allowance = false;
+ // Attempt without gc allowance before dropping.
+ try_f2fs_fallback = !try_f2fs_fallback;
}
if (try_f2fs_gc_allowance) {
- opts = entry.fs_options + entry.fs_checkpoint_opts + ":" +
- std::to_string(gc_allowance) + "%";
+ checkpoint_opts = entry.fs_checkpoint_opts + ":" + std::to_string(gc_allowance) + "%";
+ } else if (try_f2fs_fallback) {
+ checkpoint_opts = entry.fs_checkpoint_opts;
} else {
- opts = entry.fs_options;
+ checkpoint_opts = "";
}
+ opts = entry.fs_options + checkpoint_opts;
if (save_errno == EAGAIN) {
PINFO << "Retrying mount (source=" << source << ",target=" << target
<< ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
@@ -814,7 +820,7 @@
save_errno = errno;
if (try_f2fs_gc_allowance) gc_allowance += 10;
} while ((ret && save_errno == EAGAIN && gc_allowance <= 100) ||
- (ret && save_errno == EINVAL && try_f2fs_gc_allowance));
+ (ret && save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)));
const char* target_missing = "";
const char* source_missing = "";
if (save_errno == ENOENT) {
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index e3ef232..75d1e0d 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -91,12 +91,6 @@
if (key == bootconfig_key) {
*out_val = value;
return true;
- } else if (android_key == "hardware" && android_key == key) {
- // bootconfig doesn't allow subkeys and values to coexist, so
- // "androidboot.hardware" cannot be used. It is replaced in
- // bootconfig with "hardware"
- *out_val = value;
- return true;
}
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d0c89b9..f5ab557 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -413,17 +413,24 @@
return fstab_result;
}
-// Identify path to fstab file. Lookup is based on pattern
-// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
-// folders /odm/etc, vendor/etc, or /.
+// Return the path to the fstab file. There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot. Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
std::string GetFstabPath() {
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
- for (const char* prefix :
- {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) {
+ for (const char* prefix : {// late-boot/post-boot locations
+ "/odm/etc/fstab.", "/vendor/etc/fstab.",
+ // early boot locations
+ "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+ "/fstab.", "/first_stage_ramdisk/fstab."}) {
std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
index 74d160e..294e727 100644
--- a/fs_mgr/include/fs_mgr/file_wait.h
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -23,6 +23,9 @@
// Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)
// must already exist. For example, to wait on /dev/block/dm-6, /dev/block must
// be a valid directory.
+//
+// If relative_timeout is std::chrono::milliseconds::max(), then the wait will
+// block indefinitely.
bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);
// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index c4874b8..a5eda29 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -170,19 +170,18 @@
return access("/system/bin/recovery", F_OK) == 0;
}
-bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
- const std::chrono::milliseconds& timeout_ms) {
+bool DeviceMapper::CreateEmptyDevice(const std::string& name) {
std::string uuid = GenerateUuid();
- if (!CreateDevice(name, uuid)) {
- return false;
- }
+ return CreateDevice(name, uuid);
+}
+bool DeviceMapper::WaitForDevice(const std::string& name,
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
// We use the unique path for testing whether the device is ready. After
// that, it's safe to use the dm-N path which is compatible with callers
// that expect it to be formatted as such.
std::string unique_path;
- if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
- !GetDmDevicePathByName(name, path)) {
+ if (!GetDeviceUniquePath(name, &unique_path) || !GetDmDevicePathByName(name, path)) {
DeleteDevice(name);
return false;
}
@@ -208,6 +207,25 @@
return true;
}
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms) {
+ if (!CreateEmptyDevice(name)) {
+ return false;
+ }
+
+ if (!LoadTableAndActivate(name, table)) {
+ DeleteDevice(name);
+ return false;
+ }
+
+ if (!WaitForDevice(name, timeout_ms, path)) {
+ DeleteDevice(name);
+ return false;
+ }
+
+ return true;
+}
+
bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
struct dm_ioctl io;
InitIo(&io, name);
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 8006db2..541f254 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -29,6 +29,7 @@
#include <thread>
#include <android-base/file.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
@@ -679,3 +680,13 @@
ASSERT_NE(0, access(path.c_str(), F_OK));
ASSERT_EQ(ENOENT, errno);
}
+
+TEST(libdm, CreateEmptyDevice) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.CreateEmptyDevice("empty-device"));
+ auto guard =
+ android::base::make_scope_guard([&]() { dm.DeleteDeviceIfExists("empty-device", 5s); });
+
+ // Empty device should be in suspended state.
+ ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState("empty-device"));
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 70b14fa..8fcdf74 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -115,6 +115,19 @@
// - ACTIVE: resumes the device.
bool ChangeState(const std::string& name, DmDeviceState state);
+ // Creates empty device.
+ // This supports a use case when a caller doesn't need a device straight away, but instead
+ // asks kernel to create it beforehand, thus avoiding blocking itself from waiting for ueventd
+ // to create user space paths.
+ // Callers are expected to then activate their device by calling LoadTableAndActivate function.
+ // To avoid race conditions, callers must still synchronize with ueventd by calling
+ // WaitForDevice function.
+ bool CreateEmptyDevice(const std::string& name);
+
+ // Waits for device paths to be created in the user space.
+ bool WaitForDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
+ std::string* path);
+
// Creates a device, loads the given table, and activates it. If the device
// is not able to be activated, it is destroyed, and false is returned.
// After creation, |path| contains the result of calling
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index b62e33f..d16b8d6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -21,6 +21,7 @@
cc_library_headers {
name: "libfiemap_headers",
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["include"],
}
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 3c8ab42..b31c78d 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -16,6 +16,7 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -299,6 +300,27 @@
ASSERT_EQ(errno, ENOENT);
}
+TEST_F(SplitFiemapTest, CorruptSplit) {
+ unique_fd fd(open(testfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0700));
+ ASSERT_GE(fd, 0);
+
+ // Make a giant random string.
+ std::vector<char> data;
+ for (size_t i = 0x1; i < 0x7f; i++) {
+ for (size_t j = 0; j < 100; j++) {
+ data.emplace_back(i);
+ }
+ }
+ ASSERT_GT(data.size(), PATH_MAX);
+
+ data.emplace_back('\n');
+
+ ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
+ fd = {};
+
+ ASSERT_TRUE(SplitFiemap::RemoveSplitFiles(testfile));
+}
+
static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
std::string result;
for (int i = 0; i < num_files; i++) {
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 36bb3df..0df6125 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -136,6 +136,7 @@
return FiemapStatus::FromErrno(errno);
}
}
+ fsync(fd.get());
// Unset this bit, so we don't unlink on destruction.
out->creating_ = false;
@@ -192,6 +193,9 @@
std::vector<std::string> files;
if (GetSplitFileList(file_path, &files)) {
for (const auto& file : files) {
+ if (access(file.c_str(), F_OK) != 0 && (errno == ENOENT || errno == ENAMETOOLONG)) {
+ continue;
+ }
ok &= android::base::RemoveFileIfExists(file, message);
}
}
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 62493eb..0cbd9db 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -28,6 +28,7 @@
name: "libfs_avb",
defaults: ["fs_mgr_defaults"],
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 86ca8f3..fc2d8a1 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -31,6 +31,7 @@
name: "liblp",
host_supported: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
defaults: ["fs_mgr_defaults"],
cppflags: [
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index aa1f415..1c44e53 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -182,38 +182,6 @@
vendor_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"],
@@ -311,7 +279,6 @@
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
"libbase",
- "libbinder",
"libext2_uuid",
"libext4_utils",
"libfs_mgr_binder",
@@ -413,49 +380,6 @@
require_root: true,
}
-cc_defaults {
- name: "snapuserd_defaults",
- defaults: [
- "fs_mgr_defaults",
- ],
- srcs: [
- "snapuserd_server.cpp",
- "snapuserd.cpp",
- "snapuserd_daemon.cpp",
- "snapuserd_worker.cpp",
- "snapuserd_readahead.cpp",
- ],
-
- cflags: [
- "-Wall",
- "-Werror"
- ],
-
- static_libs: [
- "libbase",
- "libbrotli",
- "libcutils_sockets",
- "libdm",
- "libgflags",
- "liblog",
- "libsnapshot_cow",
- "libz",
- ],
-}
-
-cc_binary {
- name: "snapuserd",
- defaults: ["snapuserd_defaults"],
- init_rc: [
- "snapuserd.rc",
- ],
- static_executable: true,
- system_shared_libs: [],
- ramdisk_available: true,
- vendor_ramdisk_available: true,
- recovery_available: true,
-}
-
cc_test {
name: "cow_api_test",
defaults: [
@@ -557,43 +481,6 @@
},
}
-cc_test {
- name: "cow_snapuserd_test",
- defaults: [
- "fs_mgr_defaults",
- ],
- srcs: [
- "cow_snapuserd_test.cpp",
- "snapuserd.cpp",
- "snapuserd_worker.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
- static_libs: [
- "libbrotli",
- "libgtest",
- "libsnapshot_cow",
- "libsnapshot_snapuserd",
- "libcutils_sockets",
- "libz",
- "libfs_mgr",
- "libdm",
- ],
- header_libs: [
- "libstorage_literals_headers",
- "libfiemap_headers",
- ],
- test_min_api_level: 30,
- auto_gen_config: true,
- require_root: false,
-}
-
cc_binary {
name: "inspect_cow",
host_supported: true,
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index c0649ca..e2abdba 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -191,6 +191,9 @@
// Merge failure code, filled if state == MergeFailed.
MergeFailureCode merge_failure_code = 7;
+
+ // Source build fingerprint.
+ string source_build_fingerprint = 8;
}
// Next: 10
@@ -223,4 +226,7 @@
// Merge failure code, filled if the merge failed at any time (regardless
// of whether it succeeded at a later time).
MergeFailureCode merge_failure_code = 9;
+
+ // The source fingerprint at the time the OTA was downloaded.
+ string source_build_fingerprint = 10;
}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index b75b154..ecfdefe 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -981,6 +981,158 @@
ASSERT_EQ(num_clusters, 1);
}
+TEST_F(CowTest, BigSeqOp) {
+ CowOptions options;
+ CowWriter writer(options);
+ const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ auto iter = reader.GetRevMergeOpIter();
+
+ for (int i = 0; i < seq_len; i++) {
+ ASSERT_TRUE(!iter->Done());
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op.new_block, seq_len - i);
+
+ iter->Next();
+ }
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, MissingSeqOp) {
+ CowOptions options;
+ CowWriter writer(options);
+ const int seq_len = 10;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_FALSE(reader.Parse(cow_->fd));
+}
+
+TEST_F(CowTest, RevMergeOpItrTest) {
+ CowOptions options;
+ options.cluster_ops = 5;
+ options.num_merge_ops = 1;
+ CowWriter writer(options);
+ uint32_t sequence[] = {2, 10, 6, 7, 3, 5};
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer.AddSequenceData(6, sequence));
+ ASSERT_TRUE(writer.AddCopy(6, 3));
+ ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
+ ASSERT_TRUE(writer.AddCopy(3, 5));
+ ASSERT_TRUE(writer.AddCopy(2, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
+ ASSERT_TRUE(writer.AddCopy(5, 6));
+ ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
+ ASSERT_TRUE(writer.AddCopy(10, 2));
+ ASSERT_TRUE(writer.AddCopy(7, 4));
+ ASSERT_TRUE(writer.Finalize());
+
+ // New block in cow order is 6, 12, 8, 11, 3, 2, 4, 9, 5, 1, 10, 7
+ // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
+ // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
+ // new block 2 is "already merged", so will be left out.
+
+ std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ auto iter = reader.GetRevMergeOpIter();
+ auto expected_new_block = revMergeOpSequence.begin();
+
+ while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op.new_block, *expected_new_block);
+
+ iter->Next();
+ expected_new_block++;
+ }
+ ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, LegacyRevMergeOpItrTest) {
+ CowOptions options;
+ options.cluster_ops = 5;
+ options.num_merge_ops = 1;
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer.AddCopy(2, 1));
+ ASSERT_TRUE(writer.AddCopy(10, 2));
+ ASSERT_TRUE(writer.AddCopy(6, 3));
+ ASSERT_TRUE(writer.AddCopy(7, 4));
+ ASSERT_TRUE(writer.AddCopy(3, 5));
+ ASSERT_TRUE(writer.AddCopy(5, 6));
+ ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
+
+ ASSERT_TRUE(writer.Finalize());
+
+ // New block in cow order is 2, 10, 6, 7, 3, 5, 12, 8, 11, 4, 9, 1
+ // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
+ // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
+ // new block 2 is "already merged", so will be left out.
+
+ std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ auto iter = reader.GetRevMergeOpIter();
+ auto expected_new_block = revMergeOpSequence.begin();
+
+ while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op.new_block, *expected_new_block);
+
+ iter->Next();
+ expected_new_block++;
+ }
+ ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
+ ASSERT_TRUE(iter->Done());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 0753c49..3085f80 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -37,6 +37,8 @@
os << "kCowLabelOp, ";
else if (op.type == kCowClusterOp)
os << "kCowClusterOp ";
+ else if (op.type == kCowSequenceOp)
+ os << "kCowSequenceOp ";
else if (op.type == kCowFooterOp)
os << "kCowFooterOp ";
else
@@ -81,6 +83,16 @@
case kCowLabelOp:
case kCowClusterOp:
case kCowFooterOp:
+ case kCowSequenceOp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsOrderedOp(const CowOperation& op) {
+ switch (op.type) {
+ case kCowCopyOp:
return true;
default:
return false;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 2349e4a..ace6f59 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -19,6 +19,9 @@
#include <limits>
#include <optional>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include <android-base/file.h>
@@ -127,7 +130,10 @@
return false;
}
- return ParseOps(label);
+ if (!ParseOps(label)) {
+ return false;
+ }
+ return PrepMergeOps();
}
bool CowReader::ParseOps(std::optional<uint64_t> label) {
@@ -201,6 +207,8 @@
current_op_num--;
done = true;
break;
+ } else if (current_op.type == kCowSequenceOp) {
+ has_seq_ops_ = true;
}
}
@@ -251,7 +259,7 @@
LOG(ERROR) << "ops checksum does not match";
return false;
}
- SHA256(ops_buffer.get()->data(), footer_->op.ops_size, csum);
+ SHA256(ops_buffer->data(), footer_->op.ops_size, csum);
if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
LOG(ERROR) << "ops checksum does not match";
return false;
@@ -264,138 +272,173 @@
return true;
}
-void CowReader::InitializeMerge() {
- uint64_t num_copy_ops = 0;
+//
+// This sets up the data needed for MergeOpIter. MergeOpIter presents
+// data in the order we intend to merge in.
+//
+// We merge all order sensitive ops up front, and sort the rest to allow for
+// batch merging. Order sensitive ops can either be presented in their proper
+// order in the cow, or be ordered by sequence ops (kCowSequenceOp), in which
+// case we want to merge those ops first, followed by any ops not specified by
+// new_block value by the sequence op, in sorted order.
+// We will re-arrange the vector in such a way that
+// kernel can batch merge. Ex:
+//
+// Existing COW format; All the copy operations
+// are at the beginning.
+// =======================================
+// Copy-op-1 - cow_op->new_block = 1
+// Copy-op-2 - cow_op->new_block = 2
+// Copy-op-3 - cow_op->new_block = 3
+// Replace-op-4 - cow_op->new_block = 6
+// Replace-op-5 - cow_op->new_block = 4
+// Replace-op-6 - cow_op->new_block = 8
+// Replace-op-7 - cow_op->new_block = 9
+// Zero-op-8 - cow_op->new_block = 7
+// Zero-op-9 - cow_op->new_block = 5
+// =======================================
+//
+// First find the operation which isn't a copy-op
+// and then sort all the operations in descending order
+// with the key being cow_op->new_block (source block)
+//
+// The data-structure will look like:
+//
+// =======================================
+// Copy-op-1 - cow_op->new_block = 1
+// Copy-op-2 - cow_op->new_block = 2
+// Copy-op-3 - cow_op->new_block = 3
+// Replace-op-7 - cow_op->new_block = 9
+// Replace-op-6 - cow_op->new_block = 8
+// Zero-op-8 - cow_op->new_block = 7
+// Replace-op-4 - cow_op->new_block = 6
+// Zero-op-9 - cow_op->new_block = 5
+// Replace-op-5 - cow_op->new_block = 4
+// =======================================
+//
+// Daemon will read the above data-structure in reverse-order
+// when reading metadata. Thus, kernel will get the metadata
+// in the following order:
+//
+// ========================================
+// Replace-op-5 - cow_op->new_block = 4
+// Zero-op-9 - cow_op->new_block = 5
+// Replace-op-4 - cow_op->new_block = 6
+// Zero-op-8 - cow_op->new_block = 7
+// Replace-op-6 - cow_op->new_block = 8
+// Replace-op-7 - cow_op->new_block = 9
+// Copy-op-3 - cow_op->new_block = 3
+// Copy-op-2 - cow_op->new_block = 2
+// Copy-op-1 - cow_op->new_block = 1
+// ===========================================
+//
+// When merging begins, kernel will start from the last
+// metadata which was read: In the above format, Copy-op-1
+// will be the first merge operation.
+//
+// Now, batching of the merge operations happens only when
+// 1: origin block numbers in the base device are contiguous
+// (cow_op->new_block) and,
+// 2: cow block numbers which are assigned by daemon in ReadMetadata()
+// are contiguous. These are monotonically increasing numbers.
+//
+// When both (1) and (2) are true, kernel will batch merge the operations.
+// In the above case, we have to ensure that the copy operations
+// are merged first before replace operations are done. Hence,
+// we will not change the order of copy operations. Since,
+// cow_op->new_block numbers are contiguous, we will ensure that the
+// cow block numbers assigned in ReadMetadata() for these respective copy
+// operations are not contiguous forcing kernel to issue merge for each
+// copy operations without batch merging.
+//
+// For all the other operations viz. Replace and Zero op, the cow block
+// numbers assigned by daemon will be contiguous allowing kernel to batch
+// merge.
+//
+// The final format after assiging COW block numbers by the daemon will
+// look something like:
+//
+// =========================================================
+// Replace-op-5 - cow_op->new_block = 4 cow-block-num = 2
+// Zero-op-9 - cow_op->new_block = 5 cow-block-num = 3
+// Replace-op-4 - cow_op->new_block = 6 cow-block-num = 4
+// Zero-op-8 - cow_op->new_block = 7 cow-block-num = 5
+// Replace-op-6 - cow_op->new_block = 8 cow-block-num = 6
+// Replace-op-7 - cow_op->new_block = 9 cow-block-num = 7
+// Copy-op-3 - cow_op->new_block = 3 cow-block-num = 9
+// Copy-op-2 - cow_op->new_block = 2 cow-block-num = 11
+// Copy-op-1 - cow_op->new_block = 1 cow-block-num = 13
+// ==========================================================
+//
+// Merge sequence will look like:
+//
+// Merge-1 - Batch-merge { Copy-op-1, Copy-op-2, Copy-op-3 }
+// Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
+// Replace-op-4, Zero-op-9, Replace-op-5 }
+//==============================================================
+bool CowReader::PrepMergeOps() {
+ auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>();
+ std::set<int, std::greater<int>> other_ops;
+ auto seq_ops_set = std::unordered_set<uint32_t>();
+ auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>();
+ size_t num_seqs = 0;
+ size_t read;
- // Remove all the metadata operations
- ops_->erase(std::remove_if(ops_.get()->begin(), ops_.get()->end(),
- [](CowOperation& op) { return IsMetadataOp(op); }),
- ops_.get()->end());
-
- set_total_data_ops(ops_->size());
- // We will re-arrange the vector in such a way that
- // kernel can batch merge. Ex:
- //
- // Existing COW format; All the copy operations
- // are at the beginning.
- // =======================================
- // Copy-op-1 - cow_op->new_block = 1
- // Copy-op-2 - cow_op->new_block = 2
- // Copy-op-3 - cow_op->new_block = 3
- // Replace-op-4 - cow_op->new_block = 6
- // Replace-op-5 - cow_op->new_block = 4
- // Replace-op-6 - cow_op->new_block = 8
- // Replace-op-7 - cow_op->new_block = 9
- // Zero-op-8 - cow_op->new_block = 7
- // Zero-op-9 - cow_op->new_block = 5
- // =======================================
- //
- // First find the operation which isn't a copy-op
- // and then sort all the operations in descending order
- // with the key being cow_op->new_block (source block)
- //
- // The data-structure will look like:
- //
- // =======================================
- // Copy-op-1 - cow_op->new_block = 1
- // Copy-op-2 - cow_op->new_block = 2
- // Copy-op-3 - cow_op->new_block = 3
- // Replace-op-7 - cow_op->new_block = 9
- // Replace-op-6 - cow_op->new_block = 8
- // Zero-op-8 - cow_op->new_block = 7
- // Replace-op-4 - cow_op->new_block = 6
- // Zero-op-9 - cow_op->new_block = 5
- // Replace-op-5 - cow_op->new_block = 4
- // =======================================
- //
- // Daemon will read the above data-structure in reverse-order
- // when reading metadata. Thus, kernel will get the metadata
- // in the following order:
- //
- // ========================================
- // Replace-op-5 - cow_op->new_block = 4
- // Zero-op-9 - cow_op->new_block = 5
- // Replace-op-4 - cow_op->new_block = 6
- // Zero-op-8 - cow_op->new_block = 7
- // Replace-op-6 - cow_op->new_block = 8
- // Replace-op-7 - cow_op->new_block = 9
- // Copy-op-3 - cow_op->new_block = 3
- // Copy-op-2 - cow_op->new_block = 2
- // Copy-op-1 - cow_op->new_block = 1
- // ===========================================
- //
- // When merging begins, kernel will start from the last
- // metadata which was read: In the above format, Copy-op-1
- // will be the first merge operation.
- //
- // Now, batching of the merge operations happens only when
- // 1: origin block numbers in the base device are contiguous
- // (cow_op->new_block) and,
- // 2: cow block numbers which are assigned by daemon in ReadMetadata()
- // are contiguous. These are monotonically increasing numbers.
- //
- // When both (1) and (2) are true, kernel will batch merge the operations.
- // In the above case, we have to ensure that the copy operations
- // are merged first before replace operations are done. Hence,
- // we will not change the order of copy operations. Since,
- // cow_op->new_block numbers are contiguous, we will ensure that the
- // cow block numbers assigned in ReadMetadata() for these respective copy
- // operations are not contiguous forcing kernel to issue merge for each
- // copy operations without batch merging.
- //
- // For all the other operations viz. Replace and Zero op, the cow block
- // numbers assigned by daemon will be contiguous allowing kernel to batch
- // merge.
- //
- // The final format after assiging COW block numbers by the daemon will
- // look something like:
- //
- // =========================================================
- // Replace-op-5 - cow_op->new_block = 4 cow-block-num = 2
- // Zero-op-9 - cow_op->new_block = 5 cow-block-num = 3
- // Replace-op-4 - cow_op->new_block = 6 cow-block-num = 4
- // Zero-op-8 - cow_op->new_block = 7 cow-block-num = 5
- // Replace-op-6 - cow_op->new_block = 8 cow-block-num = 6
- // Replace-op-7 - cow_op->new_block = 9 cow-block-num = 7
- // Copy-op-3 - cow_op->new_block = 3 cow-block-num = 9
- // Copy-op-2 - cow_op->new_block = 2 cow-block-num = 11
- // Copy-op-1 - cow_op->new_block = 1 cow-block-num = 13
- // ==========================================================
- //
- // Merge sequence will look like:
- //
- // Merge-1 - Batch-merge { Copy-op-1, Copy-op-2, Copy-op-3 }
- // Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
- // Replace-op-4, Zero-op-9, Replace-op-5 }
- //==============================================================
-
- num_copy_ops = FindNumCopyops();
-
- std::sort(ops_.get()->begin() + num_copy_ops, ops_.get()->end(),
- [](CowOperation& op1, CowOperation& op2) -> bool {
- return op1.new_block > op2.new_block;
- });
-
- if (header_.num_merge_ops > 0) {
- ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
- }
-
- num_copy_ops = FindNumCopyops();
- set_copy_ops(num_copy_ops);
-}
-
-uint64_t CowReader::FindNumCopyops() {
- uint64_t num_copy_ops = 0;
-
- for (uint64_t i = 0; i < ops_->size(); i++) {
+ for (size_t i = 0; i < ops_->size(); i++) {
auto& current_op = ops_->data()[i];
- if (current_op.type != kCowCopyOp) {
- break;
+
+ if (current_op.type == kCowSequenceOp) {
+ size_t seq_len = current_op.data_length / sizeof(uint32_t);
+
+ merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
+ if (!GetRawBytes(current_op.source, &merge_op_blocks->data()[num_seqs],
+ current_op.data_length, &read)) {
+ PLOG(ERROR) << "Failed to read sequence op!";
+ return false;
+ }
+ for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
+ seq_ops_set.insert(merge_op_blocks->data()[j]);
+ }
+ num_seqs += seq_len;
}
- num_copy_ops += 1;
+
+ if (IsMetadataOp(current_op)) {
+ continue;
+ }
+
+ if (!has_seq_ops_ && IsOrderedOp(current_op)) {
+ merge_op_blocks->emplace_back(current_op.new_block);
+ } else if (seq_ops_set.count(current_op.new_block) == 0) {
+ other_ops.insert(current_op.new_block);
+ }
+ block_map->insert({current_op.new_block, i});
+ }
+ for (auto block : *merge_op_blocks) {
+ if (block_map->count(block) == 0) {
+ LOG(ERROR) << "Invalid Sequence Ops. Could not find Cow Op for new block " << block;
+ return false;
+ }
}
- return num_copy_ops;
+ if (merge_op_blocks->size() > header_.num_merge_ops) {
+ num_ordered_ops_to_merge_ = merge_op_blocks->size() - header_.num_merge_ops;
+ } else {
+ num_ordered_ops_to_merge_ = 0;
+ }
+ merge_op_blocks->reserve(merge_op_blocks->size() + other_ops.size());
+ for (auto block : other_ops) {
+ merge_op_blocks->emplace_back(block);
+ }
+
+ num_total_data_ops_ = merge_op_blocks->size();
+ if (header_.num_merge_ops > 0) {
+ merge_op_blocks->erase(merge_op_blocks->begin(),
+ merge_op_blocks->begin() + header_.num_merge_ops);
+ }
+
+ block_map_ = block_map;
+ merge_op_blocks_ = merge_op_blocks;
+ return true;
}
bool CowReader::GetHeader(CowHeader* header) {
@@ -430,11 +473,11 @@
CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops) {
ops_ = ops;
- op_iter_ = ops_.get()->begin();
+ op_iter_ = ops_->begin();
}
bool CowOpIter::Done() {
- return op_iter_ == ops_.get()->end();
+ return op_iter_ == ops_->end();
}
void CowOpIter::Next() {
@@ -447,9 +490,11 @@
return (*op_iter_);
}
-class CowOpReverseIter final : public ICowOpReverseIter {
+class CowRevMergeOpIter final : public ICowOpIter {
public:
- explicit CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops);
+ explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
+ std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
+ std::shared_ptr<std::unordered_map<uint32_t, int>> map);
bool Done() override;
const CowOperation& Get() override;
@@ -457,34 +502,41 @@
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
- std::vector<CowOperation>::reverse_iterator op_riter_;
+ std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
+ std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
+ std::vector<uint32_t>::reverse_iterator block_riter_;
};
-CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
+CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
+ std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
+ std::shared_ptr<std::unordered_map<uint32_t, int>> map) {
ops_ = ops;
- op_riter_ = ops_.get()->rbegin();
+ merge_op_blocks_ = merge_op_blocks;
+ map_ = map;
+
+ block_riter_ = merge_op_blocks->rbegin();
}
-bool CowOpReverseIter::Done() {
- return op_riter_ == ops_.get()->rend();
+bool CowRevMergeOpIter::Done() {
+ return block_riter_ == merge_op_blocks_->rend();
}
-void CowOpReverseIter::Next() {
+void CowRevMergeOpIter::Next() {
CHECK(!Done());
- op_riter_++;
+ block_riter_++;
}
-const CowOperation& CowOpReverseIter::Get() {
+const CowOperation& CowRevMergeOpIter::Get() {
CHECK(!Done());
- return (*op_riter_);
+ return ops_->data()[map_->at(*block_riter_)];
}
std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
return std::make_unique<CowOpIter>(ops_);
}
-std::unique_ptr<ICowOpReverseIter> CowReader::GetRevOpIter() {
- return std::make_unique<CowOpReverseIter>(ops_);
+std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter() {
+ return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_);
}
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 0a7ceab..ef30e32 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -58,8 +58,8 @@
return EmitRawBlocks(new_block_start, data, size);
}
-bool AddXorBlocks(uint32_t /*new_block_start*/, const void* /*data*/, size_t /*size*/,
- uint32_t /*old_block*/, uint16_t /*offset*/) {
+bool ICowWriter::AddXorBlocks(uint32_t /*new_block_start*/, const void* /*data*/, size_t /*size*/,
+ uint32_t /*old_block*/, uint16_t /*offset*/) {
LOG(ERROR) << "AddXorBlocks not yet implemented";
return false;
}
@@ -76,9 +76,8 @@
return EmitLabel(label);
}
-bool AddSequenceData(size_t /*num_ops*/, const uint32_t* /*data*/) {
- LOG(ERROR) << "AddSequenceData not yet implemented";
- return false;
+bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
+ return EmitSequenceData(num_ops, data);
}
bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
@@ -103,7 +102,7 @@
header_.footer_size = sizeof(CowFooter);
header_.op_size = sizeof(CowOperation);
header_.block_size = options_.block_size;
- header_.num_merge_ops = 0;
+ header_.num_merge_ops = options_.num_merge_ops;
header_.cluster_ops = options_.cluster_ops;
header_.buffer_size = 0;
footer_ = {};
@@ -337,6 +336,26 @@
return WriteOperation(op) && Sync();
}
+bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+ CHECK(!merge_in_progress_);
+ size_t to_add = 0;
+ size_t max_ops = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t);
+ while (num_ops > 0) {
+ CowOperation op = {};
+ op.type = kCowSequenceOp;
+ op.source = next_data_pos_;
+ to_add = std::min(num_ops, max_ops);
+ op.data_length = static_cast<uint16_t>(to_add * sizeof(uint32_t));
+ if (!WriteOperation(op, data, op.data_length)) {
+ PLOG(ERROR) << "AddSequenceData: write failed";
+ return false;
+ }
+ num_ops -= to_add;
+ data += to_add;
+ }
+ return true;
+}
+
bool CowWriter::EmitCluster() {
CowOperation op = {};
op.type = kCowClusterOp;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 000e5e1..464046b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -148,6 +148,7 @@
static constexpr uint8_t kCowZeroOp = 3;
static constexpr uint8_t kCowLabelOp = 4;
static constexpr uint8_t kCowClusterOp = 5;
+static constexpr uint8_t kCowSequenceOp = 7;
static constexpr uint8_t kCowFooterOp = -1;
static constexpr uint8_t kCowCompressNone = 0;
@@ -184,7 +185,10 @@
int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
+// Ops that are internal to the Cow Format and not OTA data
bool IsMetadataOp(const CowOperation& op);
+// Ops that have dependencies on old blocks, and must take care in their merge order
+bool IsOrderedOp(const CowOperation& op);
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 669e58a..6c3059c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -19,6 +19,7 @@
#include <functional>
#include <memory>
#include <optional>
+#include <unordered_map>
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
@@ -27,7 +28,6 @@
namespace snapshot {
class ICowOpIter;
-class ICowOpReverseIter;
// A ByteSink object handles requests for a buffer of a specific size. It
// always owns the underlying buffer. It's designed to minimize potential
@@ -75,8 +75,8 @@
// Return an iterator for retrieving CowOperation entries.
virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
- // Return an reverse iterator for retrieving CowOperation entries.
- virtual std::unique_ptr<ICowOpReverseIter> GetRevOpIter() = 0;
+ // Return an iterator for retrieving CowOperation entries in merge order
+ virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter() = 0;
// Get decoded bytes from the data section, handling any decompression.
// All retrieved data is passed to the sink.
@@ -98,21 +98,6 @@
virtual void Next() = 0;
};
-// Reverse Iterate over a sequence of COW operations.
-class ICowOpReverseIter {
- public:
- virtual ~ICowOpReverseIter() {}
-
- // True if there are more items to read, false otherwise.
- virtual bool Done() = 0;
-
- // Read the current operation.
- virtual const CowOperation& Get() = 0;
-
- // Advance to the next item.
- virtual void Next() = 0;
-};
-
class CowReader : public ICowReader {
public:
CowReader();
@@ -135,25 +120,25 @@
// whose lifetime depends on the CowOpIter object; the return
// value of these will never be null.
std::unique_ptr<ICowOpIter> GetOpIter() override;
- std::unique_ptr<ICowOpReverseIter> GetRevOpIter() override;
+ std::unique_ptr<ICowOpIter> GetRevMergeOpIter() override;
bool ReadData(const CowOperation& op, IByteSink* sink) override;
bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
- void InitializeMerge();
+ // Returns the total number of data ops that should be merged. This is the
+ // count of the merge sequence before removing already-merged operations.
+ // It may be different than the actual data op count, for example, if there
+ // are duplicate ops in the stream.
+ uint64_t get_num_total_data_ops() { return num_total_data_ops_; }
- // Number of copy, replace, and zero ops. Set if InitializeMerge is called.
- void set_total_data_ops(uint64_t size) { total_data_ops_ = size; }
- uint64_t total_data_ops() { return total_data_ops_; }
- // Number of copy ops. Set if InitializeMerge is called.
- void set_copy_ops(uint64_t size) { copy_ops_ = size; }
- uint64_t total_copy_ops() { return copy_ops_; }
+ uint64_t get_num_ordered_ops_to_merge() { return num_ordered_ops_to_merge_; }
void CloseCowFd() { owned_fd_ = {}; }
private:
bool ParseOps(std::optional<uint64_t> label);
+ bool PrepMergeOps();
uint64_t FindNumCopyops();
android::base::unique_fd owned_fd_;
@@ -163,8 +148,11 @@
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
std::shared_ptr<std::vector<CowOperation>> ops_;
- uint64_t total_data_ops_;
- uint64_t copy_ops_;
+ std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
+ std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
+ uint64_t num_total_data_ops_;
+ uint64_t num_ordered_ops_to_merge_;
+ bool has_seq_ops_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index fbe6461..4a807fb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -38,6 +38,9 @@
uint32_t cluster_ops = 200;
bool scratch_space = true;
+
+ // Preset the number of merged ops. Only useful for testing.
+ uint64_t num_merge_ops = 0;
};
// Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -85,6 +88,7 @@
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;
virtual bool EmitLabel(uint64_t label) = 0;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
bool ValidateNewBlock(uint64_t new_block);
@@ -120,6 +124,7 @@
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;
virtual bool EmitLabel(uint64_t label) override;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
private:
bool EmitCluster();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 94d5055..ec58cca 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -60,6 +60,7 @@
MOCK_METHOD(bool, Dump, (std::ostream & os), (override));
MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
+ MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
index 067f99c..3d384cc 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
@@ -35,13 +35,16 @@
MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));
+ MOCK_METHOD(void, set_source_build_fingerprint, (const std::string&), (override));
MOCK_METHOD(uint64_t, cow_file_size, (), (override));
MOCK_METHOD(uint64_t, total_cow_size_bytes, (), (override));
MOCK_METHOD(uint64_t, estimated_cow_size_bytes, (), (override));
MOCK_METHOD(uint32_t, boot_complete_time_ms, (), (override));
MOCK_METHOD(uint32_t, boot_complete_to_merge_start_time_ms, (), (override));
+ MOCK_METHOD(std::string, source_build_fingerprint, (), (override));
MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));
MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
+ MOCK_METHOD(bool, WriteState, (), (override));
using ISnapshotMergeStats::Result;
// Return nullptr if any failure.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
new file mode 100644
index 0000000..0457986
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -0,0 +1,52 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <gmock/gmock.h>
+#include <libsnapshot/snapshot_writer.h>
+
+namespace android::snapshot {
+
+class MockSnapshotWriter : public ISnapshotWriter {
+ public:
+ using FileDescriptor = ISnapshotWriter::FileDescriptor;
+
+ explicit MockSnapshotWriter(const CowOptions& options) : ISnapshotWriter(options) {}
+ MockSnapshotWriter() : ISnapshotWriter({}) {}
+
+ MOCK_METHOD(bool, Finalize, (), (override));
+
+ // Return number of bytes the cow image occupies on disk.
+ MOCK_METHOD(uint64_t, GetCowSize, (), (override));
+
+ // Returns true if AddCopy() operations are supported.
+ MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
+
+ MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
+ MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
+ MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
+
+ // Open the writer in write mode (no append).
+ MOCK_METHOD(bool, Initialize, (), (override));
+
+ // Open the writer in append mode, with the last label to resume
+ // from. See CowWriter::InitializeAppend.
+ MOCK_METHOD(bool, InitializeAppend, (uint64_t label), (override));
+
+ MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenReader, (), (override));
+};
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 65034f7..e60da31 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -38,7 +38,7 @@
#include <libsnapshot/auto_device.h>
#include <libsnapshot/return.h>
#include <libsnapshot/snapshot_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -177,6 +177,9 @@
// code. Otherwise, MergeFailureCode::Ok is returned.
virtual MergeFailureCode ReadMergeFailureCode() = 0;
+ // If an update is in progress, return the source build fingerprint.
+ virtual std::string ReadSourceBuildFingerprint() = 0;
+
// Find the status of the current update, if any.
//
// |progress| depends on the returned status:
@@ -369,6 +372,7 @@
ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
bool UnmapAllSnapshots() override;
+ std::string ReadSourceBuildFingerprint() override;
// We can't use WaitForFile during first-stage init, because ueventd is not
// running and therefore will not automatically create symlinks. Instead,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 4ce5077..8c2fec7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -35,12 +35,14 @@
virtual void set_boot_complete_time_ms(uint32_t ms) = 0;
virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
virtual void set_merge_failure_code(MergeFailureCode code) = 0;
+ virtual void set_source_build_fingerprint(const std::string& fingerprint) = 0;
virtual uint64_t cow_file_size() = 0;
virtual uint64_t total_cow_size_bytes() = 0;
virtual uint64_t estimated_cow_size_bytes() = 0;
virtual uint32_t boot_complete_time_ms() = 0;
virtual uint32_t boot_complete_to_merge_start_time_ms() = 0;
virtual MergeFailureCode merge_failure_code() = 0;
+ virtual std::string source_build_fingerprint() = 0;
// Called when merge ends. Properly clean up permanent storage.
class Result {
@@ -52,6 +54,10 @@
};
// Return nullptr if any failure.
virtual std::unique_ptr<Result> Finish() = 0;
+
+ // Write out the current state. This should be called when data might be lost that
+ // cannot be recovered (eg the COW sizes).
+ virtual bool WriteState() = 0;
};
class SnapshotMergeStats : public ISnapshotMergeStats {
@@ -74,11 +80,13 @@
uint32_t boot_complete_to_merge_start_time_ms() override;
void set_merge_failure_code(MergeFailureCode code) override;
MergeFailureCode merge_failure_code() override;
+ void set_source_build_fingerprint(const std::string& fingerprint) override;
+ std::string source_build_fingerprint() override;
std::unique_ptr<Result> Finish() override;
+ bool WriteState() override;
private:
bool ReadState();
- bool WriteState();
bool DeleteState();
SnapshotMergeStats(const std::string& path);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index a7cd939..74b78c5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -57,6 +57,7 @@
ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
bool UnmapAllSnapshots() override;
+ std::string ReadSourceBuildFingerprint() override;
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index bf5ce8b..c00dafa 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -76,6 +76,7 @@
bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
bool EmitLabel(uint64_t label) override;
+ bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
private:
android::base::unique_fd cow_device_;
@@ -103,6 +104,7 @@
bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
bool EmitLabel(uint64_t label) override;
+ bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
private:
android::base::unique_fd snapshot_fd_;
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 1dc61af..ed86c87 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -40,8 +40,18 @@
LOG(ERROR) << "\t -s Run Silent";
LOG(ERROR) << "\t -d Attempt to decompress";
LOG(ERROR) << "\t -b Show data for failed decompress\n";
+ LOG(ERROR) << "\t -m Show ops in reverse merge order\n";
}
+enum OpIter { Normal, RevMerge };
+
+struct Options {
+ bool silent;
+ bool decompress;
+ bool show_bad;
+ OpIter iter_type;
+};
+
// Sink that always appends to the end of a string.
class StringSink : public IByteSink {
public:
@@ -78,7 +88,7 @@
}
}
-static bool Inspect(const std::string& path, bool silent, bool decompress, bool show_bad) {
+static bool Inspect(const std::string& path, Options opt) {
android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
if (fd < 0) {
PLOG(ERROR) << "open failed: " << path;
@@ -100,7 +110,7 @@
bool has_footer = false;
if (reader.GetFooter(&footer)) has_footer = true;
- if (!silent) {
+ if (!opt.silent) {
std::cout << "Major version: " << header.major_version << "\n";
std::cout << "Minor version: " << header.minor_version << "\n";
std::cout << "Header size: " << header.header_size << "\n";
@@ -116,19 +126,24 @@
}
}
- auto iter = reader.GetOpIter();
+ std::unique_ptr<ICowOpIter> iter;
+ if (opt.iter_type == Normal) {
+ iter = reader.GetOpIter();
+ } else if (opt.iter_type == RevMerge) {
+ iter = reader.GetRevMergeOpIter();
+ }
StringSink sink;
bool success = true;
while (!iter->Done()) {
const CowOperation& op = iter->Get();
- if (!silent) std::cout << op << "\n";
+ if (!opt.silent) std::cout << op << "\n";
- if (decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
+ if (opt.decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
if (!reader.ReadData(op, &sink)) {
std::cerr << "Failed to decompress for :" << op << "\n";
success = false;
- if (show_bad) ShowBad(reader, op);
+ if (opt.show_bad) ShowBad(reader, op);
}
sink.Reset();
}
@@ -144,19 +159,24 @@
int main(int argc, char** argv) {
int ch;
- bool silent = false;
- bool decompress = false;
- bool show_bad = false;
- while ((ch = getopt(argc, argv, "sdb")) != -1) {
+ struct android::snapshot::Options opt;
+ opt.silent = false;
+ opt.decompress = false;
+ opt.show_bad = false;
+ opt.iter_type = android::snapshot::Normal;
+ while ((ch = getopt(argc, argv, "sdbm")) != -1) {
switch (ch) {
case 's':
- silent = true;
+ opt.silent = true;
break;
case 'd':
- decompress = true;
+ opt.decompress = true;
break;
case 'b':
- show_bad = true;
+ opt.show_bad = true;
+ break;
+ case 'm':
+ opt.iter_type = android::snapshot::RevMerge;
break;
default:
android::snapshot::usage();
@@ -169,7 +189,7 @@
return 1;
}
- if (!android::snapshot::Inspect(argv[optind], silent, decompress, show_bad)) {
+ if (!android::snapshot::Inspect(argv[optind], opt)) {
return 1;
}
return 0;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index be732ec..c3db32e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -716,7 +716,7 @@
}
}
- SnapshotUpdateStatus initial_status;
+ SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());
initial_status.set_state(UpdateState::Merging);
initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
initial_status.set_total_sectors(initial_target_values.total_sectors);
@@ -1193,11 +1193,7 @@
return MergeFailureCode::ParseCowConsistencyCheck;
}
- for (auto iter = reader.GetOpIter(); !iter->Done(); iter->Next()) {
- if (!IsMetadataOp(iter->Get())) {
- num_ops++;
- }
- }
+ num_ops = reader.get_num_total_data_ops();
}
// Second pass, try as hard as we can to get the actual number of blocks
@@ -2515,15 +2511,26 @@
SnapshotUpdateStatus status;
status.set_state(state);
- if (state == UpdateState::MergeFailed) {
- status.set_merge_failure_code(failure_code);
+ switch (state) {
+ case UpdateState::MergeFailed:
+ status.set_merge_failure_code(failure_code);
+ break;
+ case UpdateState::Initiated:
+ status.set_source_build_fingerprint(
+ android::base::GetProperty("ro.build.fingerprint", ""));
+ break;
+ default:
+ break;
}
// If we're transitioning between two valid states (eg, we're not beginning
- // or ending an OTA), then make sure to propagate the compression bit.
+ // or ending an OTA), then make sure to propagate the compression bit and
+ // build fingerprint.
if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
status.set_compression_enabled(old_status.compression_enabled());
+ status.set_source_build_fingerprint(old_status.source_build_fingerprint());
+ status.set_merge_phase(old_status.merge_phase());
}
return WriteSnapshotUpdateStatus(lock, status);
}
@@ -2838,7 +2845,7 @@
}
}
- SnapshotUpdateStatus status = {};
+ SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
status.set_state(update_state);
status.set_compression_enabled(cow_creator.compression_enabled);
if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
@@ -3264,9 +3271,10 @@
std::stringstream ss;
+ auto update_status = ReadSnapshotUpdateStatus(file.get());
+
ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
- ss << "Compression: " << ReadSnapshotUpdateStatus(file.get()).compression_enabled()
- << std::endl;
+ ss << "Compression: " << update_status.compression_enabled() << std::endl;
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
ss << "Rollback indicator: "
@@ -3275,6 +3283,7 @@
ss << "Forward merge indicator: "
<< (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
<< std::endl;
+ ss << "Source build fingerprint: " << update_status.source_build_fingerprint() << std::endl;
bool ok = true;
std::vector<std::string> snapshots;
@@ -3792,5 +3801,13 @@
return status.merge_failure_code();
}
+std::string SnapshotManager::ReadSourceBuildFingerprint() {
+ auto lock = LockExclusive();
+ if (!lock) return {};
+
+ SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
+ return status.source_build_fingerprint();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 4a93d65..712eafb 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -91,7 +91,6 @@
void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
report_.set_cow_file_size(cow_file_size);
- WriteState();
}
uint64_t SnapshotMergeStats::cow_file_size() {
@@ -138,6 +137,14 @@
return report_.merge_failure_code();
}
+void SnapshotMergeStats::set_source_build_fingerprint(const std::string& fingerprint) {
+ report_.set_source_build_fingerprint(fingerprint);
+}
+
+std::string SnapshotMergeStats::source_build_fingerprint() {
+ return report_.source_build_fingerprint();
+}
+
class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
public:
SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 1a9eda5..a8d5b8a 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -136,7 +136,10 @@
void set_boot_complete_to_merge_start_time_ms(uint32_t) override {}
uint32_t boot_complete_to_merge_start_time_ms() override { return 0; }
void set_merge_failure_code(MergeFailureCode) override {}
- MergeFailureCode merge_failure_code() { return MergeFailureCode::Ok; }
+ MergeFailureCode merge_failure_code() override { return MergeFailureCode::Ok; }
+ void set_source_build_fingerprint(const std::string&) override {}
+ std::string source_build_fingerprint() override { return {}; }
+ bool WriteState() override { return false; }
};
ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
@@ -170,4 +173,9 @@
return MergeFailureCode::Ok;
}
+std::string SnapshotManagerStub::ReadSourceBuildFingerprint() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return {};
+}
+
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6018643..b2203fe 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1184,6 +1184,53 @@
}
}
+TEST_F(SnapshotUpdateTest, DuplicateOps) {
+ if (!IsCompressionEnabled()) {
+ GTEST_SKIP() << "Compression-only test";
+ }
+
+ // OTA client blindly unmaps all partitions that are possibly mapped.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+ }
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name));
+ }
+
+ std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
+ for (auto* partition : partitions) {
+ AddOperation(partition);
+
+ std::unique_ptr<ISnapshotWriter> writer;
+ auto res = MapUpdateSnapshot(partition->partition_name() + "_b", &writer);
+ ASSERT_TRUE(res);
+ ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
+ ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
+ ASSERT_TRUE(writer->Finalize());
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = NewManagerForFirstStageMount("_b");
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ // Initiate the merge and wait for it to be completed.
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+}
+
// Test that shrinking and growing partitions at the same time is handled
// correctly in VABC.
TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 080f3b7..34b3e87 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -114,6 +114,10 @@
return cow_->AddLabel(label);
}
+bool CompressedSnapshotWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+ return cow_->AddSequenceData(num_ops, data);
+}
+
bool CompressedSnapshotWriter::Initialize() {
return cow_->Initialize(cow_device_);
}
@@ -183,6 +187,11 @@
return true;
}
+bool OnlineKernelSnapshotWriter::EmitSequenceData(size_t, const uint32_t*) {
+ // Not Needed
+ return true;
+}
+
std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
unique_fd fd(dup(snapshot_fd_.get()));
if (fd < 0) {
diff --git a/fs_mgr/libsnapshot/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd.rc
deleted file mode 100644
index 4bf34a2..0000000
--- a/fs_mgr/libsnapshot/snapuserd.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service snapuserd /system/bin/snapuserd
- socket snapuserd stream 0660 system system
- oneshot
- disabled
- user root
- group root system
- seclabel u:r:snapuserd:s0
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
new file mode 100644
index 0000000..bc97afc
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -0,0 +1,132 @@
+//
+// Copyright (C) 2018 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+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_defaults {
+ name: "snapuserd_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "snapuserd_server.cpp",
+ "snapuserd.cpp",
+ "snapuserd_daemon.cpp",
+ "snapuserd_worker.cpp",
+ "snapuserd_readahead.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror"
+ ],
+
+ static_libs: [
+ "libbase",
+ "libbrotli",
+ "libcutils_sockets",
+ "libdm",
+ "libfs_mgr",
+ "libgflags",
+ "liblog",
+ "libsnapshot_cow",
+ "libz",
+ ],
+}
+
+cc_binary {
+ name: "snapuserd",
+ defaults: ["snapuserd_defaults"],
+ init_rc: [
+ "snapuserd.rc",
+ ],
+ static_executable: true,
+ system_shared_libs: [],
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+}
+
+cc_test {
+ name: "cow_snapuserd_test",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "cow_snapuserd_test.cpp",
+ "snapuserd.cpp",
+ "snapuserd_worker.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libbrotli",
+ "libgtest",
+ "libsnapshot_cow",
+ "libsnapshot_snapuserd",
+ "libcutils_sockets",
+ "libz",
+ "libfs_mgr",
+ "libdm",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ "libfiemap_headers",
+ ],
+ test_min_api_level: 30,
+ auto_gen_config: true,
+ require_root: false,
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/OWNERS b/fs_mgr/libsnapshot/snapuserd/OWNERS
new file mode 100644
index 0000000..2df0a2d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/OWNERS
@@ -0,0 +1,3 @@
+akailash@google.com
+dvander@google.com
+drosen@google.com
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
similarity index 83%
rename from fs_mgr/libsnapshot/cow_snapuserd_test.cpp
rename to fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index 767cd04..a718328 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
@@ -33,7 +33,7 @@
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
#include <storage_literals/storage_literals.h>
#include "snapuserd.h"
@@ -96,6 +96,8 @@
class CowSnapuserdTest final {
public:
bool Setup();
+ bool SetupOrderedOps();
+ bool SetupOrderedOpsInverted();
bool SetupCopyOverlap_1();
bool SetupCopyOverlap_2();
bool Merge();
@@ -103,6 +105,8 @@
void ReadSnapshotDeviceAndValidate();
void Shutdown();
void MergeInterrupt();
+ void MergeInterruptFixed(int duration);
+ void MergeInterruptRandomly(int max_duration);
void ReadDmUserBlockWithoutDaemon();
std::string snapshot_dev() const { return snapshot_dev_->path(); }
@@ -117,6 +121,8 @@
void StartMerge();
void CreateCowDevice();
+ void CreateCowDeviceOrderedOps();
+ void CreateCowDeviceOrderedOpsInverted();
void CreateCowDeviceWithCopyOverlap_1();
void CreateCowDeviceWithCopyOverlap_2();
bool SetupDaemon();
@@ -197,6 +203,18 @@
return setup_ok_;
}
+bool CowSnapuserdTest::SetupOrderedOps() {
+ CreateBaseDevice();
+ CreateCowDeviceOrderedOps();
+ return SetupDaemon();
+}
+
+bool CowSnapuserdTest::SetupOrderedOpsInverted() {
+ CreateBaseDevice();
+ CreateCowDeviceOrderedOpsInverted();
+ return SetupDaemon();
+}
+
bool CowSnapuserdTest::SetupCopyOverlap_1() {
CreateBaseDevice();
CreateCowDeviceWithCopyOverlap_1();
@@ -383,6 +401,112 @@
true);
}
+void CowSnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+ // Fill random data
+ for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+ true);
+
+ offset += 1_MiB;
+ }
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+ size_t num_blocks = size_ / options.block_size;
+ size_t blk_end_copy = num_blocks * 2;
+ size_t source_blk = num_blocks - 1;
+ size_t blk_src_copy = blk_end_copy - 1;
+
+ size_t x = num_blocks;
+ while (1) {
+ ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ x -= 1;
+ if (x == 0) {
+ break;
+ }
+ source_blk -= 1;
+ blk_src_copy -= 1;
+ }
+
+ // Flush operations
+ ASSERT_TRUE(writer.Finalize());
+ // Construct the buffer required for validation
+ orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+ // Read the entire base device
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
+ true);
+ // Merged Buffer
+ memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
+}
+
+void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+ // Fill random data
+ for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+ true);
+
+ offset += 1_MiB;
+ }
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+ size_t num_blocks = size_ / options.block_size;
+ size_t x = num_blocks;
+ size_t source_blk = 0;
+ size_t blk_src_copy = num_blocks;
+
+ while (1) {
+ ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+
+ x -= 1;
+ if (x == 0) {
+ break;
+ }
+ source_blk += 1;
+ blk_src_copy += 1;
+ }
+
+ // Flush operations
+ ASSERT_TRUE(writer.Finalize());
+ // Construct the buffer required for validation
+ orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+ // Read the entire base device
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
+ true);
+ // Merged Buffer
+ memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
+}
+
void CowSnapuserdTest::CreateCowDevice() {
unique_fd rnd_fd;
loff_t offset = 0;
@@ -597,6 +721,7 @@
void CowSnapuserdTest::SimulateDaemonRestart() {
Shutdown();
+ std::this_thread::sleep_for(500ms);
SetDeviceControlName();
StartSnapuserdDaemon();
InitCowDevice();
@@ -605,6 +730,34 @@
CreateSnapshotDevice();
}
+void CowSnapuserdTest::MergeInterruptRandomly(int max_duration) {
+ std::srand(std::time(nullptr));
+ StartMerge();
+
+ for (int i = 0; i < 20; i++) {
+ int duration = std::rand() % max_duration;
+ std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+ SimulateDaemonRestart();
+ StartMerge();
+ }
+
+ SimulateDaemonRestart();
+ ASSERT_TRUE(Merge());
+}
+
+void CowSnapuserdTest::MergeInterruptFixed(int duration) {
+ StartMerge();
+
+ for (int i = 0; i < 25; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+ SimulateDaemonRestart();
+ StartMerge();
+ }
+
+ SimulateDaemonRestart();
+ ASSERT_TRUE(Merge());
+}
+
void CowSnapuserdTest::MergeInterrupt() {
// Interrupt merge at various intervals
StartMerge();
@@ -669,10 +822,9 @@
void* buffer = snapuserd_->GetExceptionBuffer(1);
loff_t offset = 0;
struct disk_exception* de;
- for (int i = 0; i < 12; i++) {
+ for (int i = 11; i >= 0; i--) {
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, i);
- ASSERT_EQ(de->new_chunk, new_chunk);
offset += sizeof(struct disk_exception);
new_chunk += 1;
}
@@ -811,71 +963,71 @@
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 100);
- ASSERT_EQ(de->new_chunk, 522);
+ ASSERT_EQ(de->new_chunk, 521);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 105);
- ASSERT_EQ(de->new_chunk, 524);
+ ASSERT_EQ(de->new_chunk, 522);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 110);
- ASSERT_EQ(de->new_chunk, 526);
+ ASSERT_EQ(de->new_chunk, 523);
offset += sizeof(struct disk_exception);
// The next 4 operations are batch merged as
// both old and new chunk are contiguous
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 50);
- ASSERT_EQ(de->new_chunk, 528);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 51);
- ASSERT_EQ(de->new_chunk, 529);
+ ASSERT_EQ(de->old_chunk, 53);
+ ASSERT_EQ(de->new_chunk, 524);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 52);
- ASSERT_EQ(de->new_chunk, 530);
+ ASSERT_EQ(de->new_chunk, 525);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 53);
- ASSERT_EQ(de->new_chunk, 531);
+ ASSERT_EQ(de->old_chunk, 51);
+ ASSERT_EQ(de->new_chunk, 526);
+ offset += sizeof(struct disk_exception);
+
+ de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
+ ASSERT_EQ(de->old_chunk, 50);
+ ASSERT_EQ(de->new_chunk, 527);
offset += sizeof(struct disk_exception);
// This is handling overlap operation with
// two batch merge operations.
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 18);
- ASSERT_EQ(de->new_chunk, 533);
+ ASSERT_EQ(de->new_chunk, 528);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 19);
- ASSERT_EQ(de->new_chunk, 534);
+ ASSERT_EQ(de->new_chunk, 529);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 20);
- ASSERT_EQ(de->new_chunk, 535);
+ ASSERT_EQ(de->new_chunk, 530);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 21);
- ASSERT_EQ(de->new_chunk, 537);
+ ASSERT_EQ(de->new_chunk, 532);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 22);
- ASSERT_EQ(de->new_chunk, 538);
+ ASSERT_EQ(de->new_chunk, 533);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 23);
- ASSERT_EQ(de->new_chunk, 539);
+ ASSERT_EQ(de->new_chunk, 534);
offset += sizeof(struct disk_exception);
// End of metadata
@@ -945,6 +1097,38 @@
harness.ReadDmUserBlockWithoutDaemon();
}
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOps());
+ harness.MergeInterruptFixed(300);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOps());
+ harness.MergeInterruptRandomly(500);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+ harness.MergeInterruptFixed(50);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+ harness.MergeInterruptRandomly(50);
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
similarity index 92%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 280e857..aeecf41 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -31,6 +31,7 @@
static constexpr uint32_t PACKET_SIZE = 512;
static constexpr char kSnapuserdSocket[] = "snapuserd";
+static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
// Ensure that the second-stage daemon for snapuserd is running.
bool EnsureSnapuserdStarted();
@@ -75,6 +76,9 @@
// snapuserd to gracefully exit once all handler threads have terminated.
// This should only be used on first-stage instances of snapuserd.
bool DetachSnapuserd();
+
+ // Returns true if the snapuserd instance supports bridging a socket to second-stage init.
+ bool SupportsSecondStageSocketHandoff();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
similarity index 100%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
similarity index 85%
rename from fs_mgr/libsnapshot/snapuserd.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
index 03c2ef6..31d0221 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
@@ -20,7 +20,7 @@
#include <optional>
#include <set>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
@@ -64,7 +64,7 @@
int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
if (ret < 0) {
- PLOG(ERROR) << "msync header failed: " << ret;
+ SNAP_PLOG(ERROR) << "msync header failed: " << ret;
return false;
}
@@ -266,14 +266,15 @@
void Snapuserd::CheckMergeCompletionStatus() {
if (!merge_initiated_) {
- SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->total_data_ops();
+ SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
+ << reader_->get_num_total_data_ops();
return;
}
struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
- << " Total-data-ops: " << reader_->total_data_ops();
+ << " Total-data-ops: " << reader_->get_num_total_data_ops();
}
/*
@@ -352,7 +353,6 @@
return false;
}
- reader_->InitializeMerge();
SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
if (!MmapMetadata()) {
@@ -361,7 +361,7 @@
}
// Initialize the iterator for reading metadata
- cowop_riter_ = reader_->GetRevOpIter();
+ std::unique_ptr<ICowOpIter> cowop_rm_iter = reader_->GetRevMergeOpIter();
exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
@@ -379,23 +379,18 @@
// this memset will ensure that metadata read is completed.
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- while (!cowop_riter_->Done()) {
- const CowOperation* cow_op = &cowop_riter_->Get();
+ while (!cowop_rm_iter->Done()) {
+ const CowOperation* cow_op = &cowop_rm_iter->Get();
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- if (IsMetadataOp(*cow_op)) {
- cowop_riter_->Next();
- continue;
- }
-
metadata_found = true;
// This loop will handle all the replace and zero ops.
// We will handle the copy ops later as it requires special
// handling of assigning chunk-id's. Furthermore, we make
// sure that replace/zero and copy ops are not batch merged; hence,
// the bump in the chunk_id before break of this loop
- if (cow_op->type == kCowCopyOp) {
+ if (IsOrderedOp(*cow_op)) {
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
break;
}
@@ -410,12 +405,11 @@
de->old_chunk = cow_op->new_block;
de->new_chunk = data_chunk_id;
-
// Store operation pointer.
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
num_ops += 1;
offset += sizeof(struct disk_exception);
- cowop_riter_->Next();
+ cowop_rm_iter->Next();
SNAP_LOG(DEBUG) << num_ops << ":"
<< " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
@@ -432,7 +426,7 @@
sizeof(struct disk_exception));
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- if (cowop_riter_->Done()) {
+ if (cowop_rm_iter->Done()) {
vec_.push_back(std::move(de_ptr));
}
}
@@ -442,21 +436,19 @@
int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
std::optional<chunk_t> prev_id = {};
- std::map<uint64_t, const CowOperation*> map;
+ std::vector<const CowOperation*> vec;
std::set<uint64_t> dest_blocks;
+ std::set<uint64_t> source_blocks;
size_t pending_copy_ops = exceptions_per_area_ - num_ops;
- uint64_t total_copy_ops = reader_->total_copy_ops();
+ uint64_t total_copy_ops = reader_->get_num_ordered_ops_to_merge();
SNAP_LOG(DEBUG) << " Processing copy-ops at Area: " << vec_.size()
<< " Number of replace/zero ops completed in this area: " << num_ops
<< " Pending copy ops for this area: " << pending_copy_ops;
- while (!cowop_riter_->Done()) {
+
+ while (!cowop_rm_iter->Done()) {
do {
- const CowOperation* cow_op = &cowop_riter_->Get();
- if (IsMetadataOp(*cow_op)) {
- cowop_riter_->Next();
- continue;
- }
+ const CowOperation* cow_op = &cowop_rm_iter->Get();
// We have two cases specific cases:
//
@@ -500,99 +492,45 @@
// scratch space and re-construct it thereby there
// is no loss of data.
//
+ // Note that we will follow the same order of COW operations
+ // as present in the COW file. This will make sure that
+ // the merge of operations are done based on the ops present
+ // in the file.
//===========================================================
- //
- // Case 2:
- //
- // Let's say we have three copy operations written to COW file
- // in the following order:
- //
- // op-1: 15 -> 18
- // op-2: 16 -> 19
- // op-3: 17 -> 20
- //
- // As aforementioned, kernel will initiate merge in reverse order.
- // Hence, we will read these ops in reverse order so that all these
- // ops are exectued in the same order as requested. Thus, we will
- // read the metadata in reverse order and for the kernel it will
- // look like:
- //
- // op-3: 17 -> 20
- // op-2: 16 -> 19
- // op-1: 15 -> 18 <-- Merge starts here in the kernel
- //
- // Now, this is problematic as kernel cannot batch merge them.
- //
- // Merge sequence will look like:
- //
- // Merge-1: op-1: 15 -> 18
- // Merge-2: op-2: 16 -> 19
- // Merge-3: op-3: 17 -> 20
- //
- // We have three merge operations.
- //
- // Even though the blocks are contiguous, kernel can batch merge
- // them if the blocks are in descending order. Update engine
- // addresses this issue partially for overlapping operations as
- // we see that op-1 to op-3 and op-4 to op-6 operatiosn are in
- // descending order. However, if the copy operations are not
- // overlapping, update engine cannot write these blocks
- // in descending order. Hence, we will try to address it.
- // Thus, we will send these blocks to the kernel and it will
- // look like:
- //
- // op-3: 15 -> 18
- // op-2: 16 -> 19
- // op-1: 17 -> 20 <-- Merge starts here in the kernel
- //
- // Now with this change, we can batch merge all these three
- // operations. Merge sequence will look like:
- //
- // Merge-1: {op-1: 17 -> 20, op-2: 16 -> 19, op-3: 15 -> 18}
- //
- // Note that we have changed the ordering of merge; However, this
- // is ok as each of these copy operations are independent and there
- // is no overlap.
- //
- //===================================================================
if (prev_id.has_value()) {
- chunk_t diff = (cow_op->new_block > prev_id.value())
- ? (cow_op->new_block - prev_id.value())
- : (prev_id.value() - cow_op->new_block);
- if (diff != 1) {
- break;
- }
-
- if (dest_blocks.count(cow_op->new_block) || map.count(cow_op->source) > 0) {
+ if (dest_blocks.count(cow_op->new_block) || source_blocks.count(cow_op->source)) {
break;
}
}
metadata_found = true;
pending_copy_ops -= 1;
- map[cow_op->new_block] = cow_op;
+ vec.push_back(cow_op);
dest_blocks.insert(cow_op->source);
+ source_blocks.insert(cow_op->new_block);
prev_id = cow_op->new_block;
- cowop_riter_->Next();
- } while (!cowop_riter_->Done() && pending_copy_ops);
+ cowop_rm_iter->Next();
+ } while (!cowop_rm_iter->Done() && pending_copy_ops);
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << map.size()
+ SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
<< " Area: " << vec_.size() << " Area offset: " << offset
<< " Pending-copy-ops in this area: " << pending_copy_ops;
- for (auto it = map.begin(); it != map.end(); it++) {
+ for (size_t i = 0; i < vec.size(); i++) {
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- de->old_chunk = it->first;
+ const CowOperation* cow_op = vec[i];
+
+ de->old_chunk = cow_op->new_block;
de->new_chunk = data_chunk_id;
// Store operation pointer.
- chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
+ chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
offset += sizeof(struct disk_exception);
num_ops += 1;
copy_ops++;
if (read_ahead_feature_) {
- read_ahead_ops_.push_back(it->second);
+ read_ahead_ops_.push_back(cow_op);
}
SNAP_LOG(DEBUG) << num_ops << ":"
@@ -611,7 +549,7 @@
sizeof(struct disk_exception));
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- if (cowop_riter_->Done()) {
+ if (cowop_rm_iter->Done()) {
vec_.push_back(std::move(de_ptr));
SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
}
@@ -635,8 +573,9 @@
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
}
}
- map.clear();
+ vec.clear();
dest_blocks.clear();
+ source_blocks.clear();
prev_id.reset();
}
@@ -661,7 +600,7 @@
<< " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
<< " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
<< " Num-ops-merged: " << header.num_merge_ops
- << " Total-data-ops: " << reader_->total_data_ops();
+ << " Total-data-ops: " << reader_->get_num_total_data_ops();
// Total number of sectors required for creating dm-user device
num_sectors_ = ChunkToSector(data_chunk_id);
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
similarity index 98%
rename from fs_mgr/libsnapshot/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.h
index 212c78e..95d2f77 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
@@ -41,7 +41,7 @@
#include <libdm/dm.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_kernel.h>
+#include <snapuserd/snapuserd_kernel.h>
namespace android {
namespace snapshot {
@@ -306,8 +306,6 @@
uint32_t exceptions_per_area_;
uint64_t num_sectors_;
- std::unique_ptr<ICowOpIter> cowop_iter_;
- std::unique_ptr<ICowOpReverseIter> cowop_riter_;
std::unique_ptr<CowReader> reader_;
// Vector of disk exception which is a
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
new file mode 100644
index 0000000..2750096
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
@@ -0,0 +1,19 @@
+service snapuserd /system/bin/snapuserd
+ socket snapuserd stream 0660 system system
+ oneshot
+ disabled
+ user root
+ group root system
+ seclabel u:r:snapuserd:s0
+
+service snapuserd_proxy /system/bin/snapuserd -socket-handoff
+ socket snapuserd stream 0660 system system
+ socket snapuserd_proxy seqpacket 0660 system root
+ oneshot
+ disabled
+ user root
+ group root system
+ seclabel u:r:snapuserd:s0
+
+on property:init.svc.snapuserd=stopped
+ setprop snapuserd.ready false
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
similarity index 88%
rename from fs_mgr/libsnapshot/snapuserd_client.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 41ab344..1ea05a3 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -33,7 +33,7 @@
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
@@ -42,13 +42,15 @@
using android::base::unique_fd;
bool EnsureSnapuserdStarted() {
- if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
- return true;
+ if (android::base::GetProperty("init.svc.snapuserd", "") != "running") {
+ android::base::SetProperty("ctl.start", "snapuserd");
+ if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
+ LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+ return false;
+ }
}
-
- android::base::SetProperty("ctl.start", "snapuserd");
- if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
- LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+ if (!android::base::WaitForProperty("snapuserd.ready", "true", 10s)) {
+ LOG(ERROR) << "Timed out waiting for snapuserd to be ready.";
return false;
}
return true;
@@ -141,6 +143,16 @@
return true;
}
+bool SnapuserdClient::SupportsSecondStageSocketHandoff() {
+ std::string msg = "supports,second_stage_socket_handoff";
+ if (!Sendmsg(msg)) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+ return false;
+ }
+ std::string response = Receivemsg();
+ return response == "success";
+}
+
std::string SnapuserdClient::Receivemsg() {
char msg[PACKET_SIZE];
ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
similarity index 82%
rename from fs_mgr/libsnapshot/snapuserd_daemon.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 7fa01b7..e05822e 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -19,13 +19,15 @@
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <gflags/gflags.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
#include "snapuserd_server.h"
DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
DEFINE_bool(no_socket, false,
"If true, no socket is used. Each additional argument is an INIT message.");
+DEFINE_bool(socket_handoff, false,
+ "If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
namespace android {
namespace snapshot {
@@ -33,8 +35,28 @@
bool Daemon::StartServer(int argc, char** argv) {
int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
+ sigfillset(&signal_mask_);
+ sigdelset(&signal_mask_, SIGINT);
+ sigdelset(&signal_mask_, SIGTERM);
+ sigdelset(&signal_mask_, SIGUSR1);
+
+ // Masking signals here ensure that after this point, we won't handle INT/TERM
+ // until after we call into ppoll()
+ signal(SIGINT, Daemon::SignalHandler);
+ signal(SIGTERM, Daemon::SignalHandler);
+ signal(SIGPIPE, Daemon::SignalHandler);
+ signal(SIGUSR1, Daemon::SignalHandler);
+
+ MaskAllSignalsExceptIntAndTerm();
+
+ if (FLAGS_socket_handoff) {
+ return server_.RunForSocketHandoff();
+ }
if (!FLAGS_no_socket) {
- return server_.Start(FLAGS_socket);
+ if (!server_.Start(FLAGS_socket)) {
+ return false;
+ }
+ return server_.Run();
}
for (int i = arg_start; i < argc; i++) {
@@ -51,8 +73,7 @@
// Skip the accept() call to avoid spurious log spam. The server will still
// run until all handlers have completed.
- server_.SetTerminating();
- return true;
+ return server_.WaitForSocket();
}
void Daemon::MaskAllSignalsExceptIntAndTerm() {
@@ -61,6 +82,7 @@
sigdelset(&signal_mask, SIGINT);
sigdelset(&signal_mask, SIGTERM);
sigdelset(&signal_mask, SIGPIPE);
+ sigdelset(&signal_mask, SIGUSR1);
if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
PLOG(ERROR) << "Failed to set sigprocmask";
}
@@ -74,28 +96,14 @@
}
}
-void Daemon::Run() {
- sigfillset(&signal_mask_);
- sigdelset(&signal_mask_, SIGINT);
- sigdelset(&signal_mask_, SIGTERM);
-
- // Masking signals here ensure that after this point, we won't handle INT/TERM
- // until after we call into ppoll()
- signal(SIGINT, Daemon::SignalHandler);
- signal(SIGTERM, Daemon::SignalHandler);
- signal(SIGPIPE, Daemon::SignalHandler);
-
- LOG(DEBUG) << "Snapuserd-server: ready to accept connections";
-
- MaskAllSignalsExceptIntAndTerm();
-
- server_.Run();
-}
-
void Daemon::Interrupt() {
server_.Interrupt();
}
+void Daemon::ReceivedSocketSignal() {
+ server_.ReceivedSocketSignal();
+}
+
void Daemon::SignalHandler(int signal) {
LOG(DEBUG) << "Snapuserd received signal: " << signal;
switch (signal) {
@@ -108,6 +116,11 @@
LOG(ERROR) << "Received SIGPIPE signal";
break;
}
+ case SIGUSR1: {
+ LOG(INFO) << "Received SIGUSR1, attaching to proxy socket";
+ Daemon::Instance().ReceivedSocketSignal();
+ break;
+ }
default:
LOG(ERROR) << "Received unknown signal " << signal;
break;
@@ -126,7 +139,5 @@
LOG(ERROR) << "Snapuserd daemon failed to start.";
exit(EXIT_FAILURE);
}
- daemon.Run();
-
return 0;
}
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
similarity index 97%
rename from fs_mgr/libsnapshot/snapuserd_daemon.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
index f8afac5..b660ba2 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
@@ -36,8 +36,8 @@
}
bool StartServer(int argc, char** argv);
- void Run();
void Interrupt();
+ void ReceivedSocketSignal();
private:
// Signal mask used with ppoll()
diff --git a/fs_mgr/libsnapshot/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
similarity index 99%
rename from fs_mgr/libsnapshot/snapuserd_readahead.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
index 16d5919..6fc26a6 100644
--- a/fs_mgr/libsnapshot/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
@@ -20,7 +20,7 @@
#include <optional>
#include <set>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
similarity index 72%
rename from fs_mgr/libsnapshot/snapuserd_server.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
index 8339690..672c13c 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
@@ -25,14 +25,26 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
-
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <fs_mgr/file_wait.h>
+#include <snapuserd/snapuserd_client.h>
#include "snapuserd.h"
#include "snapuserd_server.h"
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
namespace android {
namespace snapshot {
+using namespace std::string_literals;
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
if (input == "init") return DaemonOperations::INIT;
if (input == "start") return DaemonOperations::START;
@@ -40,6 +52,7 @@
if (input == "query") return DaemonOperations::QUERY;
if (input == "delete") return DaemonOperations::DELETE;
if (input == "detach") return DaemonOperations::DETACH;
+ if (input == "supports") return DaemonOperations::SUPPORTS;
return DaemonOperations::INVALID;
}
@@ -193,6 +206,16 @@
terminating_ = true;
return true;
}
+ case DaemonOperations::SUPPORTS: {
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
+ }
+ if (out[1] == "second_stage_socket_handoff") {
+ return Sendmsg(fd, "success");
+ }
+ return Sendmsg(fd, "fail");
+ }
default: {
LOG(ERROR) << "Received unknown message type from client";
Sendmsg(fd, "fail");
@@ -245,28 +268,44 @@
}
bool SnapuserdServer::Start(const std::string& socketname) {
+ bool start_listening = true;
+
sockfd_.reset(android_get_control_socket(socketname.c_str()));
- if (sockfd_ >= 0) {
- if (listen(sockfd_.get(), 4) < 0) {
- PLOG(ERROR) << "listen socket failed: " << socketname;
- return false;
- }
- } else {
+ if (sockfd_ < 0) {
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 false;
}
+ start_listening = false;
+ }
+ return StartWithSocket(start_listening);
+}
+
+bool SnapuserdServer::StartWithSocket(bool start_listening) {
+ if (start_listening && listen(sockfd_.get(), 4) < 0) {
+ PLOG(ERROR) << "listen socket failed";
+ return false;
}
- AddWatchedFd(sockfd_);
+ AddWatchedFd(sockfd_, POLLIN);
- LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
+ // If started in first-stage init, the property service won't be online.
+ if (access("/dev/socket/property_service", F_OK) == 0) {
+ if (!android::base::SetProperty("snapuserd.ready", "true")) {
+ LOG(ERROR) << "Failed to set snapuserd.ready property";
+ return false;
+ }
+ }
+
+ LOG(DEBUG) << "Snapuserd server now accepting connections";
return true;
}
bool SnapuserdServer::Run() {
+ LOG(INFO) << "Now listening on snapuserd socket";
+
while (!IsTerminating()) {
int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));
if (rv < 0) {
@@ -311,10 +350,10 @@
}
}
-void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
+void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
struct pollfd p = {};
p.fd = fd.get();
- p.events = POLLIN;
+ p.events = events;
watched_fds_.emplace_back(std::move(p));
}
@@ -325,7 +364,7 @@
return;
}
- AddWatchedFd(fd);
+ AddWatchedFd(fd, POLLIN);
}
bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
@@ -422,5 +461,97 @@
return true;
}
+bool SnapuserdServer::WaitForSocket() {
+ auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
+
+ auto socket_path = ANDROID_SOCKET_DIR "/"s + kSnapuserdSocketProxy;
+
+ if (!android::fs_mgr::WaitForFile(socket_path, std::chrono::milliseconds::max())) {
+ LOG(ERROR)
+ << "Failed to wait for proxy socket, second-stage snapuserd will fail to connect";
+ return false;
+ }
+
+ // We must re-initialize property service access, since we launched before
+ // second-stage init.
+ __system_properties_init();
+
+ if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
+ LOG(ERROR)
+ << "Failed to wait for proxy property, second-stage snapuserd will fail to connect";
+ return false;
+ }
+
+ unique_fd fd(socket_local_client(kSnapuserdSocketProxy, ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to connect to socket proxy";
+ return false;
+ }
+
+ char code[1];
+ std::vector<unique_fd> fds;
+ ssize_t rv = android::base::ReceiveFileDescriptorVector(fd, code, sizeof(code), 1, &fds);
+ if (rv < 0) {
+ PLOG(ERROR) << "Failed to receive server socket over proxy";
+ return false;
+ }
+ if (fds.empty()) {
+ LOG(ERROR) << "Expected at least one file descriptor from proxy";
+ return false;
+ }
+
+ // We don't care if the ACK is received.
+ code[0] = 'a';
+ if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+ PLOG(ERROR) << "Failed to send ACK to proxy";
+ return false;
+ }
+
+ sockfd_ = std::move(fds[0]);
+ if (!StartWithSocket(true)) {
+ return false;
+ }
+ return Run();
+}
+
+bool SnapuserdServer::RunForSocketHandoff() {
+ unique_fd proxy_fd(android_get_control_socket(kSnapuserdSocketProxy));
+ if (proxy_fd < 0) {
+ PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocketProxy;
+ }
+ borrowed_fd server_fd(android_get_control_socket(kSnapuserdSocket));
+ if (server_fd < 0) {
+ PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocket;
+ }
+
+ if (listen(proxy_fd.get(), 4) < 0) {
+ PLOG(FATAL) << "Proxy listen socket failed";
+ }
+
+ if (!android::base::SetProperty("snapuserd.proxy_ready", "true")) {
+ LOG(FATAL) << "Proxy failed to set ready property";
+ }
+
+ unique_fd client_fd(
+ TEMP_FAILURE_RETRY(accept4(proxy_fd.get(), nullptr, nullptr, SOCK_CLOEXEC)));
+ if (client_fd < 0) {
+ PLOG(FATAL) << "Proxy accept failed";
+ }
+
+ char code[1] = {'a'};
+ std::vector<int> fds = {server_fd.get()};
+ ssize_t rv = android::base::SendFileDescriptorVector(client_fd, code, sizeof(code), fds);
+ if (rv < 0) {
+ PLOG(FATAL) << "Proxy could not send file descriptor to snapuserd";
+ }
+ // Wait for an ACK - results don't matter, we just don't want to risk closing
+ // the proxy socket too early.
+ if (recv(client_fd, code, sizeof(code), 0) < 0) {
+ PLOG(FATAL) << "Proxy could not receive terminating code from snapuserd";
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
similarity index 92%
rename from fs_mgr/libsnapshot/snapuserd_server.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
index 6699189..846f848 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
@@ -42,6 +42,7 @@
STOP,
DELETE,
DETACH,
+ SUPPORTS,
INVALID,
};
@@ -93,6 +94,7 @@
private:
android::base::unique_fd sockfd_;
bool terminating_;
+ volatile bool received_socket_signal_ = false;
std::vector<struct pollfd> watched_fds_;
std::mutex lock_;
@@ -100,7 +102,7 @@
using HandlerList = std::vector<std::shared_ptr<DmUserHandler>>;
HandlerList dm_users_;
- void AddWatchedFd(android::base::borrowed_fd fd);
+ void AddWatchedFd(android::base::borrowed_fd fd, int events);
void AcceptClient();
bool HandleClient(android::base::borrowed_fd fd, int revents);
bool Recv(android::base::borrowed_fd fd, std::string* data);
@@ -117,6 +119,7 @@
void RunThread(std::shared_ptr<DmUserHandler> handler);
void JoinAllThreads();
+ bool StartWithSocket(bool start_listening);
// Find a DmUserHandler within a lock.
HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
@@ -129,6 +132,8 @@
bool Start(const std::string& socketname);
bool Run();
void Interrupt();
+ bool RunForSocketHandoff();
+ bool WaitForSocket();
std::shared_ptr<DmUserHandler> AddHandler(const std::string& misc_name,
const std::string& cow_device_path,
@@ -136,6 +141,7 @@
bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
void SetTerminating() { terminating_ = true; }
+ void ReceivedSocketSignal() { received_socket_signal_ = true; }
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
similarity index 99%
rename from fs_mgr/libsnapshot/snapuserd_worker.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index 682f9da..13d45fe 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
@@ -20,7 +20,7 @@
#include <optional>
#include <set>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index fd7ea04..42abd09 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -7,6 +7,7 @@
name: "libstorage_literals_headers",
host_supported: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["."],
target: {
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index a1b020a..94e1abb 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -127,7 +127,7 @@
"androidboot.serialno = \"BLAHBLAHBLAH\"\n"
"androidboot.slot_suffix = \"_a\"\n"
"androidboot.hardware.platform = \"sdw813\"\n"
- "hardware = \"foo\"\n"
+ "androidboot.hardware = \"foo\"\n"
"androidboot.revision = \"EVT1.0\"\n"
"androidboot.bootloader = \"burp-0.1-7521\"\n"
"androidboot.hardware.sku = \"mary\"\n"
@@ -159,7 +159,7 @@
{"androidboot.serialno", "BLAHBLAHBLAH"},
{"androidboot.slot_suffix", "_a"},
{"androidboot.hardware.platform", "sdw813"},
- {"hardware", "foo"},
+ {"androidboot.hardware", "foo"},
{"androidboot.revision", "EVT1.0"},
{"androidboot.bootloader", "burp-0.1-7521"},
{"androidboot.hardware.sku", "mary"},
diff --git a/gatekeeperd/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc
index 8b126d5..f572b11 100644
--- a/gatekeeperd/gatekeeperd.rc
+++ b/gatekeeperd/gatekeeperd.rc
@@ -1,4 +1,4 @@
service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
class late_start
user system
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
diff --git a/init/Android.bp b/init/Android.bp
index 3e8d4e3..a04d2db 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -228,17 +228,19 @@
stem: "init",
defaults: ["init_defaults"],
static_libs: ["libinit"],
- required: [
- "e2fsdroid",
- "init.rc",
- "mke2fs",
- "sload_f2fs",
- "make_f2fs",
- "ueventd.rc",
- ],
srcs: ["main.cpp"],
symlinks: ["ueventd"],
target: {
+ platform: {
+ required: [
+ "init.rc",
+ "ueventd.rc",
+ "e2fsdroid",
+ "make_f2fs",
+ "mke2fs",
+ "sload_f2fs",
+ ],
+ },
recovery: {
cflags: ["-DRECOVERY"],
exclude_static_libs: [
@@ -248,6 +250,14 @@
"libbinder",
"libutils",
],
+ required: [
+ "init_recovery.rc",
+ "ueventd.rc.recovery",
+ "e2fsdroid.recovery",
+ "make_f2fs.recovery",
+ "mke2fs.recovery",
+ "sload_f2fs.recovery",
+ ],
},
},
visibility: ["//packages/modules/Virtualization/microdroid"],
diff --git a/init/README.md b/init/README.md
index 4b04628..f447ab2 100644
--- a/init/README.md
+++ b/init/README.md
@@ -277,8 +277,6 @@
CLD_EXITED or an status other than '0', reboot the system with the target specified in
_target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
intended to be used with the `exec_start` builtin for any must-have checks during boot.
- A service being stopped by init (e.g. using the `stop` or `class_reset` commands) is not
- considered a failure for the purpose of this setting.
`restart_period <seconds>`
> If a non-oneshot service exits, it will be restarted at its start time plus
diff --git a/init/devices.cpp b/init/devices.cpp
index c510b23..d4a3cb9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -295,6 +295,8 @@
setfscreatecon(secontext.c_str());
}
+ gid_t new_group = -1;
+
dev_t dev = makedev(major, minor);
/* Temporarily change egid to avoid race condition setting the gid of the
* device node. Unforunately changing the euid would prevent creation of
@@ -322,10 +324,21 @@
PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
<< "' device";
}
+
+ struct stat s;
+ if (stat(path.c_str(), &s) == 0) {
+ if (gid != s.st_gid) {
+ new_group = gid;
+ }
+ } else {
+ PLOG(ERROR) << "Cannot stat " << path;
+ }
}
out:
- chown(path.c_str(), uid, -1);
+ if (chown(path.c_str(), uid, new_group) < 0) {
+ PLOG(ERROR) << "Cannot chown " << path << " " << uid << " " << new_group;
+ }
if (setegid(AID_ROOT)) {
PLOG(FATAL) << "setegid(AID_ROOT) failed";
}
diff --git a/init/init.cpp b/init/init.cpp
index a7325ca..bde8e04 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -725,6 +725,40 @@
}
}
+static Result<void> ConnectEarlyStageSnapuserdAction(const BuiltinArguments& args) {
+ auto pid = GetSnapuserdFirstStagePid();
+ if (!pid) {
+ return {};
+ }
+
+ auto info = GetSnapuserdFirstStageInfo();
+ if (auto iter = std::find(info.begin(), info.end(), "socket"s); iter == info.end()) {
+ // snapuserd does not support socket handoff, so exit early.
+ return {};
+ }
+
+ // Socket handoff is supported.
+ auto svc = ServiceList::GetInstance().FindService("snapuserd");
+ if (!svc) {
+ LOG(FATAL) << "Failed to find snapuserd service entry";
+ }
+
+ svc->SetShutdownCritical();
+ svc->SetStartedInFirstStage(*pid);
+
+ svc = ServiceList::GetInstance().FindService("snapuserd_proxy");
+ if (!svc) {
+ LOG(FATAL) << "Failed find snapuserd_proxy service entry, merge will never initiate";
+ }
+ if (!svc->MarkSocketPersistent("snapuserd")) {
+ LOG(FATAL) << "Could not find snapuserd socket in snapuserd_proxy service entry";
+ }
+ if (auto result = svc->Start(); !result.ok()) {
+ LOG(FATAL) << "Could not start snapuserd_proxy: " << result.error();
+ }
+ return {};
+}
+
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -852,6 +886,7 @@
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
+ am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
diff --git a/init/main.cpp b/init/main.cpp
index 23f5530..b01a3ee 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -25,9 +25,11 @@
#if __has_feature(address_sanitizer)
#include <sanitizer/asan_interface.h>
+#elif __has_feature(hwaddress_sanitizer)
+#include <sanitizer/hwasan_interface.h>
#endif
-#if __has_feature(address_sanitizer)
+#if __has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer)
// Load asan.options if it exists since these are not yet in the environment.
// Always ensure detect_container_overflow=0 as there are false positives with this check.
// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
@@ -51,6 +53,8 @@
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
+#elif __has_feature(hwaddress_sanitizer)
+ __hwasan_set_error_report_callback(AsanReportCallback);
#endif
// Boost prio which will be restored later
setpriority(PRIO_PROCESS, 0, -20);
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 46f8331..f0d8d45 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -90,12 +90,18 @@
auto mount_prop = entry.mount_point;
if (mount_prop == "/") mount_prop = "/root";
std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
- mount_prop = "dev.mnt.blk" + mount_prop;
+ auto blk_mount_prop = "dev.mnt.blk" + mount_prop;
+ auto dev_mount_prop = "dev.mnt.dev" + mount_prop;
// Set property even if its value does not change to trigger 'on property:'
// handling, except for clearing non-existent or already clear property.
// Goal is reduction of empty properties and associated triggers.
- if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
- android::base::SetProperty(mount_prop, value);
+ if (value.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
+ android::base::SetProperty(blk_mount_prop, value);
+ if (!value.empty()) {
+ android::base::SetProperty(dev_mount_prop, entry.blk_device.substr(strlen(devblock)));
+ } else {
+ android::base::SetProperty(dev_mount_prop, "");
+ }
}
} // namespace
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 15252a6..575cae9 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -82,6 +82,21 @@
return updatable;
}
+static bool IsMicrodroid() {
+ static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
+ return is_microdroid;
+}
+
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+static bool NeedsTwoMountNamespaces() {
+ if (!IsApexUpdatable()) return false;
+ if (IsRecoveryMode()) return false;
+ // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+ if (IsMicrodroid()) return false;
+ return true;
+}
+
#ifdef ACTIVATE_FLATTENED_APEX
static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
@@ -158,7 +173,8 @@
auto on_activate = [&](const std::string& apex_path,
const apex::proto::ApexManifest& apex_manifest) {
apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
- apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true);
+ apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true,
+ /* lastUpdateMillis= */ 0);
};
for (const auto& dir : kBuiltinDirsForApexes) {
@@ -259,7 +275,7 @@
// number of essential APEXes (e.g. com.android.runtime) are activated.
// In the namespace for post-apexd processes, all APEXes are activated.
bool success = true;
- if (IsApexUpdatable() && !IsRecoveryMode()) {
+ if (NeedsTwoMountNamespaces()) {
// Creating a new namespace by cloning, saving, and switching back to
// the original namespace.
if (unshare(CLONE_NEWNS) == -1) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ff9da42..2d67bf5 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1238,21 +1238,11 @@
});
}
-// bootconfig does not allow to populate `key=value` simultaneously with
-// `key.subkey=value` which does not work with the existing code for
-// `hardware` (e.g. we want both `ro.boot.hardware=value` and
-// `ro.boot.hardware.sku=value`) and for `qemu` (Android Stidio Emulator
-// specific).
-static bool IsAllowedBootconfigKey(const std::string_view key) {
- return (key == "hardware"sv) || (key == "qemu"sv);
-}
static void ProcessBootconfig() {
ImportBootconfig([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
- } else if (IsAllowedBootconfigKey(key)) {
- InitPropertySet("ro.boot." + key, value);
}
});
}
diff --git a/init/service.cpp b/init/service.cpp
index 5af81bf..489dd67 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -194,8 +194,6 @@
<< ") process group...";
int max_processes = 0;
int r;
-
- flags_ |= SVC_STOPPING;
if (signal == SIGTERM) {
r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
} else {
@@ -271,6 +269,9 @@
// Remove any socket resources we may have created.
for (const auto& socket : sockets_) {
+ if (socket.persist) {
+ continue;
+ }
auto path = ANDROID_SOCKET_DIR "/" + socket.name;
unlink(path.c_str());
}
@@ -279,8 +280,7 @@
f(siginfo);
}
- if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_ &&
- !(flags_ & SVC_STOPPING)) {
+ if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
trigger_shutdown(*on_failure_reboot_target_);
}
@@ -290,7 +290,7 @@
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
- flags_ &= ~(SVC_RUNNING | SVC_STOPPING);
+ flags_ &= (~SVC_RUNNING);
start_order_ = 0;
// Oneshot processes go into the disabled state on exit,
@@ -412,10 +412,7 @@
}
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
- // Starting a service removes it from the disabled or reset state and
- // immediately takes it out of the restarting state if it was in there.
- flags_ &= (~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START |
- SVC_STOPPING));
+ ResetFlagsForStart();
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
@@ -626,6 +623,23 @@
return {};
}
+void Service::SetStartedInFirstStage(pid_t pid) {
+ LOG(INFO) << "adding first-stage service '" << name_ << "'...";
+
+ time_started_ = boot_clock::now(); // not accurate, but doesn't matter here
+ pid_ = pid;
+ flags_ |= SVC_RUNNING;
+ start_order_ = next_start_order_++;
+
+ NotifyStateChange("running");
+}
+
+void Service::ResetFlagsForStart() {
+ // Starting a service removes it from the disabled or reset state and
+ // immediately takes it out of the restarting state if it was in there.
+ flags_ &= ~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START);
+}
+
Result<void> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
@@ -796,5 +810,18 @@
nullptr, str_args, false);
}
+// This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
+// a special case to support the daemon launched in first-stage init. The persist
+// feature is not part of the init language and is only used here.
+bool Service::MarkSocketPersistent(const std::string& socket_name) {
+ for (auto& socket : sockets_) {
+ if (socket.name == socket_name) {
+ socket.persist = true;
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace init
} // namespace android
diff --git a/init/service.h b/init/service.h
index 89b1f09..ccf6899 100644
--- a/init/service.h
+++ b/init/service.h
@@ -54,7 +54,6 @@
// should not be killed during shutdown
#define SVC_TEMPORARY 0x1000 // This service was started by 'exec' and should be removed from the
// service list once it is reaped.
-#define SVC_STOPPING 0x2000 // service is being stopped by init
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
@@ -100,6 +99,8 @@
void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
reap_callbacks_.emplace_back(std::move(callback));
}
+ void SetStartedInFirstStage(pid_t pid);
+ bool MarkSocketPersistent(const std::string& socket_name);
size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
static bool is_exec_service_running() { return is_exec_service_running_; }
@@ -145,6 +146,7 @@
void StopOrReset(int how);
void KillProcessGroup(int signal, bool report_oneshot = false);
void SetProcessAttributesAndCaps();
+ void ResetFlagsForStart();
static unsigned long next_start_order_;
static bool is_exec_service_running_;
diff --git a/init/service_utils.h b/init/service_utils.h
index 1e0b4bd..9b65dca 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -54,6 +54,7 @@
int perm = 0;
std::string context;
bool passcred = false;
+ bool persist = false;
// Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
// It should be called when starting a service, before calling fork(), such that the socket is
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 40467b7..b8c2fd2 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -33,10 +33,10 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapuserd_client.h>
#include <private/android_filesystem_config.h>
#include <procinfo/process_map.h>
#include <selinux/android.h>
+#include <snapuserd/snapuserd_client.h>
#include "block_dev_initializer.h"
#include "service_utils.h"
@@ -54,6 +54,7 @@
static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
+static constexpr char kSnapuserdFirstStageInfoVar[] = "FIRST_STAGE_SNAPUSERD_INFO";
static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
@@ -87,6 +88,14 @@
_exit(127);
}
+ auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 10s);
+ if (!client) {
+ LOG(FATAL) << "Could not connect to first-stage snapuserd";
+ }
+ if (client->SupportsSecondStageSocketHandoff()) {
+ setenv(kSnapuserdFirstStageInfoVar, "socket", 1);
+ }
+
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
@@ -328,5 +337,13 @@
return GetSnapuserdFirstStagePid().has_value();
}
+std::vector<std::string> GetSnapuserdFirstStageInfo() {
+ const char* pid_str = getenv(kSnapuserdFirstStageInfoVar);
+ if (!pid_str) {
+ return {};
+ }
+ return android::base::Split(pid_str, ",");
+}
+
} // namespace init
} // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index a5ab652..62aee83 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -76,6 +76,9 @@
// Return the pid of the first-stage instances of snapuserd, if it was started.
std::optional<pid_t> GetSnapuserdFirstStagePid();
+// Return snapuserd info strings that were set during first-stage init.
+std::vector<std::string> GetSnapuserdFirstStageInfo();
+
// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
// used to later execveat() snapuserd.
void SaveRamdiskPathToSnapuserd();
diff --git a/init/util.cpp b/init/util.cpp
index a40d104..9f7bfdb 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -253,8 +253,10 @@
for (const auto& entry : android::base::Split(bootconfig, "\n")) {
std::vector<std::string> pieces = android::base::Split(entry, "=");
if (pieces.size() == 2) {
- pieces[1].erase(std::remove(pieces[1].begin(), pieces[1].end(), '"'), pieces[1].end());
- fn(android::base::Trim(pieces[0]), android::base::Trim(pieces[1]));
+ // get rid of the extra space between a list of values and remove the quotes.
+ std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
+ value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+ fn(android::base::Trim(pieces[0]), android::base::Trim(value));
}
}
}
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index c8a183b..a9f6958 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -22,6 +22,7 @@
name: "libcrypto_utils",
vendor_available: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
vndk: {
enabled: true,
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index ef426ff..24c6ae6 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,8 +75,7 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_SYSPROP (1<<27)
-#define ATRACE_TAG_LAST ATRACE_TAG_SYSPROP
+#define ATRACE_TAG_LAST ATRACE_TAG_RRO
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index a940b8c..3af07b4 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -5,10 +5,8 @@
license {
name: "system_core_libkeyutils_license",
visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-BSD",
- ],
- // large-scale-change unable to identify any license_text files
+ license_kinds: ["SPDX-license-identifier-BSD"],
+ license_text: ["NOTICE"],
}
cc_library {
@@ -16,6 +14,7 @@
cflags: ["-Werror"],
defaults: ["linux_bionic_supported"],
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["include/"],
local_include_dirs: ["include/"],
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 815d2bb..c824376 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -146,12 +146,6 @@
std::this_thread::sleep_for(5ms);
}
- // With the exception of boot or shutdown, system uid_ folders are always populated. Spinning
- // here would needlessly delay most pid removals. Additionally, once empty a uid_ cgroup won't
- // have processes hanging on it (we've already spun for all its pid_), so there's no need to
- // spin anyway.
- rmdir(uid_path.c_str());
-
return ret;
}
@@ -230,7 +224,11 @@
* transferred for the user/group passed as uid/gid before system_server can properly access them.
*/
static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
- if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
+ if (mkdir(path.c_str(), mode) == -1) {
+ if (errno == EEXIST) {
+ // Directory already exists and permissions have been set at the time it was created
+ return true;
+ }
return false;
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b5fa475..449a505 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -584,7 +584,7 @@
}
}
]
- },
+ }
],
"AggregateProfiles": [
@@ -635,6 +635,10 @@
{
"Name": "CPUSET_SP_RESTRICTED",
"Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "Dex2OatBootComplete",
+ "Profiles": [ "SCHED_SP_BACKGROUND" ]
}
]
}
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index faf90c2..c42cada 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -96,6 +96,12 @@
acquire();
}
+String16::String16(String16&& o) noexcept
+ : mString(o.mString)
+{
+ o.mString = getEmptyString();
+}
+
String16::String16(const String16& o, size_t len, size_t begin)
: mString(getEmptyString())
{
@@ -126,6 +132,13 @@
release();
}
+String16& String16::operator=(String16&& other) noexcept {
+ release();
+ mString = other.mString;
+ other.mString = getEmptyString();
+ return *this;
+}
+
size_t String16::size() const
{
if (isStaticString()) {
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index 54662ac..7d7230e 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -58,12 +58,27 @@
EXPECT_STR16EQ(u"Verify me", another);
}
+TEST(String16Test, CopyAssign) {
+ String16 tmp("Verify me");
+ String16 another;
+ another = tmp;
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
TEST(String16Test, Move) {
String16 tmp("Verify me");
String16 another(std::move(tmp));
EXPECT_STR16EQ(u"Verify me", another);
}
+TEST(String16Test, MoveAssign) {
+ String16 tmp("Verify me");
+ String16 another;
+ another = std::move(tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
TEST(String16Test, Size) {
String16 tmp("Verify me");
EXPECT_EQ(9U, tmp.size());
@@ -174,10 +189,22 @@
EXPECT_STR16EQ(u"Verify me", another);
}
-TEST(String16Test, StringMoveFromStaticString) {
+TEST(String16Test, StringCopyAssignFromStaticString) {
StaticString16 tmp(u"Verify me");
- String16 another(std::move(tmp));
+ String16 another(u"nonstatic");
+ another = tmp;
EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_TRUE(another.isStaticString());
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_TRUE(tmp.isStaticString());
+}
+
+TEST(String16Test, StringMoveAssignFromStaticString) {
+ StaticString16 tmp(u"Verify me");
+ String16 another(u"nonstatic");
+ another = std::move(tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_TRUE(another.isStaticString());
}
TEST(String16Test, EmptyStringIsStatic) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 195e122..8511da9 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -429,24 +429,17 @@
// ---------------------------------------------------------------------------
// Path functions
-void String8::setPathName(const char* name)
-{
- setPathName(name, strlen(name));
-}
-
-void String8::setPathName(const char* name, size_t len)
-{
- char* buf = lockBuffer(len);
+static void setPathName(String8& s, const char* name) {
+ size_t len = strlen(name);
+ char* buf = s.lockBuffer(len);
memcpy(buf, name, len);
// remove trailing path separator, if present
- if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
- len--;
-
+ if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
buf[len] = '\0';
- unlockBuffer(len);
+ s.unlockBuffer(len);
}
String8 String8::getPathLeaf(void) const
@@ -559,7 +552,7 @@
size_t len = length();
if (len == 0) {
// no existing filename, just use the new one
- setPathName(name);
+ setPathName(*this, name);
return *this;
}
@@ -579,7 +572,7 @@
return *this;
} else {
- setPathName(name);
+ setPathName(*this, name);
return *this;
}
}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index a45d675..faf49b6 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -91,10 +91,6 @@
},
[](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());
},
};
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 60d523a..3ef56a3 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -41,6 +41,7 @@
public:
String16();
String16(const String16& o);
+ String16(String16&& o) noexcept;
String16(const String16& o,
size_t len,
size_t begin=0);
@@ -69,6 +70,7 @@
status_t append(const char16_t* other, size_t len);
inline String16& operator=(const String16& other);
+ String16& operator=(String16&& other) noexcept;
inline String16& operator+=(const String16& other);
inline String16 operator+(const String16& other) const;
@@ -172,10 +174,6 @@
template <size_t N>
explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
-
-public:
- template <size_t N>
- explicit constexpr String16(const StaticString16<N>& s) : mString(s.mString) {}
};
// String16 can be trivially moved using memcpy() because moving does not
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index cee5dc6..8b2dcf9 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -137,14 +137,6 @@
*/
/*
- * Set the filename field to a specific value.
- *
- * Normalizes the filename, removing a trailing '/' if present.
- */
- void setPathName(const char* name);
- void setPathName(const char* name, size_t numChars);
-
- /*
* Get just the filename component.
*
* "/tmp/foo/bar.c" --> "bar.c"
diff --git a/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc
index 4b11b1c..8697e9a 100644
--- a/llkd/llkd-debuggable.rc
+++ b/llkd/llkd-debuggable.rc
@@ -16,4 +16,4 @@
capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE SYS_ADMIN
file /dev/kmsg w
file /proc/sysrq-trigger w
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
diff --git a/llkd/llkd.rc b/llkd/llkd.rc
index b1f96a8..5d701fc 100644
--- a/llkd/llkd.rc
+++ b/llkd/llkd.rc
@@ -42,4 +42,4 @@
capabilities KILL IPC_LOCK
file /dev/kmsg w
file /proc/sysrq-trigger w
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7ba476b..a7322fa 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -191,33 +191,6 @@
chown system system /dev/cpuctl/camera-daemon/tasks
chmod 0664 /dev/cpuctl/camera-daemon/tasks
- # Android only use global RT throttling and doesn't use CONFIG_RT_GROUP_SCHED
- # for RT group throttling. These values here are just to make sure RT threads
- # can be migrated to those groups. These settings can be removed once we migrate
- # to GKI kernel.
- write /dev/cpuctl/cpu.rt_period_us 1000000
- write /dev/cpuctl/cpu.rt_runtime_us 950000
- # Surfaceflinger is in FG group so giving it a bit more
- write /dev/cpuctl/foreground/cpu.rt_runtime_us 450000
- write /dev/cpuctl/foreground/cpu.rt_period_us 1000000
- write /dev/cpuctl/background/cpu.rt_runtime_us 50000
- write /dev/cpuctl/background/cpu.rt_period_us 1000000
- write /dev/cpuctl/top-app/cpu.rt_runtime_us 100000
- write /dev/cpuctl/top-app/cpu.rt_period_us 1000000
- write /dev/cpuctl/rt/cpu.rt_runtime_us 100000
- write /dev/cpuctl/rt/cpu.rt_period_us 1000000
- write /dev/cpuctl/system/cpu.rt_runtime_us 100000
- write /dev/cpuctl/system/cpu.rt_period_us 1000000
- write /dev/cpuctl/system-background/cpu.rt_runtime_us 50000
- write /dev/cpuctl/system-background/cpu.rt_period_us 1000000
- write /dev/cpuctl/nnapi-hal/cpu.rt_runtime_us 50000
- write /dev/cpuctl/nnapi-hal/cpu.rt_period_us 1000000
- write /dev/cpuctl/camera-daemon/cpu.rt_runtime_us 50000
- write /dev/cpuctl/camera-daemon/cpu.rt_period_us 1000000
-
- # Migrate root group to system subgroup
- copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
-
# Create an stune group for camera-specific processes
mkdir /dev/stune/camera-daemon
chown system system /dev/stune/camera-daemon
@@ -465,6 +438,7 @@
# Start logd before any other services run to ensure we capture all of their logs.
start logd
# Start lmkd before any other services run so that it can register them
+ write /proc/sys/vm/watermark_boost_factor 0
chown root system /sys/module/lowmemorykiller/parameters/adj
chmod 0664 /sys/module/lowmemorykiller/parameters/adj
chown root system /sys/module/lowmemorykiller/parameters/minfree
@@ -776,8 +750,7 @@
mkdir /data/misc/odrefresh 0777 system system
# directory used for on-device signing key blob
mkdir /data/misc/odsign 0700 root root
- # Directory for VirtualizationService temporary image files. Ensure that it is empty.
- exec -- /bin/rm -rf /data/misc/virtualizationservice
+ # Directory for VirtualizationService temporary image files.
mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
mkdir /data/preloads 0775 system system encryption=None
@@ -1030,11 +1003,11 @@
# to access F2FS sysfs on dm-<num> directly
mkdir /dev/sys/fs/by-name 0755 system system
- symlink /sys/fs/f2fs/${dev.mnt.blk.data} /dev/sys/fs/by-name/userdata
+ symlink /sys/fs/f2fs/${dev.mnt.dev.data} /dev/sys/fs/by-name/userdata
# to access dm-<num> sysfs
mkdir /dev/sys/block/by-name 0755 system system
- symlink /sys/devices/virtual/block/${dev.mnt.blk.data} /dev/sys/block/by-name/userdata
+ symlink /sys/devices/virtual/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
# F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
# to avoid power consumption when system becomes mostly idle. Be careful
@@ -1281,7 +1254,3 @@
on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
setprop sys.init.userspace_reboot.in_progress ""
-
-# Migrate tasks again in case kernel threads are created during boot
-on property:sys.boot_completed=1
- copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 9469a48..0090841 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -12,5 +12,5 @@
onrestart restart media
onrestart restart netd
onrestart restart wificond
- writepid /dev/cpuset/foreground/tasks
+ task_profiles ProcessCapacityHigh
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 98dc088..63772bd 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -12,5 +12,5 @@
onrestart restart media
onrestart restart netd
onrestart restart wificond
- writepid /dev/cpuset/foreground/tasks
+ task_profiles ProcessCapacityHigh
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index b7d7490..97e8d8e 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -18,9 +18,11 @@
"awk",
"bc",
"bzip2",
+ "fsck.exfat",
"ldd",
"logwrapper",
"mini-keyctl",
+ "mkfs.exfat",
"mkshrc",
"newfs_msdos",
"reboot",
diff --git a/storaged/Android.bp b/storaged/Android.bp
index ec27a08..9d5cb48 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -30,7 +30,6 @@
"libhidlbase",
"liblog",
"libprotobuf-cpp-lite",
- "libsysutils",
"libutils",
"libz",
],
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index 0614fad..7085743 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -3,6 +3,6 @@
capabilities DAC_READ_SEARCH
priority 10
file /d/mmc0/mmc0:0001/ext_csd r
- writepid /dev/cpuset/system-background/tasks
+ task_profiles ServiceCapacityLow
user root
group package_info
diff --git a/trusty/confirmationui/fuzz/msg_fuzzer.cpp b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
index 8e4443c..ee55f82 100644
--- a/trusty/confirmationui/fuzz/msg_fuzzer.cpp
+++ b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
@@ -37,7 +37,7 @@
#define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf"
/* A request to render to screen may take a while. */
-const size_t kTimeoutSeconds = 30;
+const size_t kTimeoutSeconds = 60;
/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
static struct uuid confirmationui_uuid = {
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp
index c28fd05..65a3ba6 100644
--- a/trusty/fuzz/counters.cpp
+++ b/trusty/fuzz/counters.cpp
@@ -33,7 +33,7 @@
* We don't know how many counters the coverage record will contain. So, eyeball
* the size of this section.
*/
-static const size_t kMaxNumCounters = 0x8000;
+static const size_t kMaxNumCounters = 0x10000;
__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters];
namespace android {
diff --git a/trusty/keymaster/3.0/service.cpp b/trusty/keymaster/3.0/service.cpp
index 0d8436e..b916c37 100644
--- a/trusty/keymaster/3.0/service.cpp
+++ b/trusty/keymaster/3.0/service.cpp
@@ -24,7 +24,7 @@
int main() {
::android::hardware::configureRpcThreadpool(1, true);
auto trustyKeymaster = new keymaster::TrustyKeymaster();
- int err = trustyKeymaster->Initialize();
+ int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_3);
if (err != 0) {
LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
return -1;
diff --git a/trusty/keymaster/4.0/service.cpp b/trusty/keymaster/4.0/service.cpp
index 96eb584..0e5144d 100644
--- a/trusty/keymaster/4.0/service.cpp
+++ b/trusty/keymaster/4.0/service.cpp
@@ -24,7 +24,7 @@
int main() {
::android::hardware::configureRpcThreadpool(1, true);
auto trustyKeymaster = new keymaster::TrustyKeymaster();
- int err = trustyKeymaster->Initialize();
+ int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_4);
if (err != 0) {
LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
return -1;
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 6d24e84..ff6460d 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -80,12 +80,76 @@
vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
}
+cc_binary {
+ name: "android.hardware.security.keymint-service.trusty",
+ relative_install_path: "hw",
+ init_rc: ["keymint/android.hardware.security.keymint-service.trusty.rc"],
+ vintf_fragments: [
+ "keymint/android.hardware.security.keymint-service.trusty.xml",
+ ],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ local_include_dirs: [
+ "include",
+ ],
+ srcs: [
+ "TrustyKeymaster.cpp",
+ "ipc/trusty_keymaster_ipc.cpp",
+ "keymint/TrustyKeyMintDevice.cpp",
+ "keymint/TrustyKeyMintOperation.cpp",
+ "keymint/TrustyRemotelyProvisionedComponentDevice.cpp",
+ "keymint/TrustySecureClock.cpp",
+ "keymint/TrustySharedSecret.cpp",
+ "keymint/service.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "lib_android_keymaster_keymint_utils",
+ "libbase",
+ "libbinder_ndk",
+ "libhardware",
+ "libkeymaster_messages",
+ "libkeymint",
+ "liblog",
+ "libtrusty",
+ ],
+ required: [
+ "android.hardware.hardware_keystore.xml",
+ ],
+}
+
prebuilt_etc {
name: "keymaster_soft_attestation_keys.xml",
vendor: true,
src: "set_attestation_key/keymaster_soft_attestation_keys.xml",
}
+cc_library {
+ name: "libtrusty_ipc",
+ vendor: true,
+ srcs: ["ipc/trusty_keymaster_ipc.cpp"],
+ local_include_dirs: ["include"],
+ shared_libs: [
+ "libc",
+ "libcrypto",
+ "liblog",
+ "libtrusty",
+ "libhardware",
+ "libkeymaster_messages",
+ "libxml2",
+ ],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
cc_binary {
name: "trusty_keymaster_set_attestation_key",
vendor: true,
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 23e0433..cdfbd90 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include <cutils/log.h>
+#define LOG_TAG "trusty_keymaster_hal"
+#include <android-base/logging.h>
+
#include <keymaster/android_keymaster_messages.h>
#include <keymaster/keymaster_configuration.h>
#include <trusty_keymaster/TrustyKeymaster.h>
@@ -22,24 +24,28 @@
namespace keymaster {
-int TrustyKeymaster::Initialize() {
+int TrustyKeymaster::Initialize(KmVersion version) {
int err;
+ LOG(INFO) << "Initializing TrustyKeymaster as KmVersion: " << (int)version;
+
err = trusty_keymaster_connect();
if (err) {
- ALOGE("Failed to connect to trusty keymaster %d", err);
+ LOG(ERROR) << "Failed to connect to trusty keymaster (1st try)" << err;
return err;
}
// Try GetVersion2 first.
GetVersion2Request versionReq;
+ versionReq.max_message_version = MessageVersion(version);
GetVersion2Response versionRsp = GetVersion2(versionReq);
if (versionRsp.error != KM_ERROR_OK) {
- ALOGW("TA appears not to support GetVersion2, falling back (err = %d)", versionRsp.error);
+ LOG(WARNING) << "TA appears not to support GetVersion2, falling back (err = "
+ << versionRsp.error << ")";
err = trusty_keymaster_connect();
if (err) {
- ALOGE("Failed to connect to trusty keymaster %d", err);
+ LOG(FATAL) << "Failed to connect to trusty keymaster (2nd try) " << err;
return err;
}
@@ -47,13 +53,13 @@
GetVersionResponse versionRsp;
GetVersion(versionReq, &versionRsp);
if (versionRsp.error != KM_ERROR_OK) {
- ALOGE("Failed to get TA version %d", versionRsp.error);
+ LOG(FATAL) << "Failed to get TA version " << versionRsp.error;
return -1;
} else {
keymaster_error_t error;
message_version_ = NegotiateMessageVersion(versionRsp, &error);
if (error != KM_ERROR_OK) {
- ALOGE("Failed to negotiate message version %d", error);
+ LOG(FATAL) << "Failed to negotiate message version " << error;
return -1;
}
}
@@ -69,10 +75,20 @@
Configure(req, &rsp);
if (rsp.error != KM_ERROR_OK) {
- ALOGE("Failed to configure keymaster %d", rsp.error);
+ LOG(FATAL) << "Failed to configure keymaster " << rsp.error;
return -1;
}
+ // Set the vendor patchlevel to value retrieved from system property (which
+ // requires SELinux permission).
+ ConfigureVendorPatchlevelRequest vendor_req(message_version());
+ vendor_req.vendor_patchlevel = GetVendorPatchlevel();
+ ConfigureVendorPatchlevelResponse vendor_rsp = ConfigureVendorPatchlevel(vendor_req);
+ if (vendor_rsp.error != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to configure keymaster vendor patchlevel: " << vendor_rsp.error;
+ // Don't fail if this message isn't understood.
+ }
+
return 0;
}
@@ -87,7 +103,7 @@
keymaster_error_t err;
err = trusty_keymaster_send(command, req, rsp);
if (err != KM_ERROR_OK) {
- ALOGE("Failed to send cmd %d err: %d", command, err);
+ LOG(ERROR) << "Cmd " << command << " returned error: " << err;
rsp->error = err;
}
}
@@ -137,14 +153,29 @@
void TrustyKeymaster::GenerateKey(const GenerateKeyRequest& request,
GenerateKeyResponse* response) {
- GenerateKeyRequest datedRequest(request.message_version);
- datedRequest.key_description = request.key_description;
+ if (message_version_ < 4) {
+ // Pre-KeyMint we need to add TAG_CREATION_DATETIME if not provided by the caller.
+ GenerateKeyRequest datedRequest(request.message_version);
+ datedRequest.key_description = request.key_description;
- if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
- datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+ if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
+ datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+ }
+
+ ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
+ } else {
+ ForwardCommand(KM_GENERATE_KEY, request, response);
}
+}
- ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
+void TrustyKeymaster::GenerateRkpKey(const GenerateRkpKeyRequest& request,
+ GenerateRkpKeyResponse* response) {
+ ForwardCommand(KM_GENERATE_RKP_KEY, request, response);
+}
+
+void TrustyKeymaster::GenerateCsr(const GenerateCsrRequest& request,
+ GenerateCsrResponse* response) {
+ ForwardCommand(KM_GENERATE_CSR, request, response);
}
void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
@@ -229,4 +260,23 @@
return response;
}
+EarlyBootEndedResponse TrustyKeymaster::EarlyBootEnded() {
+ EarlyBootEndedResponse response(message_version());
+ ForwardCommand(KM_EARLY_BOOT_ENDED, EarlyBootEndedRequest(message_version()), &response);
+ return response;
+}
+
+DeviceLockedResponse TrustyKeymaster::DeviceLocked(const DeviceLockedRequest& request) {
+ DeviceLockedResponse response(message_version());
+ ForwardCommand(KM_DEVICE_LOCKED, request, &response);
+ return response;
+}
+
+ConfigureVendorPatchlevelResponse TrustyKeymaster::ConfigureVendorPatchlevel(
+ const ConfigureVendorPatchlevelRequest& request) {
+ ConfigureVendorPatchlevelResponse response(message_version());
+ ForwardCommand(KM_CONFIGURE_VENDOR_PATCHLEVEL, request, &response);
+ return response;
+}
+
} // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
new file mode 100644
index 0000000..5fd628f
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021, 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 <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using ::keymaster::TrustyKeymaster;
+using ::ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using ::std::optional;
+using ::std::shared_ptr;
+using ::std::vector;
+
+class TrustyKeyMintDevice : public BnKeyMintDevice {
+ public:
+ explicit TrustyKeyMintDevice(shared_ptr<TrustyKeymaster> impl) : impl_(std::move(impl)) {}
+ virtual ~TrustyKeyMintDevice() = default;
+
+ ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
+
+ ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
+
+ ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus getKeyCharacteristics(const vector<uint8_t>& keyBlob,
+ const vector<uint8_t>& clientId,
+ const vector<uint8_t>& appData,
+ vector<KeyCharacteristics>* characteristics) override;
+
+ ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+ const vector<uint8_t>& keyData,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ const vector<uint8_t>& wrappingKeyBlob,
+ const vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams,
+ int64_t passwordSid, int64_t biometricSid,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+ const vector<KeyParameter>& upgradeParams,
+ vector<uint8_t>* keyBlob) override;
+
+ ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
+ ScopedAStatus deleteAllKeys() override;
+ ScopedAStatus destroyAttestationIds() override;
+
+ ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
+ const vector<KeyParameter>& params,
+ const optional<HardwareAuthToken>& authToken, BeginResult* result) override;
+
+ ScopedAStatus deviceLocked(bool passwordOnly,
+ const optional<TimeStampToken>& timestampToken) override;
+ ScopedAStatus earlyBootEnded() override;
+
+ ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+ std::vector<uint8_t>* ephemeralKeyBlob) override;
+
+ protected:
+ std::shared_ptr<TrustyKeymaster> impl_;
+ SecurityLevel securityLevel_;
+};
+
+} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h
new file mode 100644
index 0000000..65fd2f5
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021, 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 <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+#include <hardware/keymaster_defs.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::keymaster::TrustyKeymaster;
+using ::ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using std::optional;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+class TrustyKeyMintOperation : public BnKeyMintOperation {
+ public:
+ explicit TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,
+ keymaster_operation_handle_t opHandle);
+ virtual ~TrustyKeyMintOperation();
+
+ ScopedAStatus updateAad(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken) override;
+
+ ScopedAStatus update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ vector<uint8_t>* output) override;
+
+ ScopedAStatus finish(const optional<vector<uint8_t>>& input, //
+ const optional<vector<uint8_t>>& signature, //
+ const optional<HardwareAuthToken>& authToken, //
+ const optional<TimeStampToken>& timestampToken,
+ const optional<vector<uint8_t>>& confirmationToken,
+ vector<uint8_t>* output) override;
+
+ ScopedAStatus abort() override;
+
+ protected:
+ std::shared_ptr<TrustyKeymaster> impl_;
+ keymaster_operation_handle_t opHandle_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index bec2a2a..f80e02f 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -25,7 +25,7 @@
public:
TrustyKeymaster();
~TrustyKeymaster();
- int Initialize();
+ int Initialize(KmVersion version);
void GetVersion(const GetVersionRequest& request, GetVersionResponse* response);
void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
SupportedAlgorithmsResponse* response);
@@ -42,6 +42,8 @@
void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
void Configure(const ConfigureRequest& request, ConfigureResponse* response);
void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
+ void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);
+ void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);
void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
GetKeyCharacteristicsResponse* response);
void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
@@ -60,6 +62,10 @@
ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
GetVersion2Response GetVersion2(const GetVersion2Request& request);
+ EarlyBootEndedResponse EarlyBootEnded();
+ DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
+ ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
+ const ConfigureVendorPatchlevelRequest& request);
uint32_t message_version() const { return message_version_; }
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
new file mode 100644
index 0000000..d544b51
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021, 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 <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using ::keymaster::TrustyKeymaster;
+using ::ndk::ScopedAStatus;
+using ::std::shared_ptr;
+
+class TrustyRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {
+ public:
+ explicit TrustyRemotelyProvisionedComponentDevice(shared_ptr<TrustyKeymaster> impl)
+ : impl_(std::move(impl)) {}
+ virtual ~TrustyRemotelyProvisionedComponentDevice() = default;
+
+ ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
+
+ ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
+ std::vector<uint8_t>* privateKeyHandle) override;
+
+ ScopedAStatus generateCertificateRequest(bool testMode,
+ const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& endpointEncCertChain,
+ const std::vector<uint8_t>& challenge,
+ DeviceInfo* deviceInfo, ProtectedData* protectedData,
+ std::vector<uint8_t>* keysToSignMac) override;
+
+ private:
+ std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h b/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h
new file mode 100644
index 0000000..f077b27
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021, 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 <aidl/android/hardware/security/secureclock/BnSecureClock.h>
+#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
+#include <aidl/android/hardware/security/secureclock/Timestamp.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::secureclock::trusty {
+
+class TrustySecureClock : public BnSecureClock {
+ public:
+ explicit TrustySecureClock(std::shared_ptr<::keymaster::TrustyKeymaster> impl)
+ : impl_(std::move(impl)) {}
+ ~TrustySecureClock() = default;
+ ::ndk::ScopedAStatus generateTimeStamp(int64_t challenge, TimeStampToken* token) override;
+
+ private:
+ std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+} // namespace aidl::android::hardware::security::secureclock::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h b/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h
new file mode 100644
index 0000000..946f57e
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021, 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 <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::sharedsecret::trusty {
+
+class TrustySharedSecret : public BnSharedSecret {
+ public:
+ explicit TrustySharedSecret(std::shared_ptr<::keymaster::TrustyKeymaster> impl)
+ : impl_(std::move(impl)) {}
+ ~TrustySharedSecret() = default;
+
+ ::ndk::ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override;
+ ::ndk::ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params,
+ std::vector<uint8_t>* sharingCheck) override;
+
+ private:
+ std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+} // namespace aidl::android::hardware::security::sharedsecret::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index 419c96f..fa475ae 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -54,6 +54,11 @@
KM_DESTROY_ATTESTATION_IDS = (24 << KEYMASTER_REQ_SHIFT),
KM_IMPORT_WRAPPED_KEY = (25 << KEYMASTER_REQ_SHIFT),
KM_GET_VERSION_2 = (28 << KEYMASTER_REQ_SHIFT),
+ KM_EARLY_BOOT_ENDED = (29 << KEYMASTER_REQ_SHIFT),
+ KM_DEVICE_LOCKED = (30 << KEYMASTER_REQ_SHIFT),
+ KM_GENERATE_RKP_KEY = (31 << KEYMASTER_REQ_SHIFT),
+ KM_GENERATE_CSR = (32 << KEYMASTER_REQ_SHIFT),
+ KM_CONFIGURE_VENDOR_PATCHLEVEL = (33 << KEYMASTER_REQ_SHIFT),
// Bootloader/provisioning calls.
KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
@@ -67,6 +72,8 @@
KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),
KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),
+ KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),
};
#ifdef __ANDROID__
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
new file mode 100644
index 0000000..5f8524b
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -0,0 +1,324 @@
+/*
+
+ * Copyright 2021, 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 <trusty_keymaster/TrustyKeyMintDevice.h>
+
+#define TAG TrustyKeyMintDevice
+#include <android-base/logging.h>
+
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/authorization_set.h>
+
+#include <KeyMintUtils.h>
+
+#include <trusty_keymaster/TrustyKeyMintOperation.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using keymaster::KeymasterBlob;
+using keymaster::KeymasterKeyBlob;
+using keymaster::TAG_APPLICATION_DATA;
+using keymaster::TAG_APPLICATION_ID;
+using keymaster::TAG_AUTH_TOKEN;
+using km_utils::authToken2AidlVec;
+using km_utils::kmBlob2vector;
+using km_utils::kmError2ScopedAStatus;
+using km_utils::kmParam2Aidl;
+using km_utils::KmParamSet;
+using km_utils::kmParamSet2Aidl;
+using km_utils::legacy_enum_conversion;
+
+namespace {
+
+auto kSecurityLevel = SecurityLevel::TRUSTED_ENVIRONMENT;
+
+KeyCharacteristics convertAuthSet(SecurityLevel securityLevel,
+ const keymaster::AuthorizationSet& authorizations) {
+ KeyCharacteristics retval{securityLevel, {}};
+ std::transform(authorizations.begin(), authorizations.end(),
+ std::back_inserter(retval.authorizations), kmParam2Aidl);
+ return retval;
+}
+
+vector<KeyCharacteristics> convertKeyCharacteristics(const keymaster::AuthorizationSet& sw_enforced,
+ const keymaster::AuthorizationSet& hw_enforced,
+ bool includeKeystoreEnforced = true) {
+ KeyCharacteristics keyMintEnforced = convertAuthSet(kSecurityLevel, hw_enforced);
+ KeyCharacteristics keystoreEnforced = convertAuthSet(SecurityLevel::KEYSTORE, sw_enforced);
+
+ vector<KeyCharacteristics> retval;
+ retval.reserve(2);
+
+ if (!keyMintEnforced.authorizations.empty()) retval.push_back(std::move(keyMintEnforced));
+ if (includeKeystoreEnforced && !keystoreEnforced.authorizations.empty()) {
+ retval.push_back(std::move(keystoreEnforced));
+ }
+
+ return retval;
+}
+
+Certificate convertCertificate(const keymaster_blob_t& cert) {
+ return {std::vector<uint8_t>(cert.data, cert.data + cert.data_length)};
+}
+
+vector<Certificate> convertCertificateChain(const keymaster::CertificateChain& chain) {
+ vector<Certificate> retval;
+ std::transform(chain.begin(), chain.end(), std::back_inserter(retval), convertCertificate);
+ return retval;
+}
+
+void addClientAndAppData(const vector<uint8_t>& clientId, const vector<uint8_t>& appData,
+ ::keymaster::AuthorizationSet* params) {
+ params->Clear();
+ if (clientId.size()) params->push_back(TAG_APPLICATION_ID, clientId.data(), clientId.size());
+ if (appData.size()) params->push_back(TAG_APPLICATION_DATA, appData.data(), appData.size());
+}
+
+} // namespace
+
+ScopedAStatus TrustyKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
+ info->versionNumber = 1;
+ info->securityLevel = kSecurityLevel;
+ info->keyMintName = "TrustyKeyMintDevice";
+ info->keyMintAuthorName = "Google";
+ info->timestampTokenRequired = false;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
+ if (data.size() == 0) return ScopedAStatus::ok();
+ if (data.size() > 2048) {
+ LOG(DEBUG) << "Too-large entropy update of " << data.size() << " bytes.";
+ return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
+ }
+
+ keymaster::AddEntropyRequest request(impl_->message_version());
+ request.random_data.Reinitialize(data.data(), data.size());
+
+ keymaster::AddEntropyResponse response(impl_->message_version());
+ impl_->AddRngEntropy(request, &response);
+
+ return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) {
+ keymaster::GenerateKeyRequest request(impl_->message_version());
+ request.key_description.Reinitialize(KmParamSet(keyParams));
+ if (attestationKey) {
+ request.attestation_signing_key_blob =
+ KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
+ request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
+ request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
+ attestationKey->issuerSubjectName.size());
+ }
+
+ keymaster::GenerateKeyResponse response(impl_->message_version());
+ impl_->GenerateKey(request, &response);
+
+ if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+ creationResult->keyBlob = kmBlob2vector(response.key_blob);
+ creationResult->keyCharacteristics =
+ convertKeyCharacteristics(response.unenforced, response.enforced);
+ creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::getKeyCharacteristics(
+ const vector<uint8_t>& keyBlob,
+ const vector<uint8_t>& clientId, //
+ const vector<uint8_t>& appData, //
+ vector<KeyCharacteristics>* characteristics) {
+ keymaster::GetKeyCharacteristicsRequest request(impl_->message_version());
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ addClientAndAppData(clientId, appData, &request.additional_params);
+
+ keymaster::GetKeyCharacteristicsResponse response(impl_->message_version());
+ impl_->GetKeyCharacteristics(request, &response);
+
+ if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+ *characteristics = convertKeyCharacteristics(response.unenforced, response.enforced,
+ false /* includeKeystoreEnforced */);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,
+ KeyFormat keyFormat, const vector<uint8_t>& keyData,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) {
+ keymaster::ImportKeyRequest request(impl_->message_version());
+ request.key_description.Reinitialize(KmParamSet(keyParams));
+ request.key_format = legacy_enum_conversion(keyFormat);
+ request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
+ if (attestationKey) {
+ request.attestation_signing_key_blob =
+ KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
+ request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
+ request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
+ attestationKey->issuerSubjectName.size());
+ }
+
+ keymaster::ImportKeyResponse response(impl_->message_version());
+ impl_->ImportKey(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return kmError2ScopedAStatus(response.error);
+ }
+
+ creationResult->keyBlob = kmBlob2vector(response.key_blob);
+ creationResult->keyCharacteristics =
+ convertKeyCharacteristics(response.unenforced, response.enforced);
+ creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ const vector<uint8_t>& wrappingKeyBlob, //
+ const vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams,
+ int64_t passwordSid, //
+ int64_t biometricSid,
+ KeyCreationResult* creationResult) {
+ keymaster::ImportWrappedKeyRequest request(impl_->message_version());
+ request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
+ request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
+ request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
+ request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
+ request.password_sid = static_cast<uint64_t>(passwordSid);
+ request.biometric_sid = static_cast<uint64_t>(biometricSid);
+
+ keymaster::ImportWrappedKeyResponse response(impl_->message_version());
+ impl_->ImportWrappedKey(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return kmError2ScopedAStatus(response.error);
+ }
+
+ creationResult->keyBlob = kmBlob2vector(response.key_blob);
+ creationResult->keyCharacteristics =
+ convertKeyCharacteristics(response.unenforced, response.enforced);
+ creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+ const vector<KeyParameter>& upgradeParams,
+ vector<uint8_t>* keyBlob) {
+ keymaster::UpgradeKeyRequest request(impl_->message_version());
+ request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+ request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+ keymaster::UpgradeKeyResponse response(impl_->message_version());
+ impl_->UpgradeKey(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return kmError2ScopedAStatus(response.error);
+ }
+
+ *keyBlob = kmBlob2vector(response.upgraded_key);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) {
+ keymaster::DeleteKeyRequest request(impl_->message_version());
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+ keymaster::DeleteKeyResponse response(impl_->message_version());
+ impl_->DeleteKey(request, &response);
+
+ return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::deleteAllKeys() {
+ // There's nothing to be done to delete software key blobs.
+ keymaster::DeleteAllKeysRequest request(impl_->message_version());
+ keymaster::DeleteAllKeysResponse response(impl_->message_version());
+ impl_->DeleteAllKeys(request, &response);
+
+ return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::destroyAttestationIds() {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus TrustyKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
+ const vector<KeyParameter>& params,
+ const optional<HardwareAuthToken>& authToken,
+ BeginResult* result) {
+ keymaster::BeginOperationRequest request(impl_->message_version());
+ request.purpose = legacy_enum_conversion(purpose);
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ request.additional_params.Reinitialize(KmParamSet(params));
+
+ vector<uint8_t> vector_token = authToken2AidlVec(authToken);
+ request.additional_params.push_back(
+ TAG_AUTH_TOKEN, reinterpret_cast<uint8_t*>(vector_token.data()), vector_token.size());
+
+ keymaster::BeginOperationResponse response(impl_->message_version());
+ impl_->BeginOperation(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return kmError2ScopedAStatus(response.error);
+ }
+
+ result->params = kmParamSet2Aidl(response.output_params);
+ result->challenge = response.op_handle;
+ result->operation = ndk::SharedRefBase::make<TrustyKeyMintOperation>(impl_, response.op_handle);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::deviceLocked(
+ bool passwordOnly, const std::optional<secureclock::TimeStampToken>& timestampToken) {
+ keymaster::DeviceLockedRequest request(impl_->message_version());
+ request.passwordOnly = passwordOnly;
+ if (timestampToken.has_value()) {
+ request.token.challenge = timestampToken->challenge;
+ request.token.mac = {timestampToken->mac.data(), timestampToken->mac.size()};
+ request.token.timestamp = timestampToken->timestamp.milliSeconds;
+ }
+ keymaster::DeviceLockedResponse response = impl_->DeviceLocked(request);
+ return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::earlyBootEnded() {
+ keymaster::EarlyBootEndedResponse response = impl_->EarlyBootEnded();
+ return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(
+ const std::vector<uint8_t>& storageKeyBlob, std::vector<uint8_t>* ephemeralKeyBlob) {
+ keymaster::ExportKeyRequest request(impl_->message_version());
+ request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());
+ request.key_format = KM_KEY_FORMAT_RAW;
+
+ keymaster::ExportKeyResponse response(impl_->message_version());
+ impl_->ExportKey(request, &response);
+
+ if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+ if (response.key_data) {
+ *ephemeralKeyBlob = {response.key_data, response.key_data + response.key_data_length};
+ }
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
new file mode 100644
index 0000000..9440724
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2021, 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 <trusty_keymaster/TrustyKeyMintOperation.h>
+
+#define TAG TrustyKeyMintOperation
+#include <android-base/logging.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+
+#include <KeyMintUtils.h>
+#include <keymaster/android_keymaster.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::TAG_ASSOCIATED_DATA;
+using ::keymaster::TAG_AUTH_TOKEN;
+using ::keymaster::TAG_CONFIRMATION_TOKEN;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using km_utils::authToken2AidlVec;
+using km_utils::kmError2ScopedAStatus;
+using secureclock::TimeStampToken;
+
+TrustyKeyMintOperation::TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,
+ keymaster_operation_handle_t opHandle)
+ : impl_(std::move(implementation)), opHandle_(opHandle) {}
+
+TrustyKeyMintOperation::~TrustyKeyMintOperation() {
+ if (opHandle_ != 0) {
+ abort();
+ }
+}
+
+ScopedAStatus TrustyKeyMintOperation::updateAad(
+ const vector<uint8_t>& input, const optional<HardwareAuthToken>& /* authToken */,
+ const optional<TimeStampToken>& /* timestampToken */) {
+ UpdateOperationRequest request(impl_->message_version());
+ request.op_handle = opHandle_;
+ request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(), input.size());
+
+ UpdateOperationResponse response(impl_->message_version());
+ impl_->UpdateOperation(request, &response);
+
+ return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintOperation::update(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& /* timestampToken */,
+ vector<uint8_t>* output) {
+ if (!output) return kmError2ScopedAStatus(KM_ERROR_OUTPUT_PARAMETER_NULL);
+
+ UpdateOperationRequest request(impl_->message_version());
+ request.op_handle = opHandle_;
+ if (authToken) {
+ auto tokenAsVec(authToken2AidlVec(*authToken));
+ request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
+ }
+
+ size_t serialized_size = request.SerializedSize();
+ if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
+ }
+
+ const uint8_t* input_pos = input.data();
+ const uint8_t* input_end = input.data() + input.size();
+ const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;
+ output->clear();
+
+ while (input_pos < input_end) {
+ size_t to_send = std::min(max_chunk_size, static_cast<size_t>(input_end - input_pos));
+ LOG(DEBUG) << "update: Sending " << to_send << " of " << (input_end - input_pos)
+ << " bytes";
+ request.input.Reinitialize(input_pos, to_send);
+
+ UpdateOperationResponse response(impl_->message_version());
+ impl_->UpdateOperation(request, &response);
+ if (response.error != KM_ERROR_OK) {
+ opHandle_ = 0; // Operation has ended, the handle is invalid. This saves an abort().
+ return kmError2ScopedAStatus(response.error);
+ }
+
+ input_pos += response.input_consumed;
+ output->insert(output->end(), response.output.begin(), response.output.end());
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintOperation::finish(const optional<vector<uint8_t>>& input, //
+ const optional<vector<uint8_t>>& signature, //
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& /* timestampToken */,
+ const optional<vector<uint8_t>>& confirmationToken,
+ vector<uint8_t>* output) {
+ if (!output) {
+ return ScopedAStatus(AStatus_fromServiceSpecificError(
+ static_cast<int32_t>(ErrorCode::OUTPUT_PARAMETER_NULL)));
+ }
+ output->clear();
+
+ FinishOperationRequest request(impl_->message_version());
+
+ if (authToken) {
+ auto tokenAsVec(authToken2AidlVec(*authToken));
+ request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
+ }
+ if (confirmationToken) {
+ request.additional_params.push_back(TAG_CONFIRMATION_TOKEN, confirmationToken->data(),
+ confirmationToken->size());
+ }
+
+ request.op_handle = opHandle_;
+ if (signature) request.signature.Reinitialize(signature->data(), signature->size());
+ size_t serialized_size = request.SerializedSize();
+ if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
+ }
+
+ if (input) {
+ const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;
+
+ if (input->size() > max_chunk_size) {
+ LOG(DEBUG) << "Sending an update to process finish() data";
+ // Use update to process all but the last max_chunk_size bytes.
+ auto result = update({input->begin(), input->end() - max_chunk_size}, authToken,
+ std::nullopt /* timestampToken */, output);
+ if (!result.isOk()) return result;
+
+ // Process the last max_chunk_size with finish.
+ request.input.Reinitialize(input->data() + (input->size() - max_chunk_size),
+ max_chunk_size);
+ } else {
+ request.input.Reinitialize(input->data(), input->size());
+ }
+ }
+
+ FinishOperationResponse response(impl_->message_version());
+ impl_->FinishOperation(request, &response);
+ opHandle_ = 0;
+
+ if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+ *output = {response.output.begin(), response.output.end()};
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintOperation::abort() {
+ AbortOperationRequest request(impl_->message_version());
+ request.op_handle = opHandle_;
+
+ AbortOperationResponse response(impl_->message_version());
+ impl_->AbortOperation(request, &response);
+ opHandle_ = 0;
+
+ return kmError2ScopedAStatus(response.error);
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
new file mode 100644
index 0000000..5664829
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021, 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 <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
+
+#include <assert.h>
+#include <variant>
+
+#include <KeyMintUtils.h>
+#include <keymaster/keymaster_configuration.h>
+
+#include <trusty_keymaster/TrustyKeyMintDevice.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using keymaster::GenerateCsrRequest;
+using keymaster::GenerateCsrResponse;
+using keymaster::GenerateRkpKeyRequest;
+using keymaster::GenerateRkpKeyResponse;
+using keymaster::KeymasterBlob;
+using ::std::string;
+using ::std::unique_ptr;
+using ::std::vector;
+using bytevec = ::std::vector<uint8_t>;
+
+namespace {
+
+constexpr auto STATUS_FAILED = IRemotelyProvisionedComponent::STATUS_FAILED;
+
+struct AStatusDeleter {
+ void operator()(AStatus* p) { AStatus_delete(p); }
+};
+
+class Status {
+ public:
+ Status() : status_(AStatus_newOk()) {}
+ Status(int32_t errCode, const std::string& errMsg)
+ : status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, errMsg.c_str())) {}
+ explicit Status(const std::string& errMsg)
+ : status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, errMsg.c_str())) {}
+ explicit Status(AStatus* status) : status_(status ? status : AStatus_newOk()) {}
+
+ Status(Status&&) = default;
+ Status(const Status&) = delete;
+
+ operator ::ndk::ScopedAStatus() && { // NOLINT(google-explicit-constructor)
+ return ndk::ScopedAStatus(status_.release());
+ }
+
+ bool isOk() const { return AStatus_isOk(status_.get()); }
+
+ const char* getMessage() const { return AStatus_getMessage(status_.get()); }
+
+ private:
+ std::unique_ptr<AStatus, AStatusDeleter> status_;
+};
+
+} // namespace
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
+ info->versionNumber = 1;
+ info->rpcAuthorName = "Google";
+ info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(
+ bool testMode, MacedPublicKey* macedPublicKey, bytevec* privateKeyHandle) {
+ GenerateRkpKeyRequest request(impl_->message_version());
+ request.test_mode = testMode;
+ GenerateRkpKeyResponse response(impl_->message_version());
+ impl_->GenerateRkpKey(request, &response);
+ if (response.error != KM_ERROR_OK) {
+ return Status(-static_cast<int32_t>(response.error), "Failure in key generation.");
+ }
+
+ macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);
+ *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequest(
+ bool testMode, const vector<MacedPublicKey>& keysToSign,
+ const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
+ ProtectedData* protectedData, bytevec* keysToSignMac) {
+ GenerateCsrRequest request(impl_->message_version());
+ request.test_mode = testMode;
+ request.num_keys = keysToSign.size();
+ request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];
+ for (size_t i = 0; i < keysToSign.size(); i++) {
+ request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
+ }
+ request.SetEndpointEncCertChain(endpointEncCertChain.data(), endpointEncCertChain.size());
+ request.SetChallenge(challenge.data(), challenge.size());
+ GenerateCsrResponse response(impl_->message_version());
+ impl_->GenerateCsr(request, &response);
+
+ if (response.error != KM_ERROR_OK) {
+ return Status(-static_cast<int32_t>(response.error), "Failure in CSR Generation.");
+ }
+ deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);
+ protectedData->protectedData = km_utils::kmBlob2vector(response.protected_data_blob);
+ *keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustySecureClock.cpp b/trusty/keymaster/keymint/TrustySecureClock.cpp
new file mode 100644
index 0000000..fed5420
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustySecureClock.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021, 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 <trusty_keymaster/TrustySecureClock.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+
+#include <KeyMintUtils.h>
+
+namespace aidl::android::hardware::security::secureclock::trusty {
+
+using keymint::km_utils::kmBlob2vector;
+using keymint::km_utils::kmError2ScopedAStatus;
+
+::ndk::ScopedAStatus TrustySecureClock::generateTimeStamp(int64_t challenge,
+ TimeStampToken* token) {
+ keymaster::VerifyAuthorizationRequest request(impl_->message_version());
+ request.challenge = challenge;
+
+ auto response = impl_->VerifyAuthorization(request);
+ if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+ token->challenge = response.token.challenge;
+ token->timestamp.milliSeconds = static_cast<int64_t>(response.token.timestamp);
+ token->mac = kmBlob2vector(response.token.mac);
+ return ::ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::secureclock::trusty
diff --git a/trusty/keymaster/keymint/TrustySharedSecret.cpp b/trusty/keymaster/keymint/TrustySharedSecret.cpp
new file mode 100644
index 0000000..8109168
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustySharedSecret.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <trusty_keymaster/TrustySharedSecret.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <keymaster/android_keymaster.h>
+#include "KeyMintUtils.h"
+
+namespace aidl::android::hardware::security::sharedsecret::trusty {
+
+using keymint::km_utils::kmBlob2vector;
+using keymint::km_utils::kmError2ScopedAStatus;
+
+::ndk::ScopedAStatus TrustySharedSecret::getSharedSecretParameters(SharedSecretParameters* params) {
+ auto response = impl_->GetHmacSharingParameters();
+ params->seed = kmBlob2vector(response.params.seed);
+ params->nonce = {std::begin(response.params.nonce), std::end(response.params.nonce)};
+ return kmError2ScopedAStatus(response.error);
+}
+
+::ndk::ScopedAStatus TrustySharedSecret::computeSharedSecret(
+ const std::vector<SharedSecretParameters>& params, std::vector<uint8_t>* sharingCheck) {
+ keymaster::ComputeSharedHmacRequest request(impl_->message_version());
+ request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
+ request.params_array.num_params = params.size();
+ for (size_t i = 0; i < params.size(); ++i) {
+ request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};
+ if (sizeof(request.params_array.params_array[i].nonce) != params[i].nonce.size()) {
+ return kmError2ScopedAStatus(KM_ERROR_INVALID_ARGUMENT);
+ }
+ memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
+ params[i].nonce.size());
+ }
+
+ auto response = impl_->ComputeSharedHmac(request);
+ if (response.error == KM_ERROR_OK) *sharingCheck = kmBlob2vector(response.sharing_check);
+ return kmError2ScopedAStatus(response.error);
+}
+
+} // namespace aidl::android::hardware::security::sharedsecret::trusty
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc
new file mode 100644
index 0000000..389af41
--- /dev/null
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.keymint-trusty /vendor/bin/hw/android.hardware.security.keymint-service.trusty
+ class early_hal
+ user nobody
+ group drmrpc
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
new file mode 100644
index 0000000..7ca5050
--- /dev/null
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -0,0 +1,18 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <fqname>IKeyMintDevice/default</fqname>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.security.secureclock</name>
+ <fqname>ISecureClock/default</fqname>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.security.sharedsecret</name>
+ <fqname>ISharedSecret/default</fqname>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <fqname>IRemotelyProvisionedComponent/default</fqname>
+ </hal>
+</manifest>
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
new file mode 100644
index 0000000..4060278
--- /dev/null
+++ b/trusty/keymaster/keymint/service.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#define LOG_TAG "android.hardware.security.keymint-service.trusty"
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <trusty_keymaster/TrustyKeyMintDevice.h>
+#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
+#include <trusty_keymaster/TrustySecureClock.h>
+#include <trusty_keymaster/TrustySharedSecret.h>
+
+using aidl::android::hardware::security::keymint::trusty::TrustyKeyMintDevice;
+using aidl::android::hardware::security::keymint::trusty::TrustyRemotelyProvisionedComponentDevice;
+using aidl::android::hardware::security::secureclock::trusty::TrustySecureClock;
+using aidl::android::hardware::security::sharedsecret::trusty::TrustySharedSecret;
+
+template <typename T, class... Args>
+std::shared_ptr<T> addService(Args&&... args) {
+ std::shared_ptr<T> service = std::make_shared<T>(std::forward<Args>(args)...);
+ auto instanceName = std::string(T::descriptor) + "/default";
+ LOG(ERROR) << "Adding service instance: " << instanceName;
+ auto status = AServiceManager_addService(service->asBinder().get(), instanceName.c_str());
+ CHECK(status == STATUS_OK) << "Failed to add service " << instanceName;
+ return service;
+}
+
+int main() {
+ auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();
+ int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_1);
+ if (err != 0) {
+ LOG(FATAL) << "Could not initialize TrustyKeymaster for KeyMint (" << err << ")";
+ return -1;
+ }
+
+ // Zero threads seems like a useless pool but below we'll join this thread to it, increasing
+ // the pool size to 1.
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ auto keyMint = addService<TrustyKeyMintDevice>(trustyKeymaster);
+ auto secureClock = addService<TrustySecureClock>(trustyKeymaster);
+ auto sharedSecret = addService<TrustySharedSecret>(trustyKeymaster);
+ auto remotelyProvisionedComponent =
+ addService<TrustyRemotelyProvisionedComponentDevice>(trustyKeymaster);
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index a471435..d67089f 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -29,7 +29,10 @@
"proxy.c",
],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "liblog",
+ "libhardware_legacy",
+ ],
header_libs: ["libcutils_headers"],
static_libs: [
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index d1ed649..b59fb67 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -29,6 +29,8 @@
#include <linux/major.h>
#include <linux/mmc/ioctl.h>
+#include <hardware_legacy/power.h>
+
#include "ipc.h"
#include "log.h"
#include "rpmb.h"
@@ -100,6 +102,8 @@
static uint8_t read_buf[4096];
static enum dev_type dev_type = UNKNOWN_RPMB;
+static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
+
#ifdef RPMB_DEBUG
static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
@@ -194,6 +198,7 @@
static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) {
int rc;
+ int wl_rc;
const uint8_t* write_buf = req->payload;
/*
* Meaning of member values are stated on the definition of struct sec_proto_cdb.
@@ -202,6 +207,12 @@
struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
unsigned char sense_buffer[32];
+ wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
+ if (wl_rc < 0) {
+ ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
+ return wl_rc;
+ }
+
if (req->reliable_write_size) {
/* Prepare SECURITY PROTOCOL OUT command. */
out_cdb.length = __builtin_bswap32(req->reliable_write_size);
@@ -212,6 +223,7 @@
rc = ioctl(sg_fd, SG_IO, &io_hdr);
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ goto err_op;
}
write_buf += req->reliable_write_size;
}
@@ -225,6 +237,7 @@
rc = ioctl(sg_fd, SG_IO, &io_hdr);
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ goto err_op;
}
write_buf += req->write_size;
}
@@ -240,6 +253,13 @@
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
}
}
+
+err_op:
+ wl_rc = release_wake_lock(UFS_WAKE_LOCK_NAME);
+ if (wl_rc < 0) {
+ ALOGE("%s: failed to release wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
+ }
+
return rc;
}
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 12521b0..6cd381f 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -28,5 +28,6 @@
trusty_apploader
PRODUCT_PROPERTY_OVERRIDES += \
+ ro.hardware.keystore_desede=true \
ro.hardware.keystore=trusty \
ro.hardware.gatekeeper=trusty
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 22d171d..27db0fa 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -13,6 +13,5 @@
"libutils",
"libhardware",
"android.hardware.usb.gadget@1.0",
- "libcutils",
],
}