[automerger skipped] init.rc: disable creation of bootreceiver tracing instance for kernels >=4.9 and <= 5.4 am: 4edda28b92 am: 4224e6af27 -s ours
am skip reason: Merged-In I794062741688ebea0e4bc500723a966f8f646ee1 with SHA-1 53cd1e95e7 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/core/+/15994846
Change-Id: I38e5e29d59ca991fb0aad1bd2f1adadbd1a9237c
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index da7fca1..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,66 +0,0 @@
-{
- "presubmit": [
- {
- "name": "adbd_test"
- },
- {
- "name": "adb_crypto_test"
- },
- {
- "name": "adb_pairing_auth_test"
- },
- {
- "name": "adb_pairing_connection_test"
- },
- {
- "name": "adb_tls_connection_test"
- },
- {
- "name": "CtsFsMgrTestCases"
- },
- {
- "name": "CtsInitTestCases"
- },
- {
- "name": "debuggerd_test"
- },
- {
- "name": "fs_mgr_vendor_overlay_test"
- },
- {
- "name": "init_kill_services_test"
- },
- {
- "name": "libpackagelistparser_test"
- },
- {
- "name": "libcutils_test"
- },
- {
- "name": "libmodprobe_tests"
- },
- {
- "name": "libprocinfo_test"
- },
- {
- "name": "libutils_test"
- },
- {
- "name": "memunreachable_test"
- },
- {
- "name": "memunreachable_unit_test"
- },
- {
- "name": "memunreachable_binder_test"
- },
- {
- "name": "propertyinfoserializer_tests"
- }
- ],
- "imports": [
- {
- "path": "frameworks/base/tests/StagedInstallTest"
- }
- ]
-}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d6ebb0d..2c878f0 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -439,6 +439,30 @@
{"reboot,forcedsilent", 191},
{"reboot,forcednonsilent", 192},
{"reboot,thermal,tj", 193},
+ {"reboot,emergency", 194},
+ {"reboot,factory", 195},
+ {"reboot,fastboot", 196},
+ {"reboot,gsa,hard", 197},
+ {"reboot,gsa,soft", 198},
+ {"reboot,master_dc,fault_n", 199},
+ {"reboot,master_dc,reset", 200},
+ {"reboot,ocp", 201},
+ {"reboot,pin", 202},
+ {"reboot,rom_recovery", 203},
+ {"reboot,uvlo", 204},
+ {"reboot,uvlo,pmic,if", 205},
+ {"reboot,uvlo,pmic,main", 206},
+ {"reboot,uvlo,pmic,sub", 207},
+ {"reboot,warm", 208},
+ {"watchdog,aoc", 209},
+ {"watchdog,apc", 210},
+ {"watchdog,apc,bl,debug,early", 211},
+ {"watchdog,apc,bl,early", 212},
+ {"watchdog,apc,early", 213},
+ {"watchdog,apm", 214},
+ {"watchdog,gsa,hard", 215},
+ {"watchdog,gsa,soft", 216},
+ {"watchdog,pmucal", 217},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 198e4de..0ff047f 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,10 +8,12 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wno-gcc-compat",
"-Wno-unused-argument",
"-Wno-unused-function",
"-Wno-nullability-completeness",
"-Os",
+ "-fno-finite-loops",
],
local_include_dirs: ["include"],
@@ -202,7 +180,6 @@
"libdebuggerd/backtrace.cpp",
"libdebuggerd/gwp_asan.cpp",
"libdebuggerd/open_files_list.cpp",
- "libdebuggerd/scudo.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/tombstone_proto.cpp",
"libdebuggerd/tombstone_proto_to_text.cpp",
@@ -215,9 +192,6 @@
include_dirs: [
// Needed for private/bionic_fdsan.h
"bionic/libc",
-
- // Needed for scudo/interface.h
- "external/scudo/standalone/include",
],
header_libs: [
"bionic_libc_platform_headers",
@@ -232,11 +206,13 @@
"libcutils",
"liblog",
],
+ runtime_libs: [
+ "libdexfile", // libdexfile_support dependency
+ ],
whole_static_libs: [
"libasync_safe",
"gwp_asan_crash_handler",
- "libscudo",
"libtombstone_proto",
"libprocinfo",
"libprotobuf-cpp-lite",
@@ -247,11 +223,17 @@
exclude_static_libs: [
"libdexfile_support",
],
+ exclude_runtime_libs: [
+ "libdexfile",
+ ],
},
vendor_ramdisk: {
exclude_static_libs: [
"libdexfile_support",
],
+ exclude_runtime_libs: [
+ "libdexfile",
+ ],
},
},
@@ -259,6 +241,13 @@
debuggable: {
cflags: ["-DROOT_POSSIBLE"],
},
+
+ malloc_not_svelte: {
+ cflags: ["-DUSE_SCUDO"],
+ whole_static_libs: ["libscudo"],
+ srcs: ["libdebuggerd/scudo.cpp"],
+ header_libs: ["scudo_headers"],
+ },
},
}
@@ -329,10 +318,6 @@
"gwp_asan_headers",
],
- include_dirs: [
- "external/scudo/standalone/include",
- ],
-
local_include_dirs: [
"libdebuggerd",
],
diff --git a/debuggerd/OWNERS b/debuggerd/OWNERS
index bfeedca..6f7e4a3 100644
--- a/debuggerd/OWNERS
+++ b/debuggerd/OWNERS
@@ -1,2 +1 @@
cferris@google.com
-jmgao@google.com
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
new file mode 100644
index 0000000..d5327db
--- /dev/null
+++ b/debuggerd/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "debuggerd_test"
+ }
+ ]
+}
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/crash_dump.cpp b/debuggerd/crash_dump.cpp
index a152740..5bb1d75 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -32,6 +32,7 @@
#include <set>
#include <vector>
+#include <android-base/errno_restorer.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -67,8 +68,9 @@
#include "protocol.h"
#include "util.h"
-using android::base::unique_fd;
+using android::base::ErrnoRestorer;
using android::base::StringPrintf;
+using android::base::unique_fd;
static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
struct stat st;
@@ -89,10 +91,11 @@
static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error, int flags = 0) {
if (ptrace(PTRACE_SEIZE, tid, 0, flags) != 0) {
if (errno == EPERM) {
- pid_t tracer = get_tracer(tid);
- if (tracer != -1) {
+ ErrnoRestorer errno_restorer; // In case get_tracer() fails and we fall through.
+ pid_t tracer_pid = get_tracer(tid);
+ if (tracer_pid > 0) {
*error = StringPrintf("failed to attach to thread %d, already traced by %d (%s)", tid,
- tracer, get_process_name(tracer).c_str());
+ tracer_pid, get_process_name(tracer_pid).c_str());
return false;
}
}
diff --git a/debuggerd/crash_test.cpp b/debuggerd/crash_test.cpp
index c15145f..ce0f91f 100644
--- a/debuggerd/crash_test.cpp
+++ b/debuggerd/crash_test.cpp
@@ -16,6 +16,13 @@
#include <stdint.h>
-extern "C" void crash() {
+#include "crash_test.h"
+
+extern "C" {
+
+JITDescriptor __dex_debug_descriptor = {.version = 1};
+
+void crash() {
*reinterpret_cast<volatile char*>(0xdead) = '1';
}
+}
diff --git a/debuggerd/crash_test.h b/debuggerd/crash_test.h
new file mode 100644
index 0000000..2a8bea3
--- /dev/null
+++ b/debuggerd/crash_test.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 <stdint.h>
+
+// Only support V1 of these structures.
+// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
+// for information on the JIT Compilation Interface.
+// Also, see libunwindstack/GlobalDebugImpl.h for the full definition of
+// these structures.
+struct JITCodeEntry {
+ uintptr_t next;
+ uintptr_t prev;
+ uintptr_t symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct JITDescriptor {
+ uint32_t version;
+ uint32_t action_flag;
+ uintptr_t relevant_entry;
+ uintptr_t first_entry;
+};
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 abda071..4394274 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -57,6 +57,7 @@
#include <libminijail.h>
#include <scoped_minijail.h>
+#include "crash_test.h"
#include "debuggerd/handler.h"
#include "libdebuggerd/utility.h"
#include "protocol.h"
@@ -961,6 +962,44 @@
ASSERT_MATCH(result, R"(Abort message: 'x{4045}')");
}
+TEST_F(CrasherTest, abort_message_newline_trimmed) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ android_set_abort_message("Message with a newline.\n");
+ abort();
+ });
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Abort message: 'Message with a newline.')");
+}
+
+TEST_F(CrasherTest, abort_message_multiple_newlines_trimmed) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ android_set_abort_message("Message with multiple newlines.\n\n\n\n\n");
+ abort();
+ });
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Abort message: 'Message with multiple newlines.')");
+}
+
TEST_F(CrasherTest, abort_message_backtrace) {
int intercept_result;
unique_fd output_fd;
@@ -1825,3 +1864,325 @@
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");
+}
+
+static std::string format_pointer(uintptr_t ptr) {
+#if defined(__LP64__)
+ return android::base::StringPrintf("%08x'%08x", static_cast<uint32_t>(ptr >> 32),
+ static_cast<uint32_t>(ptr & 0xffffffff));
+#else
+ return android::base::StringPrintf("%08x", static_cast<uint32_t>(ptr & 0xffffffff));
+#endif
+}
+
+static std::string format_pointer(void* ptr) {
+ return format_pointer(reinterpret_cast<uintptr_t>(ptr));
+}
+
+static std::string format_full_pointer(uintptr_t ptr) {
+#if defined(__LP64__)
+ return android::base::StringPrintf("%016" PRIx64, ptr);
+#else
+ return android::base::StringPrintf("%08x", ptr);
+#endif
+}
+
+static std::string format_full_pointer(void* ptr) {
+ return format_full_pointer(reinterpret_cast<uintptr_t>(ptr));
+}
+
+__attribute__((__noinline__)) int crash_call(uintptr_t ptr) {
+ int* crash_ptr = reinterpret_cast<int*>(ptr);
+ *crash_ptr = 1;
+ return *crash_ptr;
+}
+
+// Verify that a fault address before the first map is properly handled.
+TEST_F(CrasherTest, fault_address_before_first_map) {
+ StartProcess([]() {
+ ASSERT_EQ(0, crash_call(0x1024));
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x1024)");
+
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\):\n)");
+
+ std::string match_str = android::base::StringPrintf(
+ R"(memory map .*:\n--->Fault address falls at %s before any mapped regions\n )",
+ format_pointer(0x1024).c_str());
+ ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address after the last map is properly handled.
+TEST_F(CrasherTest, fault_address_after_last_map) {
+ uintptr_t crash_uptr = untag_address(UINTPTR_MAX - 15);
+ StartProcess([crash_uptr]() {
+ ASSERT_EQ(0, crash_call(crash_uptr));
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ std::string match_str = R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr )";
+ match_str += android::base::StringPrintf("0x%" PRIxPTR, crash_uptr);
+ ASSERT_MATCH(result, match_str);
+
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+ // Assumes that the open files section comes after the map section.
+ // If that assumption changes, the regex below needs to change.
+ match_str = android::base::StringPrintf(
+ R"(\n--->Fault address falls at %s after any mapped regions\n\nopen files:)",
+ format_pointer(crash_uptr).c_str());
+ ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address between maps is properly handled.
+TEST_F(CrasherTest, fault_address_between_maps) {
+ // Create a map before the fork so it will be present in the child.
+ void* start_ptr =
+ mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, start_ptr);
+ // Unmap the page in the middle.
+ void* middle_ptr =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + getpagesize());
+ ASSERT_EQ(0, munmap(middle_ptr, getpagesize()));
+
+ StartProcess([middle_ptr]() {
+ ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(middle_ptr)));
+ _exit(0);
+ });
+
+ // Unmap the two maps.
+ ASSERT_EQ(0, munmap(start_ptr, getpagesize()));
+ void* end_ptr =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + 2 * getpagesize());
+ ASSERT_EQ(0, munmap(end_ptr, getpagesize()));
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ std::string match_str = R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr )";
+ match_str += android::base::StringPrintf("%p", middle_ptr);
+ ASSERT_MATCH(result, match_str);
+
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+ match_str = android::base::StringPrintf(
+ R"( %s.*\n--->Fault address falls at %s between mapped regions\n %s)",
+ format_pointer(start_ptr).c_str(), format_pointer(middle_ptr).c_str(),
+ format_pointer(end_ptr).c_str());
+ ASSERT_MATCH(result, match_str);
+}
+
+// Verify that a fault address happens in the correct map.
+TEST_F(CrasherTest, fault_address_in_map) {
+ // Create a map before the fork so it will be present in the child.
+ void* ptr = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, ptr);
+
+ StartProcess([ptr]() {
+ ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(ptr)));
+ _exit(0);
+ });
+
+ ASSERT_EQ(0, munmap(ptr, getpagesize()));
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ std::string match_str = R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\), fault addr )";
+ match_str += android::base::StringPrintf("%p", ptr);
+ ASSERT_MATCH(result, match_str);
+
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+
+ match_str = android::base::StringPrintf(R"(\n--->%s.*\n)", format_pointer(ptr).c_str());
+ ASSERT_MATCH(result, match_str);
+}
+
+static constexpr uint32_t kDexData[] = {
+ 0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+ 0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+ 0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+ 0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+ 0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+ 0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+ 0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+ 0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+ 0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+ 0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+ 0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+ 0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+ 0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+ 0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+ 0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+ 0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+ 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+TEST_F(CrasherTest, verify_dex_pc_with_function_name) {
+ StartProcess([]() {
+ TemporaryDir td;
+ std::string tmp_so_name;
+ if (!CopySharedLibrary(td.path, &tmp_so_name)) {
+ _exit(1);
+ }
+
+ // In order to cause libunwindstack to look for this __dex_debug_descriptor
+ // move the library to which has a basename of libart.so.
+ std::string art_so_name = android::base::Dirname(tmp_so_name) + "/libart.so";
+ ASSERT_EQ(0, rename(tmp_so_name.c_str(), art_so_name.c_str()));
+ void* handle = dlopen(art_so_name.c_str(), RTLD_NOW | RTLD_LOCAL);
+ if (handle == nullptr) {
+ _exit(1);
+ }
+
+ void* ptr =
+ mmap(nullptr, sizeof(kDexData), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_TRUE(ptr != MAP_FAILED);
+ memcpy(ptr, kDexData, sizeof(kDexData));
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, sizeof(kDexData), "dex");
+
+ JITCodeEntry dex_entry = {.symfile_addr = reinterpret_cast<uintptr_t>(ptr),
+ .symfile_size = sizeof(kDexData)};
+
+ JITDescriptor* dex_debug =
+ reinterpret_cast<JITDescriptor*>(dlsym(handle, "__dex_debug_descriptor"));
+ ASSERT_TRUE(dex_debug != nullptr);
+ dex_debug->version = 1;
+ dex_debug->action_flag = 0;
+ dex_debug->relevant_entry = 0;
+ dex_debug->first_entry = reinterpret_cast<uintptr_t>(&dex_entry);
+
+ // This sets the magic dex pc value for register 0, using the value
+ // of register 1 + 0x102.
+ asm(".cfi_escape "
+ "0x16 /* DW_CFA_val_expression */, 0, 0x0a /* size */,"
+ "0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = 'DEX1' */"
+ "0x13 /* DW_OP_drop */,"
+ "0x92 /* DW_OP_bregx */, 1, 0x82, 0x02 /* 2-byte SLEB128 */");
+
+ // For each different architecture, set register one to the dex ptr mmap
+ // created above. Then do a nullptr dereference to force a crash.
+#if defined(__arm__)
+ asm volatile(
+ "mov r1, %[base]\n"
+ "mov r2, 0\n"
+ "str r3, [r2]\n"
+ : [base] "+r"(ptr)
+ :
+ : "r1", "r2", "r3", "memory");
+#elif defined(__aarch64__)
+ asm volatile(
+ "mov x1, %[base]\n"
+ "mov x2, 0\n"
+ "str x3, [x2]\n"
+ : [base] "+r"(ptr)
+ :
+ : "x1", "x2", "x3", "memory");
+#elif defined(__i386__)
+ asm volatile(
+ "mov %[base], %%ecx\n"
+ "movl $0, %%edi\n"
+ "movl 0(%%edi), %%edx\n"
+ : [base] "+r"(ptr)
+ :
+ : "edi", "ecx", "edx", "memory");
+#elif defined(__x86_64__)
+ asm volatile(
+ "mov %[base], %%rdx\n"
+ "movq 0, %%rdi\n"
+ "movq 0(%%rdi), %%rcx\n"
+ : [base] "+r"(ptr)
+ :
+ : "rcx", "rdx", "rdi", "memory");
+#else
+#error "Unsupported architecture"
+#endif
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ // Verify the process crashed properly.
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0)");
+
+ // Now verify that the dex_pc frame includes a proper function name.
+ ASSERT_MATCH(result, R"( \[anon:dex\] \(Main\.\<init\>\+2)");
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index f615780..baf994f 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -1,29 +1,17 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
- * All rights reserved.
+ * Copyright 2017 The Android Open Source Project
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
+ * 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
*
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * 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 <dirent.h>
diff --git a/debuggerd/handler/debuggerd_fallback_nop.cpp b/debuggerd/handler/debuggerd_fallback_nop.cpp
index 331301f..5666d98 100644
--- a/debuggerd/handler/debuggerd_fallback_nop.cpp
+++ b/debuggerd/handler/debuggerd_fallback_nop.cpp
@@ -1,29 +1,17 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
- * All rights reserved.
+ * Copyright 2017 The Android Open Source Project
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
+ * 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
*
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * 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.
*/
extern "C" void debuggerd_fallback_handler(struct siginfo_t*, struct ucontext_t*, void*) {
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index b607397..35be2bf 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -1,29 +1,17 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
+ * Copyright 2008 The Android Open Source Project
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
+ * 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
*
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * 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 "debuggerd/handler.h"
@@ -167,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;
}
@@ -193,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",
@@ -544,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/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index a14dcb0..19c1f5e 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -332,7 +332,7 @@
ASSERT_STREQ("", tombstone_contents.c_str());
ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+ ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied (EACCES)\n\n",
getFakeLogPrint().c_str());
ASSERT_STREQ("", amfd_data_.c_str());
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 9c01f15..534d7be 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -36,6 +36,7 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -57,10 +58,13 @@
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/gwp_asan.h"
#include "libdebuggerd/open_files_list.h"
-#include "libdebuggerd/scudo.h"
#include "libdebuggerd/utility.h"
#include "util.h"
+#if defined(USE_SCUDO)
+#include "libdebuggerd/scudo.h"
+#endif
+
#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
@@ -115,8 +119,26 @@
return "";
}
-static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps,
- unwindstack::Regs* regs) {
+static void dump_probable_cause(log_t* log, unwindstack::Unwinder* unwinder,
+ const ProcessInfo& process_info, const ThreadInfo& main_thread) {
+#if defined(USE_SCUDO)
+ ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
+ if (scudo_crash_data.CrashIsMine()) {
+ scudo_crash_data.DumpCause(log, unwinder);
+ return;
+ }
+#endif
+
+ GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
+ main_thread);
+ if (gwp_asan_crash_data.CrashIsMine()) {
+ gwp_asan_crash_data.DumpCause(log);
+ return;
+ }
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::Regs* regs = main_thread.registers.get();
+ const siginfo_t* si = main_thread.siginfo;
std::string cause;
if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -232,6 +254,12 @@
return;
}
+ // Remove any trailing newlines.
+ size_t index = length;
+ while (index > 0 && (msg[index - 1] == '\0' || msg[index - 1] == '\n')) {
+ --index;
+ }
+ msg[index] = '\0';
_LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
}
@@ -389,22 +417,11 @@
dump_signal_info(log, thread_info, process_info, unwinder->GetProcessMemory().get());
}
- std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
- std::unique_ptr<ScudoCrashData> scudo_crash_data;
if (primary_thread) {
- gwp_asan_crash_data = std::make_unique<GwpAsanCrashData>(unwinder->GetProcessMemory().get(),
- process_info, thread_info);
- scudo_crash_data =
- std::make_unique<ScudoCrashData>(unwinder->GetProcessMemory().get(), process_info);
- }
+ // The main thread must have a valid siginfo.
+ CHECK(thread_info.siginfo != nullptr);
+ dump_probable_cause(log, unwinder, process_info, thread_info);
- if (primary_thread && gwp_asan_crash_data->CrashIsMine()) {
- gwp_asan_crash_data->DumpCause(log);
- } else if (thread_info.siginfo && !(primary_thread && scudo_crash_data->CrashIsMine())) {
- dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
- }
-
- if (primary_thread) {
dump_abort_message(log, unwinder->GetProcessMemory().get(), process_info.abort_msg_address);
}
@@ -426,16 +443,17 @@
}
if (primary_thread) {
- if (gwp_asan_crash_data->HasDeallocationTrace()) {
- gwp_asan_crash_data->DumpDeallocationTrace(log, unwinder);
+ GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
+ thread_info);
+
+ if (gwp_asan_crash_data.HasDeallocationTrace()) {
+ gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
}
- if (gwp_asan_crash_data->HasAllocationTrace()) {
- gwp_asan_crash_data->DumpAllocationTrace(log, unwinder);
+ if (gwp_asan_crash_data.HasAllocationTrace()) {
+ gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
}
- scudo_crash_data->DumpCause(log, unwinder);
-
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index ff12017..6c380a1 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -18,7 +18,9 @@
#include "libdebuggerd/tombstone.h"
#include "libdebuggerd/gwp_asan.h"
+#if defined(USE_SCUDO)
#include "libdebuggerd/scudo.h"
+#endif
#include <errno.h>
#include <fcntl.h>
@@ -28,6 +30,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/sysinfo.h>
#include <time.h>
#include <memory>
@@ -37,6 +40,7 @@
#include <async_safe/log.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -185,11 +189,13 @@
static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
const ProcessInfo& process_info, const ThreadInfo& main_thread) {
+#if defined(USE_SCUDO)
ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
if (scudo_crash_data.CrashIsMine()) {
scudo_crash_data.AddCauseProtos(tombstone, unwinder);
return;
}
+#endif
GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
main_thread);
@@ -271,6 +277,13 @@
return;
}
+ // Remove any trailing newlines.
+ size_t index = msg.size();
+ while (index > 0 && (msg[index - 1] == '\0' || msg[index - 1] == '\n')) {
+ --index;
+ }
+ msg.resize(index);
+
tombstone->set_abort_message(msg);
}
@@ -589,14 +602,6 @@
}
}
-static std::optional<uint64_t> read_uptime_secs() {
- std::string uptime;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
- return {};
- }
- return strtoll(uptime.c_str(), nullptr, 10);
-}
-
void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, const OpenFilesList* open_files) {
@@ -607,27 +612,25 @@
result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
result.set_timestamp(get_timestamp());
- std::optional<uint64_t> system_uptime = read_uptime_secs();
- if (system_uptime) {
- android::procinfo::ProcessInfo proc_info;
- std::string error;
- if (android::procinfo::GetProcessInfo(target_thread, &proc_info, &error)) {
- uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
- result.set_process_uptime(*system_uptime - starttime);
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
- error.c_str());
- }
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read /proc/uptime: %s",
- strerror(errno));
- }
-
const ThreadInfo& main_thread = threads.at(target_thread);
result.set_pid(main_thread.pid);
result.set_tid(main_thread.tid);
result.set_uid(main_thread.uid);
result.set_selinux_label(main_thread.selinux_label);
+ // The main thread must have a valid siginfo.
+ CHECK(main_thread.siginfo != nullptr);
+
+ struct sysinfo si;
+ sysinfo(&si);
+ android::procinfo::ProcessInfo proc_info;
+ std::string error;
+ if (android::procinfo::GetProcessInfo(main_thread.pid, &proc_info, &error)) {
+ uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
+ result.set_process_uptime(si.uptime - starttime);
+ } else {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
+ error.c_str());
+ }
auto cmd_line = result.mutable_command_line();
for (const auto& arg : main_thread.command_line) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 053299a..681b963 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -362,8 +362,13 @@
print_thread_memory_dump(callback, tombstone, thread);
CBS("");
- CBS("memory map (%d %s):", tombstone.memory_mappings().size(),
- tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+
+ // No memory maps to print.
+ if (tombstone.memory_mappings().empty()) {
+ CBS("No memory maps found");
+ return;
+ }
+
int word_size = pointer_width(tombstone);
const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
if (word_size == 8) {
@@ -375,8 +380,41 @@
return StringPrintf("%0*" PRIx64, word_size * 2, ptr);
};
+ std::string memory_map_header =
+ StringPrintf("memory map (%d %s):", tombstone.memory_mappings().size(),
+ tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+
+ bool has_fault_address = signal_info.has_fault_address();
+ uint64_t fault_address = untag_address(signal_info.fault_address());
+ bool preamble_printed = false;
+ bool printed_fault_address_marker = false;
for (const auto& map : tombstone.memory_mappings()) {
+ if (!preamble_printed) {
+ preamble_printed = true;
+ if (has_fault_address) {
+ if (fault_address < map.begin_address()) {
+ memory_map_header +=
+ StringPrintf("\n--->Fault address falls at %s before any mapped regions",
+ format_pointer(fault_address).c_str());
+ printed_fault_address_marker = true;
+ } else {
+ memory_map_header += " (fault address prefixed with --->)";
+ }
+ }
+ CBS("%s", memory_map_header.c_str());
+ }
+
std::string line = " ";
+ if (has_fault_address && !printed_fault_address_marker) {
+ if (fault_address < map.begin_address()) {
+ printed_fault_address_marker = true;
+ CBS("--->Fault address falls at %s between mapped regions",
+ format_pointer(fault_address).c_str());
+ } else if (fault_address >= map.begin_address() && fault_address < map.end_address()) {
+ printed_fault_address_marker = true;
+ line = "--->";
+ }
+ }
StringAppendF(&line, "%s-%s", format_pointer(map.begin_address()).c_str(),
format_pointer(map.end_address() - 1).c_str());
StringAppendF(&line, " %s%s%s", map.read() ? "r" : "-", map.write() ? "w" : "-",
@@ -398,6 +436,11 @@
CBS("%s", line.c_str());
}
+
+ if (has_fault_address && !printed_fault_address_marker) {
+ CBS("--->Fault address falls at %s after any mapped regions",
+ format_pointer(fault_address).c_str());
+ }
}
void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
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/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 4eac0e9..8fd03c4 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 21887ab..858a338 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 90843fc..152697c 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -25,6 +25,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 4eac0e9..8fd03c4 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 1585cc6..281e231 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 05d8050..50558f7 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -449,7 +449,7 @@
}
if (crash->output.text.fd == -1) {
- LOG(WARNING) << "missing output fd";
+ LOG(WARNING) << "skipping tombstone file creation due to intercept";
return;
}
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/diagnose_usb/OWNERS b/diagnose_usb/OWNERS
index 643b448..fcd7757 100644
--- a/diagnose_usb/OWNERS
+++ b/diagnose_usb/OWNERS
@@ -1,2 +1 @@
-jmgao@google.com
yabinc@google.com
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index a72ee07..58b2a81 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,4 +1,3 @@
dvander@google.com
hridya@google.com
enh@google.com
-jmgao@google.com
diff --git a/fastboot/README.md b/fastboot/README.md
index c224448..d3b6c1a 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -27,16 +27,16 @@
1. Host sends a command, which is an ascii string in a single
packet no greater than 64 bytes.
-2. Client response with a single packet no greater than 64 bytes.
+2. Client response with a single packet no greater than 256 bytes.
The first four bytes of the response are "OKAY", "FAIL", "DATA",
or "INFO". Additional bytes may contain an (ascii) informative
message.
- a. INFO -> the remaining 60 bytes are an informative message
+ a. INFO -> the remaining 252 bytes are an informative message
(providing progress or diagnostic messages). They should
be displayed and then step #2 repeats
- b. FAIL -> the requested command failed. The remaining 60 bytes
+ b. FAIL -> the requested command failed. The remaining 252 bytes
of the response (if present) provide a textual failure message
to present to the user. Stop.
@@ -53,13 +53,13 @@
until the client has sent or received the number of bytes indicated
in the "DATA" response above.
-4. Client responds with a single packet no greater than 64 bytes.
+4. Client responds with a single packet no greater than 256 bytes.
The first four bytes of the response are "OKAY", "FAIL", or "INFO".
Similar to #2:
- a. INFO -> display the remaining 60 bytes and return to #4
+ a. INFO -> display the remaining 252 bytes and return to #4
- b. FAIL -> display the remaining 60 bytes (if present) as a failure
+ b. FAIL -> display the remaining 252 bytes (if present) as a failure
reason and consider the command failed. Stop.
c. OKAY -> success. Go to #5
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6a49fdf..532b524 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -104,8 +104,6 @@
static bool g_disable_verity = false;
static bool g_disable_verification = false;
-static const std::string convert_fbe_marker_filename("convert_fbe");
-
fastboot::FastBootDriver* fb = nullptr;
enum fb_buffer_type {
@@ -429,7 +427,7 @@
" snapshot-update merge On devices that support snapshot-based updates, finish\n"
" an in-progress update if it is in the \"merging\"\n"
" phase.\n"
- " fetch PARTITION Fetch a partition image from the device."
+ " fetch PARTITION OUT_FILE Fetch a partition image from the device."
"\n"
"boot image:\n"
" boot KERNEL [RAMDISK [SECOND]]\n"
@@ -473,9 +471,6 @@
" --disable-verification Sets disable-verification when flashing vbmeta.\n"
" --fs-options=OPTION[,OPTION]\n"
" Enable filesystem features. OPTION supports casefold, projid, compress\n"
-#if !defined(_WIN32)
- " --wipe-and-use-fbe Enable file-based encryption, wiping userdata.\n"
-#endif
// TODO: remove --unbuffered?
" --unbuffered Don't buffer input or output.\n"
" --verbose, -v Verbose output.\n"
@@ -593,10 +588,6 @@
#define tmpfile win32_tmpfile
-static std::string make_temporary_directory() {
- die("make_temporary_directory not supported under Windows, sorry!");
-}
-
static int make_temporary_fd(const char* /*what*/) {
// TODO: reimplement to avoid leaking a FILE*.
return fileno(tmpfile());
@@ -610,15 +601,6 @@
return std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
}
-static std::string make_temporary_directory() {
- std::string result(make_temporary_template());
- if (mkdtemp(&result[0]) == nullptr) {
- die("unable to create temporary directory with template %s: %s",
- result.c_str(), strerror(errno));
- }
- return result;
-}
-
static int make_temporary_fd(const char* what) {
std::string path_template(make_temporary_template());
int fd = mkstemp(&path_template[0]);
@@ -632,32 +614,6 @@
#endif
-static std::string create_fbemarker_tmpdir() {
- std::string dir = make_temporary_directory();
- std::string marker_file = dir + "/" + convert_fbe_marker_filename;
- int fd = open(marker_file.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0666);
- if (fd == -1) {
- die("unable to create FBE marker file %s locally: %s",
- marker_file.c_str(), strerror(errno));
- }
- close(fd);
- return dir;
-}
-
-static void delete_fbemarker_tmpdir(const std::string& dir) {
- std::string marker_file = dir + "/" + convert_fbe_marker_filename;
- if (unlink(marker_file.c_str()) == -1) {
- fprintf(stderr, "Unable to delete FBE marker file %s locally: %d, %s\n",
- marker_file.c_str(), errno, strerror(errno));
- return;
- }
- if (rmdir(dir.c_str()) == -1) {
- fprintf(stderr, "Unable to delete FBE marker directory %s locally: %d, %s\n",
- dir.c_str(), errno, strerror(errno));
- return;
- }
-}
-
static unique_fd unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
@@ -985,7 +941,8 @@
// Tries to locate top-level vbmeta from boot.img footer.
uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
- die("Failed to find AVB_FOOTER at offset: %" PRId64, footer_offset);
+ die("Failed to find AVB_FOOTER at offset: %" PRId64 ", is BOARD_AVB_ENABLE true?",
+ footer_offset);
}
const AvbFooter* footer = reinterpret_cast<const AvbFooter*>(data.c_str() + footer_offset);
vbmeta_offset = be64toh(footer->vbmeta_offset);
@@ -1065,6 +1022,24 @@
return;
}
+ // If overflows and negative, it should be < buf->sz.
+ int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
+
+ if (partition_size == buf->sz) {
+ return;
+ }
+ // Some device bootloaders might not implement `fastboot getvar partition-size:boot[_a|_b]`.
+ // In this case, partition_size will be zero.
+ if (partition_size < buf->sz) {
+ fprintf(stderr,
+ "Warning: skip copying boot image avb footer"
+ " (boot partition size: %" PRId64 ", boot image size: %" PRId64 ").\n",
+ partition_size, buf->sz);
+ return;
+ }
+
+ // IMPORTANT: after the following read, we need to reset buf->fd before return (if not die).
+ // Because buf->fd will still be used afterwards.
std::string data;
if (!android::base::ReadFdToString(buf->fd, &data)) {
die("Failed reading from boot");
@@ -1072,17 +1047,9 @@
uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
+ lseek(buf->fd.get(), 0, SEEK_SET); // IMPORTANT: resets buf->fd before return.
return;
}
- // If overflows and negative, it should be < buf->sz.
- int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
-
- if (partition_size == buf->sz) {
- return;
- }
- if (partition_size < buf->sz) {
- die("boot partition is smaller than boot image");
- }
unique_fd fd(make_temporary_fd("boot rewriting"));
if (!android::base::WriteStringToFd(data, fd)) {
@@ -1895,7 +1862,6 @@
bool skip_reboot = false;
bool wants_set_active = false;
bool skip_secondary = false;
- bool set_fbe_marker = false;
bool force_flash = false;
unsigned fs_options = 0;
int longindex;
@@ -1933,9 +1899,6 @@
{"unbuffered", no_argument, 0, 0},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 0},
-#if !defined(_WIN32)
- {"wipe-and-use-fbe", no_argument, 0, 0},
-#endif
{0, 0, 0, 0}
};
@@ -1989,11 +1952,6 @@
fprintf(stdout, "fastboot version %s-%s\n", PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str());
fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
return 0;
-#if !defined(_WIN32)
- } else if (name == "wipe-and-use-fbe") {
- wants_wipe = true;
- set_fbe_marker = true;
-#endif
} else {
die("unknown option %s", longopts[longindex].name);
}
@@ -2305,14 +2263,7 @@
}
if (partition_type.empty()) continue;
fb->Erase(partition);
- if (partition == "userdata" && set_fbe_marker) {
- fprintf(stderr, "setting FBE marker on initial userdata...\n");
- std::string initial_userdata_dir = create_fbemarker_tmpdir();
- fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir, fs_options);
- delete_fbemarker_tmpdir(initial_userdata_dir);
- } else {
- fb_perform_format(partition, 1, partition_type, "", "", fs_options);
- }
+ fb_perform_format(partition, 1, partition_type, "", "", fs_options);
}
}
if (wants_set_active) {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 458a7a1..d268a50 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -143,6 +143,13 @@
mke2fs_args.push_back("512");
}
+ if (fsOptions & (1 << FS_OPT_CASEFOLD)) {
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("casefold");
+ mke2fs_args.push_back("-E");
+ mke2fs_args.push_back("encoding=utf8");
+ }
+
mke2fs_args.push_back(fileName);
std::string size_str = std::to_string(partSize / block_size);
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index 5a14b63..3096905 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -28,6 +28,10 @@
#include "socket.h"
+#ifndef _WIN32
+#include <sys/select.h>
+#endif
+
#include <android-base/errors.h>
#include <android-base/stringprintf.h>
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3c83aab..cb74ae0 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 {
@@ -125,10 +127,19 @@
export_header_lib_headers: [
"libfiemap_headers",
],
- required: [
- "e2freefrag",
- "e2fsdroid",
- ],
+ target: {
+ platform: {
+ required: [
+ "e2freefrag",
+ "e2fsdroid",
+ ],
+ },
+ recovery: {
+ required: [
+ "e2fsdroid.recovery",
+ ],
+ },
+ },
}
// Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.
@@ -142,6 +153,8 @@
// Do not ever allow this library to be vendor_available as a shared library.
// It does not have a stable interface.
name: "libfs_mgr",
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
defaults: [
"libfs_mgr_defaults",
@@ -166,6 +179,8 @@
// It does not have a stable interface.
name: "libfstab",
vendor_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
defaults: ["fs_mgr_defaults"],
@@ -200,7 +215,6 @@
static_libs: [
"libavb_user",
"libgsid",
- "libutils",
"libvold_binder",
],
shared_libs: [
@@ -215,6 +229,7 @@
"liblog",
"liblp",
"libselinux",
+ "libutils",
],
header_libs: [
"libcutils_headers",
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/OWNERS b/fs_mgr/OWNERS
index cf353a1..c6f9054 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,2 +1,4 @@
+# Bug component: 30545
bowgotsai@google.com
dvander@google.com
+elsk@google.com
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 84709b6..432aa4f 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"presubmit": [
{
+ "name": "CtsFsMgrTestCases"
+ },
+ {
"name": "libdm_test"
},
{
@@ -13,6 +16,9 @@
"name": "fiemap_writer_test"
},
{
+ "name": "fs_mgr_vendor_overlay_test"
+ },
+ {
"name": "vts_libsnapshot_test"
},
{
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 21df8af..002b302 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -34,11 +34,13 @@
#include <time.h>
#include <unistd.h>
+#include <array>
#include <chrono>
#include <functional>
#include <map>
#include <memory>
#include <string>
+#include <string_view>
#include <thread>
#include <utility>
#include <vector>
@@ -102,6 +104,7 @@
using android::base::Realpath;
using android::base::SetProperty;
using android::base::StartsWith;
+using android::base::StringPrintf;
using android::base::Timer;
using android::base::unique_fd;
using android::dm::DeviceMapper;
@@ -791,20 +794,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
@@ -815,7 +824,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) {
@@ -2039,6 +2048,35 @@
return 0;
}
+static bool ConfigureIoScheduler(const std::string& device_path) {
+ if (!StartsWith(device_path, "/dev/")) {
+ LERROR << __func__ << ": invalid argument " << device_path;
+ return false;
+ }
+
+ const std::string iosched_path =
+ StringPrintf("/sys/block/%s/queue/scheduler", Basename(device_path).c_str());
+ unique_fd iosched_fd(open(iosched_path.c_str(), O_RDWR | O_CLOEXEC));
+ if (iosched_fd.get() == -1) {
+ PERROR << __func__ << ": failed to open " << iosched_path;
+ return false;
+ }
+
+ // Kernels before v4.1 only support 'noop'. Kernels [v4.1, v5.0) support
+ // 'noop' and 'none'. Kernels v5.0 and later only support 'none'.
+ static constexpr const std::array<std::string_view, 2> kNoScheduler = {"none", "noop"};
+
+ for (const std::string_view& scheduler : kNoScheduler) {
+ int ret = write(iosched_fd.get(), scheduler.data(), scheduler.size());
+ if (ret > 0) {
+ return true;
+ }
+ }
+
+ PERROR << __func__ << ": failed to write to " << iosched_path;
+ return false;
+}
+
static bool InstallZramDevice(const std::string& device) {
if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {
PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV;
@@ -2071,6 +2109,8 @@
return false;
}
+ ConfigureIoScheduler(loop_device);
+
ConfigureQueueDepth(loop_device, "/");
// set block size & direct IO
@@ -2095,7 +2135,7 @@
}
if (entry.zram_size > 0) {
- if (!PrepareZramBackingDevice(entry.zram_backingdev_size)) {
+ if (!PrepareZramBackingDevice(entry.zram_backingdev_size)) {
LERROR << "Failure of zram backing device file for '" << entry.blk_device << "'";
}
// A zram_size was specified, so we need to configure the
@@ -2191,16 +2231,16 @@
return false;
}
-std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry) {
+std::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry) {
if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
- return "";
+ return {};
}
DeviceMapper& dm = DeviceMapper::Instance();
std::string device = GetVerityDeviceName(entry);
std::vector<DeviceMapper::TargetInfo> table;
if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
- return "";
+ return {};
}
for (const auto& target : table) {
if (strcmp(target.spec.target_type, "verity") != 0) {
@@ -2216,14 +2256,15 @@
std::vector<std::string> tokens = android::base::Split(target.data, " \t\r\n");
if (tokens[0] != "0" && tokens[0] != "1") {
LOG(WARNING) << "Unrecognized device mapper version in " << target.data;
- return "";
+ return {};
}
- // Hashtree algorithm is the 8th token in the output
- return android::base::Trim(tokens[7]);
+ // Hashtree algorithm & root digest are the 8th & 9th token in the output.
+ return HashtreeInfo{.algorithm = android::base::Trim(tokens[7]),
+ .root_digest = android::base::Trim(tokens[8])};
}
- return "";
+ return {};
}
bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
@@ -2325,7 +2366,24 @@
return false;
}
- auto options = "lowerdir=" + entry.lowerdir;
+ auto lowerdir = entry.lowerdir;
+ if (entry.fs_mgr_flags.overlayfs_remove_missing_lowerdir) {
+ bool removed_any = false;
+ std::vector<std::string> lowerdirs;
+ for (const auto& dir : android::base::Split(entry.lowerdir, ":")) {
+ if (access(dir.c_str(), F_OK)) {
+ PWARNING << __FUNCTION__ << "(): remove missing lowerdir '" << dir << "'";
+ removed_any = true;
+ } else {
+ lowerdirs.push_back(dir);
+ }
+ }
+ if (removed_any) {
+ lowerdir = android::base::Join(lowerdirs, ":");
+ }
+ }
+
+ auto options = "lowerdir=" + lowerdir;
if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
options += ",override_creds=off";
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f5ab557..9b6c3dd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -181,6 +181,7 @@
CheckFlag("fsverity", fs_verity);
CheckFlag("metadata_csum", ext_meta_csum);
CheckFlag("fscompress", fs_compress);
+ CheckFlag("overlayfs_remove_missing_lowerdir", overlayfs_remove_missing_lowerdir);
#undef CheckFlag
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 4d32bda..0522ac5 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1133,7 +1133,7 @@
return false;
}
if (!images->BackingImageExists(partition_name)) {
- static constexpr uint64_t kMinimumSize = 16_MiB;
+ static constexpr uint64_t kMinimumSize = 64_MiB;
static constexpr uint64_t kMaximumSize = 2_GiB;
uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index e685070..5411aca 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -420,7 +420,8 @@
break;
}
// Find overlayfs mount point?
- if ((mount_point == "/") && (rentry.mount_point == "/system")) {
+ if ((mount_point == "/" && rentry.mount_point == "/system") ||
+ (mount_point == "/system" && rentry.mount_point == "/")) {
blk_device = rentry.blk_device;
mount_point = "/system";
found = true;
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index fdaffbe..d275320 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "android-base/file.h"
#include "fs_mgr/roots.h"
#include <sys/mount.h>
@@ -39,18 +40,26 @@
while (true) {
auto entry = GetEntryForMountPoint(fstab, str);
if (entry != nullptr) return entry;
- if (str == "/") break;
- auto slash = str.find_last_of('/');
- if (slash == std::string::npos) break;
- if (slash == 0) {
- str = "/";
- } else {
- str = str.substr(0, slash);
- }
+ str = android::base::Dirname(str);
+ if (!str.compare(".") || !str.compare("/")) break;
}
return nullptr;
}
+std::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path) {
+ std::vector<FstabEntry*> entries;
+ if (path.empty()) return entries;
+
+ std::string str(path);
+ while (true) {
+ entries = GetEntriesForMountPoint(fstab, str);
+ if (!entries.empty()) return entries;
+ str = android::base::Dirname(str);
+ if (!str.compare(".") || !str.compare("/")) break;
+ }
+ return entries;
+}
+
enum class MountState {
ERROR = -1,
NOT_MOUNTED = 0,
@@ -71,12 +80,7 @@
return MountState::NOT_MOUNTED;
}
-bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
- auto rec = GetEntryForPath(fstab, path);
- if (rec == nullptr) {
- LERROR << "unknown volume for path [" << path << "]";
- return false;
- }
+bool TryPathMount(FstabEntry* rec, const std::string& mount_pt) {
if (rec->fs_type == "ramdisk") {
// The ramdisk is always mounted.
return true;
@@ -136,6 +140,21 @@
return true;
}
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point) {
+ auto entries = GetEntriesForPath(fstab, path);
+ if (entries.empty()) {
+ LERROR << "unknown volume for path [" << path << "]";
+ return false;
+ }
+
+ for (auto entry : entries) {
+ if (TryPathMount(entry, mount_point)) return true;
+ }
+
+ LERROR << "Failed to mount for path [" << path << "]";
+ return false;
+}
+
bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
auto rec = GetEntryForPath(fstab, path);
if (rec == nullptr) {
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 4d3ecc9..21c9989 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,6 +22,7 @@
#include <linux/dm-ioctl.h>
#include <functional>
+#include <optional>
#include <string>
#include <fstab/fstab.h>
@@ -68,6 +69,13 @@
bool userdata_mounted;
};
+struct HashtreeInfo {
+ // The hash algorithm used to build the merkle tree.
+ std::string algorithm;
+ // The root digest of the merkle tree.
+ std::string root_digest;
+};
+
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
// Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
// defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
@@ -88,9 +96,9 @@
bool fs_mgr_load_verity_state(int* mode);
// Returns true if verity is enabled on this particular FstabEntry.
bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
-// Returns the hash algorithm used to build the hashtree of this particular FstabEntry. Returns an
-// empty string if the input isn't a dm-verity entry, or if there is an error.
-std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry);
+// Returns the verity hashtree information of this particular FstabEntry. Returns std::nullopt
+// if the input isn't a dm-verity entry, or if there is an error.
+std::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry);
bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
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/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index f33768b..9a4ed46 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -86,6 +86,7 @@
bool fs_verity : 1;
bool ext_meta_csum : 1;
bool fs_compress : 1;
+ bool overlayfs_remove_missing_lowerdir : 1;
} fs_mgr_flags = {};
bool is_encryptable() const {
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 428a7f4..2bb9035 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -86,7 +86,9 @@
name: "vts_libdm_test",
defaults: ["libdm_test_defaults"],
test_suites: ["vts"],
- test_min_api_level: 29,
+ test_options: {
+ min_shipping_api_level: 29,
+ },
}
cc_fuzz {
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index b1d5b39..4034e30 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -647,5 +647,61 @@
return spec.target_type == "snapshot"s && data == "Overflow"s;
}
+// Find directories in format of "/sys/block/dm-X".
+static int DmNameFilter(const dirent* de) {
+ if (android::base::StartsWith(de->d_name, "dm-")) {
+ return 1;
+ }
+ return 0;
+}
+
+std::map<std::string, std::string> DeviceMapper::FindDmPartitions() {
+ static constexpr auto DM_PATH_PREFIX = "/sys/block/";
+ dirent** namelist;
+ int n = scandir(DM_PATH_PREFIX, &namelist, DmNameFilter, alphasort);
+ if (n == -1) {
+ PLOG(ERROR) << "Failed to scan dir " << DM_PATH_PREFIX;
+ return {};
+ }
+ if (n == 0) {
+ LOG(ERROR) << "No dm block device found.";
+ free(namelist);
+ return {};
+ }
+
+ static constexpr auto DM_PATH_SUFFIX = "/dm/name";
+ static constexpr auto DEV_PATH = "/dev/block/";
+ std::map<std::string, std::string> dm_block_devices;
+ while (n--) {
+ std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ PLOG(WARNING) << "Failed to read " << path;
+ } else {
+ std::string dm_block_name = android::base::Trim(content);
+ // AVB is using 'vroot' for the root block device but we're expecting 'system'.
+ if (dm_block_name == "vroot") {
+ dm_block_name = "system";
+ } else if (android::base::EndsWith(dm_block_name, "-verity")) {
+ auto npos = dm_block_name.rfind("-verity");
+ dm_block_name = dm_block_name.substr(0, npos);
+ } else if (!android::base::GetProperty("ro.boot.avb_version", "").empty()) {
+ // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices,
+ // if DAP is enabled, then a -verity suffix must be used to
+ // differentiate between dm-linear and dm-verity devices. If we get
+ // here, we're AVB 2 and looking at a non-verity partition.
+ free(namelist[n]);
+ continue;
+ }
+
+ dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name));
+ }
+ free(namelist[n]);
+ }
+ free(namelist);
+
+ return dm_block_devices;
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index b0639e6..90d91a0 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -109,6 +109,10 @@
optional_args_.emplace_back("ignore_zero_blocks");
}
+void DmTargetVerity::CheckAtMostOnce() {
+ optional_args_.emplace_back("check_at_most_once");
+}
+
std::string DmTargetVerity::GetParameterString() const {
std::string base = android::base::Join(base_args_, " ");
if (optional_args_.empty()) {
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 8314ec5..541f254 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -684,13 +684,9 @@
TEST(libdm, CreateEmptyDevice) {
DeviceMapper& dm = DeviceMapper::Instance();
ASSERT_TRUE(dm.CreateEmptyDevice("empty-device"));
- auto guard = android::base::make_scope_guard([&]() { dm.DeleteDevice("empty-device", 5s); });
+ 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"));
-
- std::string path;
- ASSERT_TRUE(dm.WaitForDevice("empty-device", 5s, &path));
- // Path should exist.
- ASSERT_EQ(0, access(path.c_str(), F_OK));
}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index e6698ea..332fcf5 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -17,6 +17,7 @@
#ifndef _LIBDM_DM_H_
#define _LIBDM_DM_H_
+#include <dirent.h>
#include <fcntl.h>
#include <linux/dm-ioctl.h>
#include <linux/kdev_t.h>
@@ -26,6 +27,7 @@
#include <unistd.h>
#include <chrono>
+#include <map>
#include <memory>
#include <optional>
#include <string>
@@ -53,7 +55,33 @@
// that prefix.
std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
-class DeviceMapper final {
+// This interface is for testing purposes. See DeviceMapper proper for what these methods do.
+class IDeviceMapper {
+ public:
+ virtual ~IDeviceMapper() {}
+
+ struct TargetInfo {
+ struct dm_target_spec spec;
+ std::string data;
+ TargetInfo() {}
+ TargetInfo(const struct dm_target_spec& spec, const std::string& data)
+ : spec(spec), data(data) {}
+
+ bool IsOverflowSnapshot() const;
+ };
+
+ virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms) = 0;
+ virtual DmDeviceState GetState(const std::string& name) const = 0;
+ virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) = 0;
+ virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) = 0;
+ virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) = 0;
+ virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) = 0;
+ virtual bool GetDeviceString(const std::string& name, std::string* dev) = 0;
+ virtual bool DeleteDeviceIfExists(const std::string& name) = 0;
+};
+
+class DeviceMapper final : public IDeviceMapper {
public:
class DmBlockDevice final {
public:
@@ -93,7 +121,7 @@
// Removes a device mapper device with the given name.
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
- bool DeleteDeviceIfExists(const std::string& name);
+ bool DeleteDeviceIfExists(const std::string& name) override;
// Removes a device mapper device with the given name and waits for |timeout_ms| milliseconds
// for the corresponding block device to be deleted.
bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
@@ -112,7 +140,7 @@
// Returns the current state of the underlying device mapper device
// with given name.
// One of INVALID, SUSPENDED or ACTIVE.
- DmDeviceState GetState(const std::string& name) const;
+ DmDeviceState GetState(const std::string& name) const override;
// Puts the given device to the specified status, which must be either:
// - SUSPENDED: suspend the device, or
@@ -156,7 +184,7 @@
// not |path| is available. It is the caller's responsibility to ensure
// there are no races.
bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
- const std::chrono::milliseconds& timeout_ms);
+ const std::chrono::milliseconds& timeout_ms) override;
// Create a device and activate the given table, without waiting to acquire
// a valid path. If the caller will use GetDmDevicePathByName(), it should
@@ -168,7 +196,7 @@
// process. A device with the given name must already exist.
//
// Returns 'true' on success, false otherwise.
- bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+ bool LoadTableAndActivate(const std::string& name, const DmTable& table) override;
// Returns true if a list of available device mapper targets registered in the kernel was
// successfully read and stored in 'targets'. Returns 'false' otherwise.
@@ -214,7 +242,7 @@
// Returns a major:minor string for the named device-mapper node, that can
// be used as inputs to DmTargets that take a block device.
- bool GetDeviceString(const std::string& name, std::string* dev);
+ bool GetDeviceString(const std::string& name, std::string* dev) override;
// The only way to create a DeviceMapper object.
static DeviceMapper& Instance();
@@ -229,20 +257,11 @@
// contain one TargetInfo for each target in the table. If the device does
// not exist, or there were too many targets, the call will fail and return
// false.
- struct TargetInfo {
- struct dm_target_spec spec;
- std::string data;
- TargetInfo() {}
- TargetInfo(const struct dm_target_spec& spec, const std::string& data)
- : spec(spec), data(data) {}
-
- bool IsOverflowSnapshot() const;
- };
- bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+ bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override;
// Identical to GetTableStatus, except also retrives the active table for the device
// mapper device from the kernel.
- bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+ bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) override;
static std::string GetTargetType(const struct dm_target_spec& spec);
@@ -259,6 +278,12 @@
// * A failure occurred.
std::optional<std::string> GetParentBlockDeviceByPath(const std::string& path);
+ // Iterate the content over "/sys/block/dm-x/dm/name" and find
+ // all the dm-wrapped block devices.
+ //
+ // Returns mapping <partition-name, /dev/block/dm-x>
+ std::map<std::string, std::string> FindDmPartitions();
+
private:
// Maximum possible device mapper targets registered in the kernel.
// This is only used to read the list of targets from kernel so we allocate
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 478a3c6..9543058 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -127,6 +127,7 @@
void UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks, uint32_t start);
void SetVerityMode(const std::string& mode);
void IgnoreZeroBlocks();
+ void CheckAtMostOnce();
std::string name() const override { return "verity"; }
std::string GetParameterString() const override;
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 1c5872e..5deba65 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -20,6 +20,8 @@
cc_library_headers {
name: "libfiemap_headers",
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["include"],
}
@@ -88,7 +90,9 @@
test_suites: ["vts", "device-tests"],
auto_gen_config: true,
- test_min_api_level: 29,
+ test_options: {
+ min_shipping_api_level: 29,
+ },
require_root: true,
}
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 6892025..a0ad208 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -27,6 +27,8 @@
cc_library_static {
name: "libfs_avb",
defaults: ["fs_mgr_defaults"],
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
@@ -150,6 +152,7 @@
static_libs: [
"libavb",
"libdm",
+ "libext2_uuid",
"libfs_avb",
"libfstab",
],
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 31494c1..e913d50 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -92,6 +92,10 @@
// Always use ignore_zero_blocks.
target.IgnoreZeroBlocks();
+ if (hashtree_desc.flags & AVB_HASHTREE_DESCRIPTOR_FLAGS_CHECK_AT_MOST_ONCE) {
+ target.CheckAtMostOnce();
+ }
+
LINFO << "Built verity table: '" << target.GetParameterString() << "'";
return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 1827566..6f874a6 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -755,6 +755,7 @@
std::string out_public_key_data;
std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
fd, "system", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+ ASSERT_EQ(0, close(fd.release()));
EXPECT_NE(nullptr, vbmeta);
EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
@@ -776,8 +777,9 @@
// Should return ErrorVerification.
vbmeta = VerifyVBMetaData(hash_modified_fd, "system", "" /*expected_public_key_blob */,
nullptr /* out_public_key_data */, &verify_result);
+ ASSERT_EQ(0, close(hash_modified_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
// Modifies the auxiliary data block.
@@ -791,8 +793,9 @@
// Should return ErrorVerification.
vbmeta = VerifyVBMetaData(aux_modified_fd, "system", "" /*expected_public_key_blob */,
nullptr /* out_public_key_data */, &verify_result);
+ ASSERT_EQ(0, close(aux_modified_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
// Resets previous modification by setting offset to -1, and checks the verification can pass.
@@ -802,8 +805,9 @@
// Should return ResultOK..
vbmeta = VerifyVBMetaData(ok_fd, "system", "" /*expected_public_key_blob */,
nullptr /* out_public_key_data */, &verify_result);
+ ASSERT_EQ(0, close(ok_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
}
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 7e528b1..4b81c2c 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -30,6 +30,8 @@
cc_library {
name: "liblp",
host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
defaults: ["fs_mgr_defaults"],
cppflags: [
@@ -103,7 +105,9 @@
defaults: ["liblp_test_defaults"],
test_suites: ["vts"],
auto_gen_config: true,
- test_min_api_level: 29,
+ test_options: {
+ min_shipping_api_level: 29,
+ },
require_root: true,
}
diff --git a/fs_mgr/liblp/OWNERS b/fs_mgr/liblp/OWNERS
new file mode 100644
index 0000000..6a95eb2
--- /dev/null
+++ b/fs_mgr/liblp/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 391836
+dvander@google.com
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6a764e4..5ab2ce2 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -118,6 +118,7 @@
native_coverage : true,
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
+ ramdisk_available: true,
recovery_available: true,
cflags: [
"-DLIBSNAPSHOT_NO_COW_WRITE",
@@ -181,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"],
@@ -282,7 +251,9 @@
"vts",
"device-tests"
],
- test_min_api_level: 29,
+ test_options: {
+ min_shipping_api_level: 29,
+ },
auto_gen_config: true,
require_root: true,
}
@@ -310,7 +281,6 @@
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
"libbase",
- "libbinder",
"libext2_uuid",
"libext4_utils",
"libfs_mgr_binder",
@@ -412,49 +382,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: [
@@ -482,7 +409,9 @@
test_suites: [
"device-tests"
],
- test_min_api_level: 30,
+ test_options: {
+ min_shipping_api_level: 30,
+ },
auto_gen_config: true,
require_root: false,
host_supported: true,
@@ -556,43 +485,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,
@@ -616,3 +508,13 @@
"inspect_cow.cpp",
],
}
+
+python_library_host {
+ name: "snapshot_proto_python",
+ srcs: [
+ "android/snapshot/snapshot.proto",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+}
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index de8768c..e2abdba 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -223,7 +223,8 @@
// Time from sys.boot_completed to merge start, in milliseconds.
uint32 boot_complete_to_merge_start_time_ms = 8;
- // Merge failure code, filled if state == MergeFailed.
+ // 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.
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index b75b154..ba4044f 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -140,6 +140,85 @@
ASSERT_TRUE(iter->Done());
}
+TEST_F(CowTest, ReadWriteXor) {
+ CowOptions options;
+ options.cluster_ops = 0;
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddCopy(10, 20));
+ ASSERT_TRUE(writer.AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ CowHeader header;
+ CowFooter footer;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_TRUE(reader.GetHeader(&header));
+ ASSERT_TRUE(reader.GetFooter(&footer));
+ ASSERT_EQ(header.magic, kCowMagicNumber);
+ ASSERT_EQ(header.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(footer.op.num_ops, 4);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 10);
+ ASSERT_EQ(op->source, 20);
+
+ StringSink sink;
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowXorOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ // Note: the zero operation gets split into two blocks.
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 52);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
TEST_F(CowTest, CompressGz) {
CowOptions options;
options.cluster_ops = 0;
@@ -981,6 +1060,239 @@
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, ResumeSeqOp) {
+ CowOptions options;
+ auto writer = std::make_unique<CowWriter>(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 / 2));
+ ASSERT_TRUE(writer->AddLabel(1));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+ auto reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd, 1));
+ auto itr = reader->GetRevMergeOpIter();
+ ASSERT_TRUE(itr->Done());
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd));
+
+ auto iter = reader->GetRevMergeOpIter();
+
+ uint64_t expected_block = 10;
+ while (!iter->Done() && expected_block > 0) {
+ ASSERT_FALSE(iter->Done());
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op.new_block, expected_block);
+
+ iter->Next();
+ expected_block--;
+ }
+ ASSERT_EQ(expected_block, 0);
+ ASSERT_TRUE(iter->Done());
+}
+
+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, 13));
+ ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
+ ASSERT_TRUE(writer.AddCopy(3, 15));
+ ASSERT_TRUE(writer.AddCopy(2, 11));
+ ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
+ ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
+ ASSERT_TRUE(writer.AddCopy(5, 16));
+ ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
+ ASSERT_TRUE(writer.AddCopy(10, 12));
+ ASSERT_TRUE(writer.AddCopy(7, 14));
+ 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, 11));
+ ASSERT_TRUE(writer.AddCopy(10, 12));
+ ASSERT_TRUE(writer.AddCopy(6, 13));
+ ASSERT_TRUE(writer.AddCopy(7, 14));
+ ASSERT_TRUE(writer.AddCopy(3, 15));
+ ASSERT_TRUE(writer.AddCopy(5, 16));
+ 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());
+}
+
+TEST_F(CowTest, InvalidMergeOrderTest) {
+ CowOptions options;
+ options.cluster_ops = 5;
+ options.num_merge_ops = 1;
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ auto writer = std::make_unique<CowWriter>(options);
+ CowReader reader;
+
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer->AddCopy(3, 2));
+ ASSERT_TRUE(writer->AddCopy(2, 1));
+ ASSERT_TRUE(writer->AddLabel(1));
+ ASSERT_TRUE(writer->Finalize());
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_TRUE(reader.VerifyMergeOps());
+
+ ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ ASSERT_TRUE(writer->AddCopy(4, 2));
+ ASSERT_TRUE(writer->Finalize());
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_FALSE(reader.VerifyMergeOps());
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+ ASSERT_TRUE(writer->AddCopy(2, 1));
+ ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
+ ASSERT_TRUE(writer->Finalize());
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_FALSE(reader.VerifyMergeOps());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 0753c49..94c4109 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -37,6 +37,10 @@
os << "kCowLabelOp, ";
else if (op.type == kCowClusterOp)
os << "kCowClusterOp ";
+ else if (op.type == kCowXorOp)
+ os << "kCowXorOp ";
+ else if (op.type == kCowSequenceOp)
+ os << "kCowSequenceOp ";
else if (op.type == kCowFooterOp)
os << "kCowFooterOp ";
else
@@ -52,14 +56,17 @@
os << (int)op.compression << "?, ";
os << "data_length:" << op.data_length << ",\t";
os << "new_block:" << op.new_block << ",\t";
- os << "source:" << op.source << ")";
+ os << "source:" << op.source;
+ if (op.type == kCowXorOp)
+ os << " (block:" << op.source / BLOCK_SZ << " offset:" << op.source % BLOCK_SZ << ")";
+ os << ")";
return os;
}
int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
return op.source;
- } else if (op.type == kCowReplaceOp && cluster_ops == 0) {
+ } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
return op.data_length;
} else {
return 0;
@@ -81,6 +88,17 @@
case kCowLabelOp:
case kCowClusterOp:
case kCowFooterOp:
+ case kCowSequenceOp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsOrderedOp(const CowOperation& op) {
+ switch (op.type) {
+ case kCowCopyOp:
+ case kCowXorOp:
return true;
default:
return false;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 2349e4a..5306b28 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>
@@ -31,7 +34,11 @@
namespace android {
namespace snapshot {
-CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
+CowReader::CowReader()
+ : fd_(-1),
+ header_(),
+ fd_size_(0),
+ merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()) {}
static void SHA256(const void*, size_t, uint8_t[]) {
#if 0
@@ -42,6 +49,24 @@
#endif
}
+std::unique_ptr<CowReader> CowReader::CloneCowReader() {
+ auto cow = std::make_unique<CowReader>();
+ cow->owned_fd_.reset();
+ cow->header_ = header_;
+ cow->footer_ = footer_;
+ cow->fd_size_ = fd_size_;
+ cow->last_label_ = last_label_;
+ cow->ops_ = ops_;
+ cow->merge_op_blocks_ = merge_op_blocks_;
+ cow->merge_op_start_ = merge_op_start_;
+ cow->block_map_ = block_map_;
+ cow->num_total_data_ops_ = num_total_data_ops_;
+ cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
+ cow->has_seq_ops_ = has_seq_ops_;
+ cow->data_loc_ = data_loc_;
+ return cow;
+}
+
bool CowReader::InitForMerge(android::base::unique_fd&& fd) {
owned_fd_ = std::move(fd);
fd_ = owned_fd_.get();
@@ -127,11 +152,17 @@
return false;
}
- return ParseOps(label);
+ if (!ParseOps(label)) {
+ return false;
+ }
+ // If we're resuming a write, we're not ready to merge
+ if (label.has_value()) return true;
+ return PrepMergeOps();
}
bool CowReader::ParseOps(std::optional<uint64_t> label) {
uint64_t pos;
+ auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
// Skip the scratch space
if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
@@ -151,6 +182,13 @@
// Reading a v1 version of COW which doesn't have buffer_size.
header_.buffer_size = 0;
}
+ uint64_t data_pos = 0;
+
+ if (header_.cluster_ops) {
+ data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+ } else {
+ data_pos = pos + sizeof(CowOperation);
+ }
auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
uint64_t current_op_num = 0;
@@ -171,7 +209,11 @@
while (current_op_num < ops_buffer->size()) {
auto& current_op = ops_buffer->data()[current_op_num];
current_op_num++;
+ if (current_op.type == kCowXorOp) {
+ data_loc->insert({current_op.new_block, data_pos});
+ }
pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+ data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
if (current_op.type == kCowClusterOp) {
break;
@@ -189,7 +231,7 @@
memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
off_t offs = lseek(fd_.get(), pos, SEEK_SET);
if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed";
+ PLOG(ERROR) << "lseek next op failed " << offs;
return false;
}
if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
@@ -201,13 +243,15 @@
current_op_num--;
done = true;
break;
+ } else if (current_op.type == kCowSequenceOp) {
+ has_seq_ops_ = true;
}
}
// Position for next cluster read
off_t offs = lseek(fd_.get(), pos, SEEK_SET);
if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed";
+ PLOG(ERROR) << "lseek next op failed " << offs;
return false;
}
ops_buffer->resize(current_op_num);
@@ -251,7 +295,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;
@@ -260,142 +304,215 @@
ops_ = ops_buffer;
ops_->shrink_to_fit();
+ data_loc_ = data_loc;
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());
+ for (size_t i = 0; i < ops_->size(); i++) {
+ auto& current_op = ops_->data()[i];
- 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 }
- //==============================================================
+ if (current_op.type == kCowSequenceOp) {
+ size_t seq_len = current_op.data_length / sizeof(uint32_t);
- num_copy_ops = FindNumCopyops();
+ 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;
+ }
- std::sort(ops_.get()->begin() + num_copy_ops, ops_.get()->end(),
- [](CowOperation& op1, CowOperation& op2) -> bool {
- return op1.new_block > op2.new_block;
- });
+ if (IsMetadataOp(current_op)) {
+ continue;
+ }
- if (header_.num_merge_ops > 0) {
- ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
+ 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;
+ }
}
- num_copy_ops = FindNumCopyops();
- set_copy_ops(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_start_ = header_.num_merge_ops;
+ }
+
+ block_map_ = block_map;
+ merge_op_blocks_ = merge_op_blocks;
+ return true;
}
-uint64_t CowReader::FindNumCopyops() {
- uint64_t num_copy_ops = 0;
-
- for (uint64_t i = 0; i < ops_->size(); i++) {
- auto& current_op = ops_->data()[i];
- if (current_op.type != kCowCopyOp) {
- break;
+bool CowReader::VerifyMergeOps() {
+ auto itr = GetMergeOpIter(true);
+ std::unordered_map<uint64_t, CowOperation> overwritten_blocks;
+ while (!itr->Done()) {
+ CowOperation op = itr->Get();
+ uint64_t block;
+ bool offset;
+ if (op.type == kCowCopyOp) {
+ block = op.source;
+ offset = false;
+ } else if (op.type == kCowXorOp) {
+ block = op.source / BLOCK_SZ;
+ offset = (op.source % BLOCK_SZ) != 0;
+ } else {
+ itr->Next();
+ continue;
}
- num_copy_ops += 1;
- }
- return num_copy_ops;
+ CowOperation* overwrite = nullptr;
+ if (overwritten_blocks.count(block)) {
+ overwrite = &overwritten_blocks[block];
+ LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
+ << op << "\noverwritten by previously merged op:\n"
+ << *overwrite;
+ }
+ if (offset && overwritten_blocks.count(block + 1)) {
+ overwrite = &overwritten_blocks[block + 1];
+ LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
+ << op << "\noverwritten by previously merged op:\n"
+ << *overwrite;
+ }
+ if (overwrite != nullptr) return false;
+ overwritten_blocks[op.new_block] = op;
+ itr->Next();
+ }
+ return true;
}
bool CowReader::GetHeader(CowHeader* header) {
@@ -430,11 +547,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 +564,12 @@
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,
+ uint64_t start);
bool Done() override;
const CowOperation& Get() override;
@@ -457,34 +577,94 @@
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_;
+ uint64_t start_;
};
-CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
+class CowMergeOpIter final : public ICowOpIter {
+ public:
+ explicit CowMergeOpIter(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, uint64_t start);
+
+ bool Done() override;
+ const CowOperation& Get() override;
+ void Next() override;
+
+ private:
+ 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_;
+ std::vector<uint32_t>::iterator block_iter_;
+ uint64_t start_;
+};
+
+CowMergeOpIter::CowMergeOpIter(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,
+ uint64_t start) {
ops_ = ops;
- op_riter_ = ops_.get()->rbegin();
+ merge_op_blocks_ = merge_op_blocks;
+ map_ = map;
+ start_ = start;
+
+ block_iter_ = merge_op_blocks->begin() + start;
}
-bool CowOpReverseIter::Done() {
- return op_riter_ == ops_.get()->rend();
+bool CowMergeOpIter::Done() {
+ return block_iter_ == merge_op_blocks_->end();
}
-void CowOpReverseIter::Next() {
+void CowMergeOpIter::Next() {
CHECK(!Done());
- op_riter_++;
+ block_iter_++;
}
-const CowOperation& CowOpReverseIter::Get() {
+const CowOperation& CowMergeOpIter::Get() {
CHECK(!Done());
- return (*op_riter_);
+ return ops_->data()[map_->at(*block_iter_)];
+}
+
+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,
+ uint64_t start) {
+ ops_ = ops;
+ merge_op_blocks_ = merge_op_blocks;
+ map_ = map;
+ start_ = start;
+
+ block_riter_ = merge_op_blocks->rbegin();
+}
+
+bool CowRevMergeOpIter::Done() {
+ return block_riter_ == merge_op_blocks_->rend() - start_;
+}
+
+void CowRevMergeOpIter::Next() {
+ CHECK(!Done());
+ block_riter_++;
+}
+
+const CowOperation& CowRevMergeOpIter::Get() {
+ CHECK(!Done());
+ 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(bool ignore_progress) {
+ return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+ ignore_progress ? 0 : merge_op_start_);
+}
+
+std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {
+ return std::make_unique<CowMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+ ignore_progress ? 0 : merge_op_start_);
}
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
@@ -554,7 +734,13 @@
return false;
}
- CowDataStream stream(this, op.source, op.data_length);
+ uint64_t offset;
+ if (op.type == kCowXorOp) {
+ offset = data_loc_->at(op.new_block);
+ } else {
+ offset = op.source;
+ }
+ CowDataStream stream(this, offset, op.data_length);
decompressor->set_stream(&stream);
decompressor->set_sink(sink);
return decompressor->Decompress(header_.block_size);
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 51c00a9..5ce1d3b 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -58,6 +58,26 @@
return EmitRawBlocks(new_block_start, data, size);
}
+bool ICowWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ if (offset >= options_.block_size) {
+ LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
+ << options_.block_size;
+ }
+ return EmitXorBlocks(new_block_start, data, size, old_block, offset);
+}
+
bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
uint64_t last_block = new_block_start + num_blocks - 1;
if (!ValidateNewBlock(last_block)) {
@@ -70,6 +90,10 @@
return EmitLabel(label);
}
+bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
+ return EmitSequenceData(num_ops, data);
+}
+
bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
@@ -92,7 +116,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_ = {};
@@ -268,13 +292,27 @@
}
bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
+}
+
+bool CowWriter::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
+ return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
+}
+
+bool CowWriter::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+ uint64_t old_block, uint16_t offset, uint8_t type) {
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
CHECK(!merge_in_progress_);
for (size_t i = 0; i < size / header_.block_size; i++) {
CowOperation op = {};
- op.type = kCowReplaceOp;
op.new_block = new_block_start + i;
- op.source = next_data_pos_;
+ op.type = type;
+ if (type == kCowXorOp) {
+ op.source = (old_block + i) * header_.block_size + offset;
+ } else {
+ op.source = next_data_pos_;
+ }
if (compression_) {
auto data = Compress(iter, header_.block_size);
@@ -326,6 +364,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/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 14ce0ee..a6d96ed 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -139,5 +139,9 @@
}
}
+android::dm::IDeviceMapper& DeviceInfo::GetDeviceMapper() {
+ return android::dm::DeviceMapper::Instance();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 7999c99..8aefb85 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -40,6 +40,7 @@
bool IsRecovery() const override;
std::unique_ptr<IImageManager> OpenImageManager() const override;
bool IsFirstStageInit() const override;
+ android::dm::IDeviceMapper& GetDeviceMapper() override;
void set_first_stage_init(bool value) { first_stage_init_ = value; }
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
index ed77c15..4a36251 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -17,8 +17,9 @@
#include <android-base/logging.h>
#include <stdint.h>
+#include <limits>
#include <optional>
-#include <vector>
+#include <unordered_set>
namespace android {
namespace snapshot {
@@ -37,21 +38,16 @@
return;
}
- if (modified_chunks_.size() <= chunk_id) {
- if (modified_chunks_.max_size() <= chunk_id) {
- LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
- valid_ = false;
- return;
- }
- modified_chunks_.resize(chunk_id + 1, false);
- if (modified_chunks_.size() <= chunk_id) {
- LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
- valid_ = false;
- return;
- }
+ if (chunk_id > std::numeric_limits<uint32_t>::max()) {
+ LOG(ERROR) << "Chunk exceeds maximum size: " << chunk_id;
+ valid_ = false;
+ return;
+ }
+ if (modified_chunks_.count(chunk_id) > 0) {
+ return;
}
- modified_chunks_[chunk_id] = true;
+ modified_chunks_.emplace(chunk_id);
}
std::optional<uint64_t> cow_size_bytes() const {
@@ -91,23 +87,16 @@
return std::nullopt;
}
- uint64_t modified_chunks_count = 0;
uint64_t cow_chunks = 0;
- for (const auto& c : modified_chunks_) {
- if (c) {
- ++modified_chunks_count;
- }
- }
-
/* disk header + padding = 1 chunk */
cow_chunks += 1;
/* snapshot modified chunks */
- cow_chunks += modified_chunks_count;
+ cow_chunks += modified_chunks_.size();
/* snapshot chunks index metadata */
- cow_chunks += 1 + modified_chunks_count / exceptions_per_chunk;
+ cow_chunks += 1 + modified_chunks_.size() / exceptions_per_chunk;
return cow_chunks;
}
@@ -150,30 +139,8 @@
/*
* |modified_chunks_| is a container that keeps trace of the modified
* chunks.
- * Multiple options were considered when choosing the most appropriate data
- * structure for this container. Here follows a summary of why vector<bool>
- * has been chosen, taking as a reference a snapshot partition of 4 GiB and
- * chunk size of 4 KiB.
- * - std::set<uint64_t> is very space-efficient for a small number of
- * operations, but if the whole snapshot is changed, it would need to
- * store
- * 4 GiB / 4 KiB * (64 bit / 8) = 8 MiB
- * just for the data, plus the additional data overhead for the red-black
- * tree used for data sorting (if each rb-tree element stores 3 address
- * and the word-aligne color, the total size grows to 32 MiB).
- * - std::bitset<N> is not a good fit because requires a priori knowledge,
- * at compile time, of the bitset size.
- * - std::vector<bool> is a special case of vector, which uses a data
- * compression that allows reducing the space utilization of each element
- * to 1 bit. In detail, this data structure is composed of a resizable
- * array of words, each of them representing a bitmap. On a 64 bit
- * device, modifying the whole 4 GiB snapshot grows this container up to
- * 4 * GiB / 4 KiB / 64 = 64 KiB
- * that, even if is the same space requirement to change a single byte at
- * the highest address of the snapshot, is a very affordable space
- * requirement.
*/
- std::vector<bool> modified_chunks_;
+ std::unordered_set<uint32_t> modified_chunks_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 000e5e1..c15682a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -138,6 +138,8 @@
// For Label operations, this is the value of the applied label.
//
// For Cluster operations, this is the length of the following data region
+ //
+ // For Xor operations, this is the byte location in the source image.
uint64_t source;
} __attribute__((packed));
@@ -148,6 +150,8 @@
static constexpr uint8_t kCowZeroOp = 3;
static constexpr uint8_t kCowLabelOp = 4;
static constexpr uint8_t kCowClusterOp = 5;
+static constexpr uint8_t kCowXorOp = 6;
+static constexpr uint8_t kCowSequenceOp = 7;
static constexpr uint8_t kCowFooterOp = -1;
static constexpr uint8_t kCowCompressNone = 0;
@@ -184,7 +188,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..143f73c 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
@@ -68,6 +68,7 @@
// Return the file footer.
virtual bool GetFooter(CowFooter* footer) = 0;
+ virtual bool VerifyMergeOps() = 0;
// Return the last valid label
virtual bool GetLastLabel(uint64_t* label) = 0;
@@ -75,8 +76,11 @@
// 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 reverse merge order
+ virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress) = 0;
+
+ // Return an iterator for retrieving CowOperation entries in merge order
+ virtual std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress) = 0;
// Get decoded bytes from the data section, handling any decompression.
// All retrieved data is passed to the sink.
@@ -98,22 +102,7 @@
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 {
+class CowReader final : public ICowReader {
public:
CowReader();
~CowReader() { owned_fd_ = {}; }
@@ -124,6 +113,7 @@
bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
bool InitForMerge(android::base::unique_fd&& fd);
+ bool VerifyMergeOps() override;
bool GetHeader(CowHeader* header) override;
bool GetFooter(CowFooter* footer) override;
@@ -135,25 +125,29 @@
// 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(bool ignore_progress = false) override;
+ std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) 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_ = {}; }
+ // Creates a clone of the current CowReader without the file handlers
+ std::unique_ptr<CowReader> CloneCowReader();
+
private:
bool ParseOps(std::optional<uint64_t> label);
+ bool PrepMergeOps();
uint64_t FindNumCopyops();
android::base::unique_fd owned_fd_;
@@ -163,8 +157,13 @@
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_;
+ uint64_t merge_op_start_;
+ 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_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index f43ea68..e17b5c6 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
@@ -55,12 +58,19 @@
// Encode a sequence of raw blocks. |size| must be a multiple of the block size.
bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
+ // Add a sequence of xor'd blocks. |size| must be a multiple of the block size.
+ bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset);
+
// Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
// Add a label to the op sequence.
bool AddLabel(uint64_t label);
+ // Add sequence data for op merging. Data is a list of the destination block numbers.
+ bool AddSequenceData(size_t num_ops, const uint32_t* data);
+
// Flush all pending writes. This must be called before closing the writer
// to ensure that the correct headers and footers are written.
virtual bool Finalize() = 0;
@@ -76,8 +86,11 @@
protected:
virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) = 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);
@@ -111,12 +124,17 @@
protected:
virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) 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();
bool EmitClusterIfNeeded();
+ bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+ uint16_t offset, uint8_t type);
void SetupHeaders();
bool ParseOptions();
bool OpenForWrite();
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..b0be5a5
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -0,0 +1,55 @@
+//
+// 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, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_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));
+ MOCK_METHOD(bool, VerifyMergeOps, (), (override, const, noexcept));
+
+ // 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 9bf5db1..55f4ed7 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) \
@@ -110,6 +110,7 @@
virtual bool IsTestDevice() const { return false; }
virtual bool IsFirstStageInit() const = 0;
virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
+ virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
// Helper method for implementing OpenImageManager.
std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
@@ -611,6 +612,14 @@
MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
const SnapshotStatus& update_status);
+ // Get status or table information about a device-mapper node with a single target.
+ enum class TableQuery {
+ Table,
+ Status,
+ };
+ bool GetSingleTarget(const std::string& dm_name, TableQuery query,
+ android::dm::DeviceMapper::TargetInfo* target);
+
// Interact with status files under /metadata/ota/snapshots.
bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
@@ -773,9 +782,9 @@
bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {});
- std::string gsid_dir_;
- std::string metadata_dir_;
+ android::dm::IDeviceMapper& dm_;
std::unique_ptr<IDeviceInfo> device_;
+ std::string metadata_dir_;
std::unique_ptr<IImageManager> images_;
bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index bf5ce8b..545f117 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -47,6 +47,7 @@
virtual bool InitializeAppend(uint64_t label) = 0;
virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
+ virtual bool VerifyMergeOps() const noexcept = 0;
protected:
android::base::borrowed_fd GetSourceFd();
@@ -58,7 +59,7 @@
};
// Send writes to a COW or a raw device directly, based on a threshold.
-class CompressedSnapshotWriter : public ISnapshotWriter {
+class CompressedSnapshotWriter final : public ISnapshotWriter {
public:
CompressedSnapshotWriter(const CowOptions& options);
@@ -70,21 +71,26 @@
bool Finalize() override;
uint64_t GetCowSize() override;
std::unique_ptr<FileDescriptor> OpenReader() override;
+ bool VerifyMergeOps() const noexcept;
protected:
bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset) 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:
+ std::unique_ptr<CowReader> OpenCowReader() const;
android::base::unique_fd cow_device_;
std::unique_ptr<CowWriter> cow_;
};
// Write directly to a dm-snapshot device.
-class OnlineKernelSnapshotWriter : public ISnapshotWriter {
+class OnlineKernelSnapshotWriter final : public ISnapshotWriter {
public:
OnlineKernelSnapshotWriter(const CowOptions& options);
@@ -98,11 +104,18 @@
uint64_t GetCowSize() override { return cow_size_; }
std::unique_ptr<FileDescriptor> OpenReader() override;
+ // Online kernel snapshot writer doesn't care about merge sequences.
+ // So ignore.
+ bool VerifyMergeOps() const noexcept override { return true; }
+
protected:
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 EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset) 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/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 4e7ccf1..1f57bbc 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -99,6 +99,9 @@
std::unique_ptr<IImageManager> OpenImageManager() const override {
return IDeviceInfo::OpenImageManager("ota/test");
}
+ android::dm::IDeviceMapper& GetDeviceMapper() override {
+ return android::dm::DeviceMapper::Instance();
+ }
bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 4a84fba..548ba00 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -16,8 +16,10 @@
#include <stdio.h>
#include <unistd.h>
+#include <iomanip>
#include <iostream>
#include <string>
+#include <vector>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
@@ -39,9 +41,28 @@
LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
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 -b Show data for failed decompress";
+ LOG(ERROR) << "\t -l Show ops";
+ LOG(ERROR) << "\t -m Show ops in reverse merge order";
+ LOG(ERROR) << "\t -n Show ops in merge order";
+ LOG(ERROR) << "\t -a Include merged ops in any merge order listing";
+ LOG(ERROR) << "\t -o Shows sequence op block order";
+ LOG(ERROR) << "\t -v Verifies merge order has no conflicts\n";
}
+enum OpIter { Normal, RevMerge, Merge };
+
+struct Options {
+ bool silent;
+ bool decompress;
+ bool show_ops;
+ bool show_bad;
+ bool show_seq;
+ bool verify_sequence;
+ OpIter iter_type;
+ bool include_merged;
+};
+
// Sink that always appends to the end of a string.
class StringSink : public IByteSink {
public:
@@ -78,7 +99,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,12 +121,14 @@
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";
std::cout << "Footer size: " << header.footer_size << "\n";
std::cout << "Block size: " << header.block_size << "\n";
+ std::cout << "Num merge ops: " << header.num_merge_ops << "\n";
+ std::cout << "RA buffer size: " << header.buffer_size << "\n";
std::cout << "\n";
if (has_footer) {
std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
@@ -114,23 +137,56 @@
}
}
- auto iter = reader.GetOpIter();
+ if (opt.verify_sequence) {
+ if (reader.VerifyMergeOps()) {
+ std::cout << "\nMerge sequence is consistent.\n";
+ } else {
+ std::cout << "\nMerge sequence is inconsistent!\n";
+ }
+ }
+
+ std::unique_ptr<ICowOpIter> iter;
+ if (opt.iter_type == Normal) {
+ iter = reader.GetOpIter();
+ } else if (opt.iter_type == RevMerge) {
+ iter = reader.GetRevMergeOpIter(opt.include_merged);
+ } else if (opt.iter_type == Merge) {
+ iter = reader.GetMergeOpIter(opt.include_merged);
+ }
StringSink sink;
bool success = true;
while (!iter->Done()) {
const CowOperation& op = iter->Get();
- if (!silent) std::cout << op << "\n";
+ if (!opt.silent && opt.show_ops) 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();
}
+ if (op.type == kCowSequenceOp && opt.show_seq) {
+ size_t read;
+ std::vector<uint32_t> merge_op_blocks;
+ size_t seq_len = op.data_length / sizeof(uint32_t);
+ merge_op_blocks.resize(seq_len);
+ if (!reader.GetRawBytes(op.source, merge_op_blocks.data(), op.data_length, &read)) {
+ PLOG(ERROR) << "Failed to read sequence op!";
+ return false;
+ }
+ if (!opt.silent) {
+ std::cout << "Sequence for " << op << " is :\n";
+ for (size_t i = 0; i < seq_len; i++) {
+ std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << ", ";
+ if ((i + 1) % 10 == 0 || i + 1 == seq_len) std::cout << "\n";
+ }
+ }
+ }
+
iter->Next();
}
@@ -142,19 +198,41 @@
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;
+ opt.verify_sequence = false;
+ opt.include_merged = false;
+ while ((ch = getopt(argc, argv, "sdbmnolva")) != -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;
+ case 'n':
+ opt.iter_type = android::snapshot::Merge;
+ break;
+ case 'o':
+ opt.show_seq = true;
+ break;
+ case 'l':
+ opt.show_ops = true;
+ break;
+ case 'v':
+ opt.verify_sequence = true;
+ break;
+ case 'a':
+ opt.include_merged = true;
break;
default:
android::snapshot::usage();
@@ -167,7 +245,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/scripts/Android.bp b/fs_mgr/libsnapshot/scripts/Android.bp
new file mode 100644
index 0000000..829f5bc
--- /dev/null
+++ b/fs_mgr/libsnapshot/scripts/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_binary_host {
+ name: "dump_snapshot_proto",
+ main: "dump_snapshot_proto.py",
+ srcs: [
+ "dump_snapshot_proto.py",
+ ],
+ libs: [
+ "snapshot_proto_python",
+ ],
+}
diff --git a/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py b/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py
new file mode 100644
index 0000000..566108d
--- /dev/null
+++ b/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py
@@ -0,0 +1,39 @@
+# 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.
+
+import argparse
+
+from android.snapshot import snapshot_pb2
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('type', type = str, help = 'Type (snapshot or update)')
+ parser.add_argument('file', type = str, help = 'Input file')
+ args = parser.parse_args()
+
+ with open(args.file, 'rb') as fp:
+ data = fp.read()
+
+ if args.type == 'snapshot':
+ msg = snapshot_pb2.SnapshotStatus()
+ elif args.type == 'update':
+ msg = snapshot_pb2.SnapshotUpdateStatus()
+ else:
+ raise Exception('Unknown proto type')
+
+ msg.ParseFromString(data)
+ print(msg)
+
+if __name__ == '__main__':
+ main()
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 4c94da2..3d8ae29 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -114,9 +114,8 @@
return sm;
}
-SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
- metadata_dir_ = device_->GetMetadataDir();
-}
+SnapshotManager::SnapshotManager(IDeviceInfo* device)
+ : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}
static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
@@ -402,8 +401,6 @@
const std::chrono::milliseconds& timeout_ms, std::string* path) {
CHECK(lock);
- auto& dm = DeviceMapper::Instance();
-
// Use an extra decoration for first-stage init, so we can transition
// to a new table entry in second-stage.
std::string misc_name = name;
@@ -423,7 +420,7 @@
DmTable table;
table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
- if (!dm.CreateDevice(name, table, path, timeout_ms)) {
+ if (!dm_.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
if (!WaitForDevice(*path, timeout_ms)) {
@@ -490,8 +487,6 @@
uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
- auto& dm = DeviceMapper::Instance();
-
// Note that merging is a global state. We do track whether individual devices
// have completed merging, but the start of the merge process is considered
// atomic.
@@ -528,7 +523,7 @@
DmTable table;
table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
kSnapshotChunkSize);
- if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+ if (!dm_.CreateDevice(name, table, dev_path, timeout_ms)) {
LOG(ERROR) << "Could not create snapshot device: " << name;
return false;
}
@@ -659,7 +654,6 @@
auto other_suffix = device_->GetOtherSlotSuffix();
- auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
if (android::base::EndsWith(snapshot, other_suffix)) {
// Allow the merge to continue, but log this unexpected case.
@@ -671,7 +665,7 @@
// the same time. This is a fairly serious error. We could forcefully
// map everything here, but it should have been mapped during first-
// stage init.
- if (dm.GetState(snapshot) == DmDeviceState::INVALID) {
+ if (dm_.GetState(snapshot) == DmDeviceState::INVALID) {
LOG(ERROR) << "Cannot begin merge; device " << snapshot << " is not mapped.";
return false;
}
@@ -804,10 +798,8 @@
}
MergeFailureCode SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {
- auto& dm = DeviceMapper::Instance();
-
std::vector<DeviceMapper::TargetInfo> old_targets;
- if (!dm.GetTableInfo(name, &old_targets)) {
+ if (!dm_.GetTableInfo(name, &old_targets)) {
LOG(ERROR) << "Could not read snapshot device table: " << name;
return MergeFailureCode::GetTableInfo;
}
@@ -825,7 +817,7 @@
DmTable table;
table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
SnapshotStorageMode::Merge, kSnapshotChunkSize);
- if (!dm.LoadTableAndActivate(name, table)) {
+ if (!dm_.LoadTableAndActivate(name, table)) {
LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << name;
return MergeFailureCode::ActivateNewTable;
}
@@ -833,24 +825,18 @@
return MergeFailureCode::Ok;
}
-enum class TableQuery {
- Table,
- Status,
-};
-
-static bool GetSingleTarget(const std::string& dm_name, TableQuery query,
- DeviceMapper::TargetInfo* target) {
- auto& dm = DeviceMapper::Instance();
- if (dm.GetState(dm_name) == DmDeviceState::INVALID) {
+bool SnapshotManager::GetSingleTarget(const std::string& dm_name, TableQuery query,
+ DeviceMapper::TargetInfo* target) {
+ if (dm_.GetState(dm_name) == DmDeviceState::INVALID) {
return false;
}
std::vector<DeviceMapper::TargetInfo> targets;
bool result;
if (query == TableQuery::Status) {
- result = dm.GetTableStatus(dm_name, &targets);
+ result = dm_.GetTableStatus(dm_name, &targets);
} else {
- result = dm.GetTableInfo(dm_name, &targets);
+ result = dm_.GetTableInfo(dm_name, &targets);
}
if (!result) {
LOG(ERROR) << "Could not query device: " << dm_name;
@@ -1180,11 +1166,9 @@
return MergeFailureCode::Ok;
}
- auto& dm = DeviceMapper::Instance();
-
std::string cow_image_name = GetMappedCowDeviceName(name, status);
std::string cow_image_path;
- if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
+ if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
return MergeFailureCode::GetCowPathConsistencyCheck;
}
@@ -1204,11 +1188,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
@@ -1364,8 +1344,6 @@
bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
const SnapshotStatus& status) {
- auto& dm = DeviceMapper::Instance();
-
// Verify we have a snapshot-merge device.
DeviceMapper::TargetInfo target;
if (!GetSingleTarget(name, TableQuery::Table, &target)) {
@@ -1404,7 +1382,7 @@
return false;
}
- if (!dm.LoadTableAndActivate(name, table)) {
+ if (!dm_.LoadTableAndActivate(name, table)) {
return false;
}
@@ -1477,8 +1455,6 @@
}
}
- auto& dm = DeviceMapper::Instance();
-
auto lock = LockExclusive();
if (!lock) return false;
@@ -1492,7 +1468,7 @@
size_t ok_cows = 0;
for (const auto& snapshot : snapshots) {
std::string user_cow_name = GetDmUserCowName(snapshot);
- if (dm.GetState(user_cow_name) == DmDeviceState::INVALID) {
+ if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {
continue;
}
@@ -1519,7 +1495,7 @@
DmTable table;
table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
- if (!dm.LoadTableAndActivate(user_cow_name, table)) {
+ if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
LOG(ERROR) << "Unable to swap tables for " << misc_name;
continue;
}
@@ -1532,7 +1508,7 @@
}
std::string source_device;
- if (!dm.GetDmDevicePathByName(source_device_name, &source_device)) {
+ if (!dm_.GetDmDevicePathByName(source_device_name, &source_device)) {
LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
continue;
}
@@ -1540,7 +1516,7 @@
std::string cow_image_name = GetMappedCowDeviceName(snapshot, snapshot_status);
std::string cow_image_device;
- if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
+ if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
LOG(ERROR) << "Could not get device path for " << cow_image_name;
continue;
}
@@ -1715,8 +1691,7 @@
// snapshot, but it's on the wrong slot. We can't unmap an active
// partition. If this is not really a snapshot, skip the unmap
// step.
- auto& dm = DeviceMapper::Instance();
- if (dm.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
+ if (dm_.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
LOG(ERROR) << "Detected snapshot " << name << " on " << current_slot << " slot"
<< " for source partition; removing without unmap.";
should_unmap = false;
@@ -2053,14 +2028,13 @@
// Create the base device for the snapshot, or if there is no snapshot, the
// device itself. This device consists of the real blocks in the super
// partition that this logical partition occupies.
- auto& dm = DeviceMapper::Instance();
std::string base_path;
if (!CreateLogicalPartition(params, &base_path)) {
LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
<< " as device " << params.GetDeviceName();
return false;
}
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, params.GetDeviceName());
if (paths) {
paths->target_device = base_path;
@@ -2074,7 +2048,7 @@
// We don't have ueventd in first-stage init, so use device major:minor
// strings instead.
std::string base_device;
- if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+ if (!dm_.GetDeviceString(params.GetDeviceName(), &base_device)) {
LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
return false;
}
@@ -2117,7 +2091,7 @@
}
auto source_device = GetSourceDeviceName(params.GetPartitionName());
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, source_device);
} else {
source_device_path = base_path;
}
@@ -2144,7 +2118,7 @@
<< params.GetPartitionName();
return false;
}
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, name);
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, name);
remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
@@ -2210,8 +2184,6 @@
std::string cow_image_name = GetCowImageDeviceName(partition_name);
*cow_name = GetCowName(partition_name);
- auto& dm = DeviceMapper::Instance();
-
// Map COW image if necessary.
if (snapshot_status.cow_file_size() > 0) {
if (!EnsureImageManager()) return false;
@@ -2262,11 +2234,11 @@
// We have created the DmTable now. Map it.
std::string cow_path;
- if (!dm.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
+ if (!dm_.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
LOG(ERROR) << "Could not create COW device: " << *cow_name;
return false;
}
- created_devices->EmplaceBack<AutoUnmapDevice>(&dm, *cow_name);
+ created_devices->EmplaceBack<AutoUnmapDevice>(&dm_, *cow_name);
LOG(INFO) << "Mapped COW device for " << params.GetPartitionName() << " at " << cow_path;
return true;
}
@@ -2293,10 +2265,8 @@
}
bool SnapshotManager::UnmapDmUserDevice(const std::string& snapshot_name) {
- auto& dm = DeviceMapper::Instance();
-
auto dm_user_name = GetDmUserCowName(snapshot_name);
- if (dm.GetState(dm_user_name) == DmDeviceState::INVALID) {
+ if (dm_.GetState(dm_user_name) == DmDeviceState::INVALID) {
return true;
}
@@ -2556,6 +2526,7 @@
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);
}
@@ -3515,7 +3486,6 @@
return false;
}
- auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, snapshot, &status)) {
@@ -3526,7 +3496,7 @@
}
std::vector<DeviceMapper::TargetInfo> targets;
- if (!dm.GetTableStatus(snapshot, &targets)) {
+ if (!dm_.GetTableStatus(snapshot, &targets)) {
LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
return false;
}
@@ -3619,11 +3589,9 @@
// isn't running yet.
bool SnapshotManager::GetMappedImageDevicePath(const std::string& device_name,
std::string* device_path) {
- auto& dm = DeviceMapper::Instance();
-
// Try getting the device string if it is a device mapper device.
- if (dm.GetState(device_name) != DmDeviceState::INVALID) {
- return dm.GetDmDevicePathByName(device_name, device_path);
+ if (dm_.GetState(device_name) != DmDeviceState::INVALID) {
+ return dm_.GetDmDevicePathByName(device_name, device_path);
}
// Otherwise, get path from IImageManager.
@@ -3632,10 +3600,9 @@
bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
std::string* device_string_or_mapped_path) {
- auto& dm = DeviceMapper::Instance();
// Try getting the device string if it is a device mapper device.
- if (dm.GetState(device_name) != DmDeviceState::INVALID) {
- return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+ if (dm_.GetState(device_name) != DmDeviceState::INVALID) {
+ return dm_.GetDeviceString(device_name, device_string_or_mapped_path);
}
// Otherwise, get path from IImageManager.
@@ -3751,10 +3718,9 @@
bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms) {
- auto& dm = DeviceMapper::Instance();
auto start = std::chrono::steady_clock::now();
while (true) {
- if (dm.DeleteDeviceIfExists(name)) {
+ if (dm_.DeleteDeviceIfExists(name)) {
return true;
}
auto now = std::chrono::steady_clock::now();
@@ -3767,7 +3733,7 @@
// Try to diagnose why this failed. First get the actual device path.
std::string full_path;
- if (!dm.GetDmDevicePathByName(name, &full_path)) {
+ if (!dm_.GetDmDevicePathByName(name, &full_path)) {
LOG(ERROR) << "Unable to diagnose DM_DEV_REMOVE failure.";
return false;
}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index 0096f85..acee2f4 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -139,7 +139,7 @@
auto& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> table;
if (!dm.GetTableInfo(dev_name, &table)) {
- PCHECK(errno == ENODEV);
+ PCHECK(errno == ENODEV || errno == ENXIO);
return {};
}
return table;
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 3ed27c8..c1a5af7 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -101,7 +101,8 @@
: env_(env),
data_(&data),
partition_opener_(std::move(partition_opener)),
- metadata_dir_(metadata_dir) {}
+ metadata_dir_(metadata_dir),
+ dm_(android::dm::DeviceMapper::Instance()) {}
// Following APIs are mocked.
std::string GetMetadataDir() const override { return metadata_dir_; }
@@ -125,6 +126,7 @@
}
bool IsRecovery() const override { return data_->is_recovery(); }
bool IsFirstStageInit() const override { return false; }
+ android::dm::IDeviceMapper& GetDeviceMapper() override { return dm_; }
std::unique_ptr<IImageManager> OpenImageManager() const {
return env_->CheckCreateFakeImageManager();
}
@@ -137,6 +139,7 @@
std::unique_ptr<TestPartitionOpener> partition_opener_;
std::string metadata_dir_;
bool switched_slot_ = false;
+ android::dm::DeviceMapper& dm_;
bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
};
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index 5ee8e25..6546c2a 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -221,7 +221,7 @@
private:
size_t ignore_start_;
- char discard_[4096];
+ char discard_[BLOCK_SZ];
};
ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
@@ -277,6 +277,29 @@
errno = EIO;
return -1;
}
+ } else if (op->type == kCowXorOp) {
+ borrowed_fd fd = GetSourceFd();
+ if (fd < 0) {
+ // GetSourceFd sets errno.
+ return -1;
+ }
+
+ off64_t offset = op->source + start_offset;
+ char data[BLOCK_SZ];
+ if (!android::base::ReadFullyAtOffset(fd, &data, bytes_to_read, offset)) {
+ PLOG(ERROR) << "read " << *source_device_;
+ // ReadFullyAtOffset sets errno.
+ return -1;
+ }
+ PartialSink partial_sink(buffer, bytes_to_read, start_offset);
+ if (!cow_->ReadData(*op, &partial_sink)) {
+ LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
+ errno = EIO;
+ return -1;
+ }
+ for (size_t i = 0; i < bytes_to_read; i++) {
+ ((char*)buffer)[i] ^= data[i];
+ }
} else {
LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
errno = EINVAL;
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
index 9373059..078f16e 100644
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
@@ -63,7 +63,9 @@
void WriteCow(ISnapshotWriter* writer) {
std::string new_block = MakeNewBlockString();
+ std::string xor_block = MakeXorBlockString();
+ ASSERT_TRUE(writer->AddXorBlocks(1, xor_block.data(), xor_block.size(), 0, kBlockSize / 2));
ASSERT_TRUE(writer->AddCopy(3, 0));
ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));
ASSERT_TRUE(writer->AddZeroBlocks(7, 2));
@@ -75,7 +77,7 @@
ASSERT_NE(reader, nullptr);
// Test that unchanged blocks are not modified.
- std::unordered_set<size_t> changed_blocks = {3, 5, 7, 8};
+ std::unordered_set<size_t> changed_blocks = {1, 3, 5, 7, 8};
for (size_t i = 0; i < kBlockCount; i++) {
if (changed_blocks.count(i)) {
continue;
@@ -88,6 +90,17 @@
}
// Test that we can read back our modified blocks.
+ std::string data(kBlockSize, 0);
+ std::string offsetblock = base_blocks_[0].substr(kBlockSize / 2, kBlockSize / 2) +
+ base_blocks_[1].substr(0, kBlockSize / 2);
+ ASSERT_EQ(offsetblock.size(), kBlockSize);
+ ASSERT_EQ(reader->Seek(1 * kBlockSize, SEEK_SET), 1 * kBlockSize);
+ ASSERT_EQ(reader->Read(data.data(), data.size()), kBlockSize);
+ for (int i = 0; i < 100; i++) {
+ data[i] = (char)~(data[i]);
+ }
+ ASSERT_EQ(data, offsetblock);
+
std::string block(kBlockSize, 0);
ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);
ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
@@ -141,6 +154,12 @@
return new_block;
}
+ std::string MakeXorBlockString() {
+ std::string data(100, -1);
+ data.resize(kBlockSize, 0);
+ return data;
+ }
+
std::unique_ptr<TemporaryFile> base_;
std::unique_ptr<TemporaryFile> cow_;
std::vector<std::string> base_blocks_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 7630efe..43c7fe2 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..48b7d80 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -67,7 +67,7 @@
return cow_->GetCowSize();
}
-std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
+std::unique_ptr<CowReader> CompressedSnapshotWriter::OpenCowReader() const {
unique_fd cow_fd(dup(cow_device_.get()));
if (cow_fd < 0) {
PLOG(ERROR) << "dup COW device";
@@ -79,6 +79,20 @@
LOG(ERROR) << "Unable to read COW";
return nullptr;
}
+ return cow;
+}
+
+bool CompressedSnapshotWriter::VerifyMergeOps() const noexcept {
+ auto cow_reader = OpenCowReader();
+ if (cow_reader == nullptr) {
+ LOG(ERROR) << "Couldn't open CowReader";
+ return false;
+ }
+ return cow_reader->VerifyMergeOps();
+}
+
+std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
+ auto cow = OpenCowReader();
auto reader = std::make_unique<CompressedSnapshotReader>();
if (!reader->SetCow(std::move(cow))) {
@@ -106,6 +120,11 @@
return cow_->AddRawBlocks(new_block_start, data, size);
}
+bool CompressedSnapshotWriter::EmitXorBlocks(uint32_t new_block_start, const void* data,
+ size_t size, uint32_t old_block, uint16_t offset) {
+ return cow_->AddXorBlocks(new_block_start, data, size, old_block, offset);
+}
+
bool CompressedSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
return cow_->AddZeroBlocks(new_block_start, num_blocks);
}
@@ -114,6 +133,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_);
}
@@ -153,6 +176,11 @@
return true;
}
+bool OnlineKernelSnapshotWriter::EmitXorBlocks(uint32_t, const void*, size_t, uint32_t, uint16_t) {
+ LOG(ERROR) << "EmitXorBlocks not implemented.";
+ return false;
+}
+
bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
std::string zeroes(options_.block_size, 0);
for (uint64_t i = 0; i < num_blocks; i++) {
@@ -183,6 +211,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/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 5eb2003..67189d4 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -36,7 +36,9 @@
" dump\n"
" Print snapshot states.\n"
" merge\n"
- " Deprecated.\n";
+ " Deprecated.\n"
+ " map\n"
+ " Map all partitions at /dev/block/mapper\n";
return EX_USAGE;
}
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..6bd5323
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -0,0 +1,136 @@
+//
+// 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",
+ "libext4_utils",
+ ],
+}
+
+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",
+ "libext4_utils",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ "libfiemap_headers",
+ ],
+ test_options: {
+ min_shipping_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 91%
rename from fs_mgr/libsnapshot/cow_snapuserd_test.cpp
rename to fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index bd432bb..bff0a50 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"
@@ -327,7 +327,7 @@
void CowSnapuserdTest::CreateBaseDevice() {
unique_fd rnd_fd;
- total_base_size_ = (size_ * 4);
+ total_base_size_ = (size_ * 5);
base_fd_ = CreateTempFile("base_device", total_base_size_);
ASSERT_GE(base_fd_, 0);
@@ -372,6 +372,11 @@
offset += size_;
ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+
+ // XOR
+ offset += size_;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
@@ -496,9 +501,10 @@
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 blk_end_copy = num_blocks * 3;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
+ uint16_t xor_offset = 5;
size_t x = num_blocks;
while (1) {
@@ -511,6 +517,11 @@
blk_src_copy -= 1;
}
+ for (size_t i = num_blocks; i > 0; i--) {
+ ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
+ &random_buffer_1_.get()[options.block_size * (i - 1)],
+ options.block_size, 2 * num_blocks + i - 1, xor_offset));
+ }
// Flush operations
ASSERT_TRUE(writer.Finalize());
// Construct the buffer required for validation
@@ -519,7 +530,11 @@
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_);
+ memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
+ memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
+ for (int i = 0; i < size_; i++) {
+ orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
+ }
}
void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
@@ -541,6 +556,7 @@
offset += 1_MiB;
}
+ memset(random_buffer_1_.get(), 0, size_);
CowOptions options;
options.compression = "gz";
@@ -551,7 +567,8 @@
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;
+ size_t blk_src_copy = 2 * num_blocks;
+ uint16_t xor_offset = 5;
while (1) {
ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
@@ -564,6 +581,8 @@
blk_src_copy += 1;
}
+ ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+ xor_offset));
// Flush operations
ASSERT_TRUE(writer.Finalize());
// Construct the buffer required for validation
@@ -572,7 +591,11 @@
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_);
+ memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
+ memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
+ for (int i = 0; i < size_; i++) {
+ orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
+ }
}
void CowSnapuserdTest::CreateCowDevice() {
@@ -606,6 +629,17 @@
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
+ uint32_t sequence[num_blocks * 2];
+ // Sequence for Copy ops
+ for (int i = 0; i < num_blocks; i++) {
+ sequence[i] = num_blocks - 1 - i;
+ }
+ // Sequence for Xor ops
+ for (int i = 0; i < num_blocks; i++) {
+ sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
+ }
+ ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
+
size_t x = num_blocks;
while (1) {
ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
@@ -631,6 +665,11 @@
ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+ size_t blk_xor_start = blk_random2_replace_start + num_blocks;
+ size_t xor_offset = BLOCK_SZ / 2;
+ ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+ xor_offset));
+
// Flush operations
ASSERT_TRUE(writer.Finalize());
// Construct the buffer required for validation
@@ -640,6 +679,13 @@
memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
+ size_ + xor_offset),
+ true);
+ for (int i = 0; i < size_; i++) {
+ orig_buffer_.get()[(size_ * 4) + i] =
+ (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
+ }
}
void CowSnapuserdTest::InitCowDevice() {
@@ -1107,6 +1153,35 @@
}
}
+TEST(Snapuserd_Test, xor_buffer) {
+ std::string data = "Test String";
+ std::string jumbled = {0x0C, 0x2A, 0x21, 0x54, 0x73, 0x27, 0x06, 0x1B, 0x07, 0x09, 0x46};
+ std::string result = "XOR String!";
+
+ BufferSink sink;
+ XorSink xor_sink;
+ sink.Initialize(sizeof(struct dm_user_header) + 10);
+ int buffsize = 5;
+ xor_sink.Initialize(&sink, buffsize);
+
+ void* buff = sink.GetPayloadBuffer(data.length());
+ memcpy(buff, data.data(), data.length());
+
+ size_t actual;
+ size_t count = 0;
+ while (count < data.length()) {
+ void* xor_buff = xor_sink.GetBuffer(10, &actual);
+ ASSERT_EQ(actual, buffsize);
+ ASSERT_NE(xor_buff, nullptr);
+ memcpy(xor_buff, jumbled.data() + count, buffsize);
+ xor_sink.ReturnData(xor_buff, actual);
+ count += actual;
+ }
+
+ std::string answer = reinterpret_cast<char*>(sink.GetPayloadBufPtr());
+ ASSERT_EQ(answer, result);
+}
+
TEST(Snapuserd_Test, Snapshot_Metadata) {
CowSnapuserdMetadataTest harness;
harness.Setup();
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 97%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index 6bb7a39..c592257 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -47,8 +47,6 @@
static constexpr uint32_t CHUNK_SIZE = 8;
static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);
-#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
-
// This structure represents the kernel COW header.
// All the below fields should be in Little Endian format.
struct disk_header {
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
similarity index 78%
rename from fs_mgr/libsnapshot/snapuserd.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
index 57a61a7..5f4d706 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
@@ -16,11 +16,23 @@
#include "snapuserd.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <unistd.h>
+#include <algorithm>
+
#include <csignal>
#include <optional>
#include <set>
-#include <libsnapshot/snapuserd_client.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <snapuserd/snapuserd_client.h>
namespace android {
namespace snapshot {
@@ -53,6 +65,10 @@
return true;
}
+std::unique_ptr<CowReader> Snapuserd::CloneReaderForWorker() {
+ return reader_->CloneCowReader();
+}
+
bool Snapuserd::CommitMerge(int num_merge_ops) {
struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
ch->num_merge_ops += num_merge_ops;
@@ -64,7 +80,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 +282,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();
}
/*
@@ -333,7 +350,7 @@
CowHeader header;
CowOptions options;
bool metadata_found = false;
- int replace_ops = 0, zero_ops = 0, copy_ops = 0;
+ int replace_ops = 0, zero_ops = 0, copy_ops = 0, xor_ops = 0;
SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
@@ -352,7 +369,6 @@
return false;
}
- reader_->InitializeMerge();
SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
if (!MmapMetadata()) {
@@ -361,7 +377,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 +395,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 +421,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 +442,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));
}
}
@@ -445,19 +455,16 @@
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();
+ size_t pending_ordered_ops = exceptions_per_area_ - num_ops;
+ uint64_t total_ordered_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()) {
+ << " Pending copy ops for this area: " << pending_ordered_ops;
+
+ 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:
//
@@ -502,28 +509,38 @@
// 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\
+ // 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.
//===========================================================
+ uint64_t block_source = cow_op->source;
+ uint64_t block_offset = 0;
+ if (cow_op->type == kCowXorOp) {
+ block_source /= BLOCK_SZ;
+ block_offset = cow_op->source % BLOCK_SZ;
+ }
if (prev_id.has_value()) {
- if (dest_blocks.count(cow_op->new_block) || source_blocks.count(cow_op->source)) {
+ if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source) ||
+ (block_offset > 0 && source_blocks.count(block_source + 1))) {
break;
}
}
metadata_found = true;
- pending_copy_ops -= 1;
+ pending_ordered_ops -= 1;
vec.push_back(cow_op);
- dest_blocks.insert(cow_op->source);
+ dest_blocks.insert(block_source);
+ if (block_offset > 0) {
+ dest_blocks.insert(block_source + 1);
+ }
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_ordered_ops);
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
+ SNAP_LOG(DEBUG) << "Batch Merge copy-ops/xor-ops of size: " << vec.size()
<< " Area: " << vec_.size() << " Area offset: " << offset
- << " Pending-copy-ops in this area: " << pending_copy_ops;
+ << " Pending-ordered-ops in this area: " << pending_ordered_ops;
for (size_t i = 0; i < vec.size(); i++) {
struct disk_exception* de =
@@ -537,13 +554,18 @@
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
offset += sizeof(struct disk_exception);
num_ops += 1;
- copy_ops++;
+ if (cow_op->type == kCowCopyOp) {
+ copy_ops++;
+ } else { // it->second->type == kCowXorOp
+ xor_ops++;
+ }
+
if (read_ahead_feature_) {
read_ahead_ops_.push_back(cow_op);
}
SNAP_LOG(DEBUG) << num_ops << ":"
- << " Copy-op: "
+ << " Ordered-op: "
<< " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
if (num_ops == exceptions_per_area_) {
@@ -558,27 +580,27 @@
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();
}
- if (!(pending_copy_ops == 0)) {
- SNAP_LOG(ERROR)
- << "Invalid pending_copy_ops: expected: 0 found: " << pending_copy_ops;
+ if (!(pending_ordered_ops == 0)) {
+ SNAP_LOG(ERROR) << "Invalid pending_ordered_ops: expected: 0 found: "
+ << pending_ordered_ops;
return false;
}
- pending_copy_ops = exceptions_per_area_;
+ pending_ordered_ops = exceptions_per_area_;
}
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- total_copy_ops -= 1;
+ total_ordered_ops -= 1;
/*
* Split the number of ops based on the size of read-ahead buffer
* region. We need to ensure that kernel doesn't issue IO on blocks
* which are not read by the read-ahead thread.
*/
- if (read_ahead_feature_ && (total_copy_ops % num_ra_ops_per_iter == 0)) {
+ if (read_ahead_feature_ && (total_ordered_ops % num_ra_ops_per_iter == 0)) {
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
}
}
@@ -607,9 +629,9 @@
SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
<< " Num Sector: " << ChunkToSector(data_chunk_id)
<< " 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();
+ << " Copy-ops: " << copy_ops << " Xor-ops: " << xor_ops
+ << " Areas: " << vec_.size() << " Num-ops-merged: " << header.num_merge_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);
@@ -668,6 +690,74 @@
return ReadMetadata();
}
+void Snapuserd::ReadBlocksToCache(const std::string& dm_block_device,
+ const std::string& partition_name, off_t offset, size_t size) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
+ if (fd.get() == -1) {
+ SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
+ << " partition-name: " << partition_name;
+ return;
+ }
+
+ size_t remain = size;
+ off_t file_offset = offset;
+ // We pick 4M I/O size based on the fact that the current
+ // update_verifier has a similar I/O size.
+ size_t read_sz = 1024 * BLOCK_SZ;
+ std::vector<uint8_t> buf(read_sz);
+
+ while (remain > 0) {
+ size_t to_read = std::min(remain, read_sz);
+
+ if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
+ SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+ << " at offset: " << file_offset
+ << " partition-name: " << partition_name << " total-size: " << size
+ << " remain_size: " << remain;
+ return;
+ }
+
+ file_offset += to_read;
+ remain -= to_read;
+ }
+
+ SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
+ << " partition: " << partition_name << " size: " << size
+ << " offset: " << offset;
+}
+
+void Snapuserd::ReadBlocks(const std::string& partition_name, const std::string& dm_block_device) {
+ SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
+ << " Block-Device: " << dm_block_device;
+
+ uint64_t dev_sz = 0;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "Cannot open block device";
+ return;
+ }
+
+ dev_sz = get_block_device_size(fd.get());
+ if (!dev_sz) {
+ SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+ return;
+ }
+
+ int num_threads = 2;
+ size_t num_blocks = dev_sz >> BLOCK_SHIFT;
+ size_t num_blocks_per_thread = num_blocks / num_threads;
+ size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
+ off_t offset = 0;
+
+ for (int i = 0; i < num_threads; i++) {
+ std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
+ partition_name, offset, read_sz_per_thread);
+
+ offset += read_sz_per_thread;
+ }
+}
+
/*
* Entry point to launch threads
*/
@@ -696,6 +786,39 @@
std::async(std::launch::async, &WorkerThread::RunThread, worker_threads_[i].get()));
}
+ bool second_stage_init = true;
+
+ // We don't want to read the blocks during first stage init.
+ if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
+ second_stage_init = false;
+ }
+
+ if (second_stage_init) {
+ SNAP_LOG(INFO) << "Reading blocks to cache....";
+ auto& dm = DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+ } else {
+ auto parts = android::base::Split(misc_name_, "-");
+ std::string partition_name = parts[0];
+
+ const char* suffix_b = "_b";
+ const char* suffix_a = "_a";
+
+ partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+ partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+ if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+ SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+ } else {
+ ReadBlocks(partition_name, dm_block_devices.at(partition_name));
+ }
+ }
+ } else {
+ SNAP_LOG(INFO) << "Not reading block device into cache";
+ }
+
bool ret = true;
for (auto& t : threads) {
ret = t.get() && ret;
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
similarity index 90%
rename from fs_mgr/libsnapshot/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.h
index 212c78e..6388a83 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
@@ -38,10 +38,11 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
#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 {
@@ -107,6 +108,20 @@
size_t buffer_size_;
};
+class XorSink : public IByteSink {
+ public:
+ void Initialize(BufferSink* sink, size_t size);
+ void Reset();
+ void* GetBuffer(size_t requested, size_t* actual) override;
+ bool ReturnData(void* buffer, size_t len) override;
+
+ private:
+ BufferSink* bufsink_;
+ std::unique_ptr<uint8_t[]> buffer_;
+ size_t buffer_size_;
+ size_t returned_;
+};
+
class Snapuserd;
class ReadAheadThread {
@@ -116,10 +131,10 @@
bool RunThread();
private:
- void InitializeIter();
- bool IterDone();
- void IterNext();
- const CowOperation* GetIterOp();
+ void InitializeRAIter();
+ bool RAIterDone();
+ void RAIterNext();
+ const CowOperation* GetRAOpIter();
void InitializeBuffer();
bool InitializeFds();
@@ -129,7 +144,7 @@
}
bool ReadAheadIOStart();
- void PrepareReadAhead(uint64_t* source_block, int* pending_ops, std::vector<uint64_t>& blocks);
+ void PrepareReadAhead(uint64_t* source_offset, int* pending_ops, std::vector<uint64_t>& blocks);
bool ReconstructDataFromCow();
void CheckOverlap(const CowOperation* cow_op);
@@ -187,7 +202,9 @@
// Processing COW operations
bool ProcessCowOp(const CowOperation* cow_op);
bool ProcessReplaceOp(const CowOperation* cow_op);
+ // Handles Copy and Xor
bool ProcessCopyOp(const CowOperation* cow_op);
+ bool ProcessXorOp(const CowOperation* cow_op);
bool ProcessZeroOp();
bool ReadFromBaseDevice(const CowOperation* cow_op);
@@ -206,6 +223,7 @@
std::unique_ptr<CowReader> reader_;
BufferSink bufsink_;
+ XorSink xorsink_;
std::string cow_device_;
std::string backing_store_device_;
@@ -244,6 +262,7 @@
void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
bool InitializeWorkers();
+ std::unique_ptr<CowReader> CloneReaderForWorker();
std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
@@ -284,6 +303,7 @@
// Total number of blocks to be merged in a given read-ahead buffer region
void SetTotalRaBlocksMerged(int x) { total_ra_blocks_merged_ = x; }
int GetTotalRaBlocksMerged() { return total_ra_blocks_merged_; }
+ void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
private:
bool IsChunkIdMetadata(chunk_t chunk);
@@ -296,6 +316,10 @@
bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
struct BufferState* GetBufferState();
+ void ReadBlocks(const std::string& partition_name, const std::string& dm_block_device);
+ void ReadBlocksToCache(const std::string& dm_block_device, const std::string& partition_name,
+ off_t offset, size_t size);
+
std::string cow_device_;
std::string backing_store_device_;
std::string control_device_;
@@ -306,8 +330,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
@@ -337,6 +359,7 @@
bool merge_initiated_ = false;
bool attached_ = false;
+ bool is_socket_present_;
};
} // namespace snapshot
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 86%
rename from fs_mgr/libsnapshot/snapuserd_readahead.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
index 16d5919..3bb7a0a 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 {
@@ -172,24 +172,39 @@
}
void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(cow_op->source)) {
+ uint64_t source_block = cow_op->source;
+ uint64_t source_offset = 0;
+ if (cow_op->type == kCowXorOp) {
+ source_block /= BLOCK_SZ;
+ source_offset = cow_op->source % BLOCK_SZ;
+ }
+ if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
+ (source_offset > 0 && source_blocks_.count(source_block + 1))) {
overlap_ = true;
}
- dest_blocks_.insert(cow_op->source);
+ dest_blocks_.insert(source_block);
+ if (source_offset > 0) {
+ dest_blocks_.insert(source_block + 1);
+ }
source_blocks_.insert(cow_op->new_block);
}
-void ReadAheadThread::PrepareReadAhead(uint64_t* source_block, int* pending_ops,
+void ReadAheadThread::PrepareReadAhead(uint64_t* source_offset, int* pending_ops,
std::vector<uint64_t>& blocks) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
+ CHECK_NE(source_offset, nullptr);
- if (!IterDone() && num_ops) {
- // Get the first block
- const CowOperation* cow_op = GetIterOp();
- *source_block = cow_op->source;
- IterNext();
+ if (!RAIterDone() && num_ops) {
+ // Get the first block with offset
+ const CowOperation* cow_op = GetRAOpIter();
+ CHECK_NE(cow_op, nullptr);
+ *source_offset = cow_op->source;
+ if (cow_op->type == kCowCopyOp) {
+ *source_offset *= BLOCK_SZ;
+ }
+ RAIterNext();
num_ops -= 1;
nr_consecutive = 1;
blocks.push_back(cow_op->new_block);
@@ -201,15 +216,20 @@
/*
* Find number of consecutive blocks working backwards.
*/
- while (!IterDone() && num_ops) {
- const CowOperation* op = GetIterOp();
- if (op->source != (*source_block - nr_consecutive)) {
+ while (!RAIterDone() && num_ops) {
+ const CowOperation* op = GetRAOpIter();
+ CHECK_NE(op, nullptr);
+ uint64_t next_offset = op->source;
+ if (op->type == kCowCopyOp) {
+ next_offset *= BLOCK_SZ;
+ }
+ if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
break;
}
nr_consecutive += 1;
num_ops -= 1;
blocks.push_back(op->new_block);
- IterNext();
+ RAIterNext();
if (!overlap_) {
CheckOverlap(op);
@@ -247,12 +267,12 @@
// We are done re-constructing the mapping; however, we need to make sure
// all the COW operations to-be merged are present in the re-constructed
// mapping.
- while (!IterDone()) {
- const CowOperation* op = GetIterOp();
+ while (!RAIterDone()) {
+ const CowOperation* op = GetRAOpIter();
if (read_ahead_buffer_map.find(op->new_block) != read_ahead_buffer_map.end()) {
num_ops -= 1;
snapuserd_->SetFinalBlockMerged(op->new_block);
- IterNext();
+ RAIterNext();
} else {
// Verify that we have covered all the ops which were re-constructed
// from COW device - These are the ops which are being
@@ -312,10 +332,10 @@
source_blocks_.clear();
while (true) {
- uint64_t source_block;
+ uint64_t source_offset;
int linear_blocks;
- PrepareReadAhead(&source_block, &num_ops, blocks);
+ PrepareReadAhead(&source_offset, &num_ops, blocks);
linear_blocks = blocks.size();
if (linear_blocks == 0) {
// No more blocks to read
@@ -324,7 +344,7 @@
}
// Get the first block in the consecutive set of blocks
- source_block = source_block + 1 - linear_blocks;
+ source_offset = source_offset - (linear_blocks - 1) * BLOCK_SZ;
size_t io_size = (linear_blocks * BLOCK_SZ);
num_ops -= linear_blocks;
total_blocks_merged += linear_blocks;
@@ -358,10 +378,12 @@
// Read from the base device consecutive set of blocks in one shot
if (!android::base::ReadFullyAtOffset(backing_store_fd_,
(char*)read_ahead_buffer_ + buffer_offset, io_size,
- source_block * BLOCK_SZ)) {
- SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
- << "at block :" << source_block << " buffer_offset : " << buffer_offset
- << " io_size : " << io_size << " buf-addr : " << read_ahead_buffer_;
+ source_offset)) {
+ SNAP_PLOG(ERROR) << "Ordered-op failed. Read from backing store: "
+ << backing_store_device_ << "at block :" << source_offset / BLOCK_SZ
+ << " offset :" << source_offset % BLOCK_SZ
+ << " buffer_offset : " << buffer_offset << " io_size : " << io_size
+ << " buf-addr : " << read_ahead_buffer_;
snapuserd_->ReadAheadIOFailed();
return false;
@@ -394,10 +416,10 @@
return false;
}
- InitializeIter();
+ InitializeRAIter();
InitializeBuffer();
- while (!IterDone()) {
+ while (!RAIterDone()) {
if (!ReadAheadIOStart()) {
return false;
}
@@ -433,21 +455,21 @@
return true;
}
-void ReadAheadThread::InitializeIter() {
+void ReadAheadThread::InitializeRAIter() {
std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
read_ahead_iter_ = read_ahead_ops.rbegin();
}
-bool ReadAheadThread::IterDone() {
+bool ReadAheadThread::RAIterDone() {
std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
return read_ahead_iter_ == read_ahead_ops.rend();
}
-void ReadAheadThread::IterNext() {
+void ReadAheadThread::RAIterNext() {
read_ahead_iter_++;
}
-const CowOperation* ReadAheadThread::GetIterOp() {
+const CowOperation* ReadAheadThread::GetRAOpIter() {
return *read_ahead_iter_;
}
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..2f87557 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");
@@ -204,6 +227,7 @@
void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
+ handler->snapuserd()->SetSocketPresent(is_socket_present_);
if (!handler->snapuserd()->Start()) {
LOG(ERROR) << " Failed to launch all worker threads";
}
@@ -245,28 +269,45 @@
}
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);
+ is_socket_present_ = true;
- 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 +352,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 +366,7 @@
return;
}
- AddWatchedFd(fd);
+ AddWatchedFd(fd, POLLIN);
}
bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
@@ -422,5 +463,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..3b6ff15 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,14 +94,16 @@
private:
android::base::unique_fd sockfd_;
bool terminating_;
+ volatile bool received_socket_signal_ = false;
std::vector<struct pollfd> watched_fds_;
+ bool is_socket_present_ = false;
std::mutex lock_;
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 +120,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 +133,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 +142,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 92%
rename from fs_mgr/libsnapshot/snapuserd_worker.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index defb5bb..5d184ad 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 {
@@ -71,6 +71,39 @@
return msg->payload.buf;
}
+void XorSink::Initialize(BufferSink* sink, size_t size) {
+ bufsink_ = sink;
+ buffer_size_ = size;
+ returned_ = 0;
+ buffer_ = std::make_unique<uint8_t[]>(size);
+}
+
+void XorSink::Reset() {
+ returned_ = 0;
+}
+
+void* XorSink::GetBuffer(size_t requested, size_t* actual) {
+ if (requested > buffer_size_) {
+ *actual = buffer_size_;
+ } else {
+ *actual = requested;
+ }
+ return buffer_.get();
+}
+
+bool XorSink::ReturnData(void* buffer, size_t len) {
+ uint8_t* xor_data = reinterpret_cast<uint8_t*>(buffer);
+ uint8_t* buff = reinterpret_cast<uint8_t*>(bufsink_->GetPayloadBuffer(len + returned_));
+ if (buff == nullptr) {
+ return false;
+ }
+ for (size_t i = 0; i < len; i++) {
+ buff[returned_ + i] ^= xor_data[i];
+ }
+ returned_ += len;
+ return true;
+}
+
WorkerThread::WorkerThread(const std::string& cow_device, const std::string& backing_device,
const std::string& control_device, const std::string& misc_name,
std::shared_ptr<Snapuserd> snapuserd) {
@@ -105,11 +138,11 @@
}
bool WorkerThread::InitReader() {
- reader_ = std::make_unique<CowReader>();
+ reader_ = snapuserd_->CloneReaderForWorker();
+
if (!reader_->InitForMerge(std::move(cow_fd_))) {
return false;
}
-
return true;
}
@@ -150,10 +183,19 @@
}
SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
<< " Source: " << cow_op->source;
- if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ,
- cow_op->source * BLOCK_SZ)) {
- SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
- << "at block :" << cow_op->source;
+ uint64_t offset = cow_op->source;
+ if (cow_op->type == kCowCopyOp) {
+ offset *= BLOCK_SZ;
+ }
+ if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
+ std::string op;
+ if (cow_op->type == kCowCopyOp)
+ op = "Copy-op";
+ else {
+ op = "Xor-op";
+ }
+ SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
+ << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
return false;
}
@@ -188,6 +230,23 @@
return true;
}
+bool WorkerThread::ProcessXorOp(const CowOperation* cow_op) {
+ if (!GetReadAheadPopulatedBuffer(cow_op)) {
+ SNAP_LOG(DEBUG) << " GetReadAheadPopulatedBuffer failed..."
+ << " new_block: " << cow_op->new_block;
+ if (!ReadFromBaseDevice(cow_op)) {
+ return false;
+ }
+ }
+ xorsink_.Reset();
+ if (!reader_->ReadData(*cow_op, &xorsink_)) {
+ SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block;
+ return false;
+ }
+
+ return true;
+}
+
bool WorkerThread::ProcessZeroOp() {
// Zero out the entire block
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
@@ -219,6 +278,10 @@
return ProcessCopyOp(cow_op);
}
+ case kCowXorOp: {
+ return ProcessXorOp(cow_op);
+ }
+
default: {
SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
}
@@ -490,10 +553,10 @@
}
int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- int unmerged_exceptions, bool* copy_op, bool* commit) {
+ int unmerged_exceptions, bool* ordered_op, bool* commit) {
int merged_ops_cur_iter = 0;
std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
- *copy_op = false;
+ *ordered_op = false;
std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
// Find the operations which are merged in this cycle.
@@ -531,9 +594,9 @@
}
const CowOperation* cow_op = it->second;
- if (snapuserd_->IsReadAheadFeaturePresent() && cow_op->type == kCowCopyOp) {
- *copy_op = true;
- // Every single copy operation has to come from read-ahead
+ if (snapuserd_->IsReadAheadFeaturePresent() && IsOrderedOp(*cow_op)) {
+ *ordered_op = true;
+ // Every single ordered operation has to come from read-ahead
// cache.
if (read_ahead_buffer_map.find(cow_op->new_block) == read_ahead_buffer_map.end()) {
SNAP_LOG(ERROR)
@@ -577,7 +640,7 @@
bool WorkerThread::ProcessMergeComplete(chunk_t chunk, void* buffer) {
uint32_t stride = exceptions_per_area_ + 1;
const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
- bool copy_op = false;
+ bool ordered_op = false;
bool commit = false;
// ChunkID to vector index
@@ -602,7 +665,7 @@
}
int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset,
- unmerged_exceptions, ©_op, &commit);
+ unmerged_exceptions, &ordered_op, &commit);
// There should be at least one operation merged in this cycle
if (!(merged_ops_cur_iter > 0)) {
@@ -610,7 +673,7 @@
return false;
}
- if (copy_op) {
+ if (ordered_op) {
if (commit) {
// Push the flushing logic to read-ahead thread so that merge thread
// can make forward progress. Sync will happen in the background
@@ -839,6 +902,7 @@
bool WorkerThread::RunThread() {
InitializeBufsink();
+ xorsink_.Initialize(&bufsink_, BLOCK_SZ);
if (!InitializeFds()) {
return false;
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 671de9d..e97afed 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -57,14 +57,14 @@
// Automatically unmap a device upon deletion.
struct AutoUnmapDevice : AutoDevice {
// On destruct, delete |name| from device mapper.
- AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
+ AutoUnmapDevice(android::dm::IDeviceMapper* dm, const std::string& name)
: AutoDevice(name), dm_(dm) {}
AutoUnmapDevice(AutoUnmapDevice&& other) = default;
~AutoUnmapDevice();
private:
DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
- android::dm::DeviceMapper* dm_ = nullptr;
+ android::dm::IDeviceMapper* dm_ = nullptr;
};
// Automatically unmap an image upon deletion.
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index 5b07168..42abd09 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -6,6 +6,8 @@
cc_library_headers {
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 953574b..94e1abb 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -120,7 +120,7 @@
};
const std::string bootconfig =
- "androidboot.bootdevice = \" \"1d84000.ufshc\"\n"
+ "androidboot.bootdevice = \"1d84000.ufshc\"\n"
"androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
"androidboot.baseband = \"sdy\"\n"
"androidboot.keymaster = \"1\"\n"
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 95e814b..a7f0c0e 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -43,8 +43,8 @@
"libhidlbase",
"android.hardware.gatekeeper@1.0",
"libgatekeeper_aidl",
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.security.authorization-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.security.authorization-ndk",
],
static_libs: ["libscrypt_static"],
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/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 50eee19..9a47f6b 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -46,14 +46,6 @@
HealthdDraw::HealthdDraw(animation* anim)
: kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
- int ret = gr_init();
-
- if (ret < 0) {
- LOGE("gr_init failed\n");
- graphics_available = false;
- return;
- }
-
graphics_available = true;
sys_font = gr_sys_font();
if (sys_font == nullptr) {
@@ -235,3 +227,11 @@
LOGW("Charging, level unknown\n");
}
}
+
+std::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) {
+ if (gr_init() < 0) {
+ LOGE("gr_init failed\n");
+ return nullptr;
+ }
+ return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim));
+}
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 7c847bd..0b48ce8 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -26,8 +26,6 @@
class HealthdDraw {
public:
- // Configures font using given animation.
- HealthdDraw(animation* anim);
virtual ~HealthdDraw();
// Redraws screen.
@@ -36,6 +34,8 @@
// Blanks screen if true, unblanks if false.
virtual void blank_screen(bool blank);
+ static std::unique_ptr<HealthdDraw> Create(animation *anim);
+
protected:
virtual void clear_screen();
@@ -76,6 +76,10 @@
// true if minui init'ed OK, false if minui init failed
bool graphics_available;
+
+ private:
+ // Configures font using given animation.
+ HealthdDraw(animation* anim);
};
#endif // HEALTHD_DRAW_H
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index e95efc0..3ea90b0 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -218,9 +218,7 @@
char* ptr;
size_t len;
- LOGW("\n");
LOGW("*************** LAST KMSG ***************\n");
- LOGW("\n");
const char* kmsg[] = {
// clang-format off
"/sys/fs/pstore/console-ramoops-0",
@@ -263,9 +261,7 @@
}
out:
- LOGW("\n");
LOGW("************* END LAST KMSG *************\n");
- LOGW("\n");
}
static int request_suspend(bool enable) {
@@ -325,7 +321,8 @@
}
}
- healthd_draw_.reset(new HealthdDraw(&batt_anim_));
+ healthd_draw_ = HealthdDraw::Create(&batt_anim_);
+ if (healthd_draw_ == nullptr) return;
if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
healthd_draw_->blank_screen(true);
diff --git a/init/Android.bp b/init/Android.bp
index 7eeafa2..66427dc 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -89,7 +89,19 @@
"host_init_verifier.cpp",
]
-cc_defaults {
+soong_config_module_type {
+ name: "libinit_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT",
+ ],
+ properties: [
+ "cflags",
+ ],
+}
+
+libinit_cc_defaults {
name: "init_defaults",
sanitize: {
misc_undefined: ["signed-integer-overflow"],
@@ -109,6 +121,7 @@
"-DDUMP_ON_UMOUNT_FAILURE=0",
"-DSHUTDOWN_ZERO_TIMEOUT=0",
"-DINIT_FULL_SOURCES",
+ "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0",
],
product_variables: {
debuggable: {
@@ -137,6 +150,14 @@
cppflags: ["-DUSER_MODE_LINUX"],
},
},
+ soong_config_variables: {
+ PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT: {
+ cflags: [
+ "-UINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT",
+ "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=1",
+ ],
+ },
+ },
static_libs: [
"libavb",
"libc++fs",
@@ -228,17 +249,20 @@
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",
+ "extra_free_kbytes.sh",
+ "make_f2fs",
+ "mke2fs",
+ "sload_f2fs",
+ ],
+ },
recovery: {
cflags: ["-DRECOVERY"],
exclude_static_libs: [
@@ -248,16 +272,45 @@
"libbinder",
"libutils",
],
+ required: [
+ "init_recovery.rc",
+ "ueventd.rc.recovery",
+ "e2fsdroid.recovery",
+ "make_f2fs.recovery",
+ "mke2fs.recovery",
+ "sload_f2fs.recovery",
+ ],
},
},
visibility: ["//packages/modules/Virtualization/microdroid"],
}
-// This currently is only for the VM usecase.
-// TODO(jiyong): replace init_first_stage in Android.mk with this
+soong_config_module_type {
+ name: "init_first_stage_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: ["BOARD_BUILD_SYSTEM_ROOT_IMAGE", "BOARD_USES_RECOVERY_AS_BOOT"],
+ properties: ["installable"],
+}
+
+// Do not install init_first_stage even with mma if we're system-as-root.
+// Otherwise, it will overwrite the symlink.
+init_first_stage_cc_defaults {
+ name: "init_first_stage_defaults",
+ soong_config_variables: {
+ BOARD_BUILD_SYSTEM_ROOT_IMAGE: {
+ installable: false,
+ },
+ BOARD_USES_RECOVERY_AS_BOOT: {
+ installable: false,
+ },
+ },
+}
+
cc_binary {
- name: "init_first_stage_soong",
- stem: "init_vendor",
+ name: "init_first_stage",
+ stem: "init",
+ defaults: ["init_first_stage_defaults"],
srcs: [
"block_dev_initializer.cpp",
@@ -268,7 +321,6 @@
"first_stage_mount.cpp",
"reboot_utils.cpp",
"selabel.cpp",
- "selinux.cpp",
"service_utils.cpp",
"snapuserd_transition.cpp",
"switch_root.cpp",
@@ -283,23 +335,16 @@
"libfec",
"libfec_rs",
"libsquashfs_utils",
- "liblogwrap",
- "libext4_utils",
"libcrypto_utils",
- "libsparse",
"libavb",
- "libkeyutils",
"liblp",
"libcutils",
"libbase",
"liblog",
"libcrypto_static",
- "libdl",
- "libz",
"libselinux",
"libcap",
"libgsi",
- "libcom.android.sysprop.apex",
"liblzma",
"libunwindstack_no_dex",
"libbacktrace_no_dex",
@@ -313,6 +358,7 @@
],
static_executable: true,
+ system_shared_libs: [],
cflags: [
"-Wall",
@@ -363,8 +409,23 @@
sanitize: {
misc_undefined: ["signed-integer-overflow"],
+
+ // First stage init is weird: it may start without stdout/stderr, and no /proc.
hwaddress: false,
},
+
+ // Install adb_debug.prop into debug ramdisk.
+ // This allows adb root on a user build, when debug ramdisk is used.
+ required: ["adb_debug.prop"],
+
+ ramdisk: true,
+
+ install_in_root: true,
+}
+
+phony {
+ name: "init_system",
+ required: ["init_second_stage"],
}
// Tests
@@ -507,3 +568,8 @@
},
},
}
+
+sh_binary {
+ name: "extra_free_kbytes.sh",
+ src: "extra_free_kbytes.sh",
+}
diff --git a/init/Android.mk b/init/Android.mk
index 3c7d95a..c08fe03 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -2,153 +2,6 @@
LOCAL_PATH:= $(call my-dir)
--include system/sepolicy/policy_version.mk
-
-# --
-
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-init_options += \
- -DALLOW_FIRST_STAGE_CONSOLE=1 \
- -DALLOW_LOCAL_PROP_OVERRIDE=1 \
- -DALLOW_PERMISSIVE_SELINUX=1 \
- -DREBOOT_BOOTLOADER_ON_PANIC=1 \
- -DWORLD_WRITABLE_KMSG=1 \
- -DDUMP_ON_UMOUNT_FAILURE=1
-else
-init_options += \
- -DALLOW_FIRST_STAGE_CONSOLE=0 \
- -DALLOW_LOCAL_PROP_OVERRIDE=0 \
- -DALLOW_PERMISSIVE_SELINUX=0 \
- -DREBOOT_BOOTLOADER_ON_PANIC=0 \
- -DWORLD_WRITABLE_KMSG=0 \
- -DDUMP_ON_UMOUNT_FAILURE=0
-endif
-
-ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
-init_options += \
- -DSHUTDOWN_ZERO_TIMEOUT=1
-else
-init_options += \
- -DSHUTDOWN_ZERO_TIMEOUT=0
-endif
-
-init_options += -DLOG_UEVENTS=0 \
- -DSEPOLICY_VERSION=$(POLICYVERS)
-
-init_cflags += \
- $(init_options) \
- -Wall -Wextra \
- -Wno-unused-parameter \
- -Werror \
-
-# --
-
-# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink.
-ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES := \
- block_dev_initializer.cpp \
- devices.cpp \
- first_stage_console.cpp \
- first_stage_init.cpp \
- first_stage_main.cpp \
- first_stage_mount.cpp \
- reboot_utils.cpp \
- selabel.cpp \
- selinux.cpp \
- service_utils.cpp \
- snapuserd_transition.cpp \
- switch_root.cpp \
- uevent_listener.cpp \
- util.cpp \
-
-LOCAL_MODULE := init_first_stage
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-LOCAL_MODULE_STEM := init
-
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
-
-# Install adb_debug.prop into debug ramdisk.
-# This allows adb root on a user build, when debug ramdisk is used.
-LOCAL_REQUIRED_MODULES := \
- adb_debug.prop \
-
-# Set up the directories that first stage init mounts on.
-
-my_ramdisk_dirs := \
- debug_ramdisk \
- dev \
- metadata \
- mnt \
- proc \
- second_stage_resources \
- sys \
-
-LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(my_ramdisk_dirs))
-ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
- LOCAL_POST_INSTALL_CMD += $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(my_ramdisk_dirs))
-endif
-
-my_ramdisk_dirs :=
-
-LOCAL_STATIC_LIBRARIES := \
- libc++fs \
- libfs_avb \
- libfs_mgr \
- libfec \
- libfec_rs \
- libsquashfs_utils \
- liblogwrap \
- libext4_utils \
- libcrypto_utils \
- libsparse \
- libavb \
- libkeyutils \
- liblp \
- libcutils \
- libbase \
- liblog \
- libcrypto_static \
- libdl \
- libz \
- libselinux \
- libcap \
- libgsi \
- libcom.android.sysprop.apex \
- liblzma \
- libunwindstack_no_dex \
- libbacktrace_no_dex \
- libmodprobe \
- libext2_uuid \
- libprotobuf-cpp-lite \
- libsnapshot_cow \
- libsnapshot_init \
- update_metadata-protos \
- libprocinfo \
-
-LOCAL_SANITIZE := signed-integer-overflow
-# First stage init is weird: it may start without stdout/stderr, and no /proc.
-LOCAL_NOSANITIZE := hwaddress
-include $(BUILD_EXECUTABLE)
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := init_system
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-LOCAL_REQUIRED_MODULES := \
- init_second_stage \
-
-include $(BUILD_PHONY_PACKAGE)
-
include $(CLEAR_VARS)
LOCAL_MODULE := init_vendor
@@ -156,8 +9,10 @@
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
LOCAL_REQUIRED_MODULES := \
init_first_stage \
-endif
+endif # BOARD_USES_RECOVERY_AS_BOOT
+endif # BOARD_BUILD_SYSTEM_ROOT_IMAGE
include $(BUILD_PHONY_PACKAGE)
diff --git a/init/README.md b/init/README.md
index 4a262c9..58a8d6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -404,6 +404,33 @@
3. Any time that property c transitions to value d, while property a already equals b.
+Trigger Sequence
+----------------
+
+Init uses the following sequence of triggers during early boot. These are the
+built-in triggers defined in init.cpp.
+
+ 1. `early-init` - The first in the sequence, triggered after cgroups has been configured
+ but before ueventd's coldboot is complete.
+ 2. `init` - Triggered after coldboot is complete.
+ 3. `charger` - Triggered if `ro.bootmode == "charger"`.
+ 4. `late-init` - Triggered if `ro.bootmode != "charger"`, or via healthd triggering a boot
+ from charging mode.
+
+Remaining triggers are configured in `init.rc` and are not built-in. The default sequence for
+these is specified under the "on late-init" event in `init.rc`. Actions internal to `init.rc`
+have been omitted.
+
+ 1. `early-fs` - Start vold.
+ 2. `fs` - Vold is up. Mount partitions not marked as first-stage or latemounted.
+ 3. `post-fs` - Configure anything dependent on early mounts.
+ 4. `late-fs` - Mount partitions marked as latemounted.
+ 5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is
+ reformatted here if it couldn't mount in first-stage init.
+ 6. `zygote-start` - Start the zygote.
+ 7. `early-boot` - After zygote has started.
+ 8. `boot` - After `early-boot` actions have completed.
+
Commands
--------
@@ -666,7 +693,7 @@
fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
under /odm/etc, /vendor/etc, or / at runtime, in that order.
-`verity_update_state <mount-point>`
+`verity_update_state`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
because fs\_mgr can't set them directly itself.
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index d22f68f..2401da3 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -123,7 +123,10 @@
The exact firmware file to be served can be customized by running an external program by a
`external_firmware_handler` line in a ueventd.rc file. This line takes the format of
- external_firmware_handler <devpath> <user name to run as> <path to external program>
+ external_firmware_handler <devpath> <user [group]> <path to external program>
+
+The handler will be run as the given user, or if a group is provided, as the given user and group.
+
For example
external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin
@@ -160,3 +163,13 @@
from enabling the parallelization option:
parallel_restorecon enabled
+
+Do parallel restorecon to speed up boot process, subdirectories under `/sys`
+can be sliced by ueventd.rc, and run on multiple process.
+ parallel_restorecon_dir <directory>
+
+For example
+ parallel_restorecon_dir /sys
+ parallel_restorecon_dir /sys/devices
+ parallel_restorecon_dir /sys/devices/platform
+ parallel_restorecon_dir /sys/devices/platform/soc
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
new file mode 100644
index 0000000..03b9eaa
--- /dev/null
+++ b/init/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsInitTestCases"
+ },
+ {
+ "name": "init_kill_services_test"
+ },
+ {
+ "name": "MicrodroidHostTestCases"
+ }
+ ]
+}
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 9c2a7bb..05e00ed 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -87,7 +87,13 @@
auto iter = devices->find(name);
if (iter == devices->end()) {
- return ListenerAction::kContinue;
+ auto partition_name = DeviceHandler::GetPartitionNameForDevice(uevent.device_name);
+ if (!partition_name.empty()) {
+ iter = devices->find(partition_name);
+ }
+ if (iter == devices->end()) {
+ return ListenerAction::kContinue;
+ }
}
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 035038f..994eed9 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -894,9 +894,11 @@
std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
SetProperty("partition." + partition + ".verified", std::to_string(mode));
- std::string hash_alg = fs_mgr_get_hashtree_algorithm(entry);
- if (!hash_alg.empty()) {
- SetProperty("partition." + partition + ".verified.hash_alg", hash_alg);
+ auto hashtree_info = fs_mgr_get_hashtree_info(entry);
+ if (hashtree_info) {
+ SetProperty("partition." + partition + ".verified.hash_alg", hashtree_info->algorithm);
+ SetProperty("partition." + partition + ".verified.root_digest",
+ hashtree_info->root_digest);
}
}
@@ -1305,20 +1307,8 @@
return {};
}
-
-static bool IsApexUpdatable() {
- static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
- return updatable;
-}
-
static Result<void> do_update_linker_config(const BuiltinArguments&) {
- // If APEX is not updatable, then all APEX information are already included in the first
- // linker config generation, so there is no need to update linker configuration again.
- if (IsApexUpdatable()) {
- return GenerateLinkerConfiguration();
- }
-
- return {};
+ return GenerateLinkerConfiguration();
}
static Result<void> parse_apex_configs() {
diff --git a/init/devices.cpp b/init/devices.cpp
index ce6298a..d4a3cb9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -46,6 +46,7 @@
using android::base::ReadFileToString;
using android::base::Readlink;
using android::base::Realpath;
+using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::Trim;
@@ -187,6 +188,36 @@
}
}
+std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) {
+ static const auto partition_map = [] {
+ std::vector<std::pair<std::string, std::string>> partition_map;
+ auto parser = [&partition_map](const std::string& key, const std::string& value) {
+ if (key != "androidboot.partition_map") {
+ return;
+ }
+ for (const auto& map : Split(value, ";")) {
+ auto map_pieces = Split(map, ",");
+ if (map_pieces.size() != 2) {
+ LOG(ERROR) << "Expected a comma separated device,partition mapping, but found '"
+ << map << "'";
+ continue;
+ }
+ partition_map.emplace_back(map_pieces[0], map_pieces[1]);
+ }
+ };
+ ImportKernelCmdline(parser);
+ ImportBootconfig(parser);
+ return partition_map;
+ }();
+
+ for (const auto& [device, partition] : partition_map) {
+ if (query_device == device) {
+ return partition;
+ }
+ }
+ return {};
+}
+
// Given a path that may start with a platform device, find the parent platform device by finding a
// parent directory with a 'subsystem' symlink that points to the platform bus.
// If it doesn't start with a platform device, return false
@@ -264,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
@@ -291,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";
}
@@ -376,6 +420,10 @@
// If we don't have a partition name but we are a partition on a boot device, create a
// symlink of /dev/block/by-name/<device_name> for symmetry.
links.emplace_back("/dev/block/by-name/" + uevent.device_name);
+ auto partition_name = GetPartitionNameForDevice(uevent.device_name);
+ if (!partition_name.empty()) {
+ links.emplace_back("/dev/block/by-name/" + partition_name);
+ }
}
auto last_slash = uevent.path.rfind('/');
diff --git a/init/devices.h b/init/devices.h
index d70d746..f9f4d79 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -122,6 +122,12 @@
std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+ // `androidboot.partition_map` allows associating a partition name for a raw block device
+ // through a comma separated and semicolon deliminated list. For example,
+ // `androidboot.partition_map=vdb,metadata;vdc,userdata` maps `vdb` to `metadata` and `vdc` to
+ // `userdata`.
+ static std::string GetPartitionNameForDevice(const std::string& device);
+
private:
bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
diff --git a/init/extra_free_kbytes.sh b/init/extra_free_kbytes.sh
new file mode 100755
index 0000000..aeaa912
--- /dev/null
+++ b/init/extra_free_kbytes.sh
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+# Script implements watermark_scale calculation which results in the same low
+# watermark as if extra_free_kbytes tunable were to be used.
+#
+# Usage: extra_free_kbytes.sh <extra_free_kbytes value>
+#
+# extra_free_kbytes is distributed between zones based on
+# zone.managed_pages/vm_total_pages ratio, where vm_total_pages is the sum of
+# zone.managed_pages for all zones (zone.high used in this calculation is 0
+# when this is calculated). Therefore for each zone its share is calculated as:
+#
+# extra_free_pages = extra_free_kbytes / page_size
+# extra_share = extra_free_pages * managed_pages / vm_total_pages
+#
+# This extra_share is added to the low and high watermarks:
+#
+# low = min + max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
+# high = min + 2 * max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
+#
+# Because Android uses extra_free_kbytes to adjust the low watermark, we ignore
+# the difference in how watermark_scale and extra_free_kbytes affect the high
+# watermark and will match the low watermark only.
+#
+# To eliminate extra_share and compansate the difference with watermark_scale,
+# a new watermark_scale_new is calculated as:
+#
+# (1) max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share =
+# max(min / 4, managed_pages * (watermark_scale_new / 10000))
+#
+# Two cases to consider:
+# A. managed_pages * (watermark_scale / 10000) > min / 4
+# The formula (1) becomes:
+#
+# managed_pages * (watermark_scale / 10000) + extra_share =
+# managed_pages * (watermark_scale_new / 10000)
+#
+# after simplifying and substituting extra_share formula becomes:
+#
+# (2) watermark_scale_new = watermark_scale + extra_free_pages / vm_total_pages * 10000
+#
+# B. managed_pages * (watermark_scale / 10000) < min / 4
+# The formula (1) becomes:
+#
+# min / 4 + extra_share = max(min / 4, managed_pages * (watermark_scale_new / 10000))
+#
+# after calculating watermark_scale_new, if (managed_pages * (watermark_scale_new / 10000))
+# is still smaller than min / 4 then we can't compensate extra_share with
+# watermark_scale anyway. Therefore calculation becomes:
+#
+# watermark_scale_new = (min / 4 + extra_share) / managed_pages * 10000
+#
+# after simplifying and substituting extra_share formula becomes:
+#
+# (3) watermark_scale_new = (min / 4) * 10000 / managed_pages + extra_free_pages / vm_total_pages * 10000
+#
+# After defining watermark_delta = extra_free_pages / vm_total_pages * 10000:
+#
+# if (managed_pages * (watermark_scale / 10000) > min / 4)
+# watermark_scale_new = watermark_scale + watermark_delta
+# else
+# watermark_scale_new = (min / 4) * 10000 / managed_pages + watermark_delta
+#
+
+if [ "$#" -ne 1 ]
+then
+ echo "Usage: $0 <extra_free_kbytes value>"
+ exit
+fi
+
+extra_free_kbytes=$1
+
+# if extra_free_kbytes knob exists, use it and exit
+if [ -e /proc/sys/vm/extra_free_kbytes ]
+then
+ echo $extra_free_kbytes > /proc/sys/vm/extra_free_kbytes
+ exit
+fi
+
+watermark_scale=`cat /proc/sys/vm/watermark_scale_factor`
+
+# convert extra_free_kbytes to pages
+page_size=$(getconf PAGESIZE)
+page_size_kb=$((page_size/1024))
+extra_free_pg=$((extra_free_kbytes/page_size_kb))
+
+managed=($(grep managed /proc/zoneinfo | awk '{print $2}'))
+length=${#managed[@]}
+min=($(grep "min" /proc/zoneinfo | awk '{print $2}'))
+
+# calculate vm_total_pages.
+# WARNING: if the final low watermark differs from the original, the source of
+# the error is likely vm_total_pages which is impossible to get exact from the
+# userspace. Grep for "Total pages" in the kernel logs to see the actual
+# vm_total_pages and plug it in the calculation to confirm the source of the
+# error. Error caused by this inaccuracy is normally within 1% range.
+vm_total_pages=0
+i=0
+while [ $i -lt $length ]
+do
+ vm_total_pages=$((vm_total_pages + managed[i]))
+ i=$((i+1))
+done
+
+# calculate watermark_scale_new for each zone and choose the max
+max_watermark_scale=0
+i=0
+while [ $i -lt $length ]
+do
+ # skip unmanaged zones
+ if [ ${managed[i]} -eq 0 ]
+ then
+ i=$((i+1))
+ continue
+ fi
+
+ base_margin=$((min[i] / 4))
+ calc_margin=$(echo "${managed[i]} * $watermark_scale / 10000" | bc)
+ # round the value by adding 0.5 and truncating the decimal part
+ watermark_delta=$(echo "x=($extra_free_pg / ($vm_total_pages / 10000) + 0.5); scale = 0; x/1" | bc -l)
+ if [ $calc_margin -gt $base_margin ]
+ then
+ watermark_scale_new=$(echo "$watermark_scale + $watermark_delta" | bc)
+ else
+ watermark_scale_new=$(echo "$base_margin / (${managed[i]} / 10000) + $watermark_delta" | bc)
+ fi
+
+ if [ $max_watermark_scale -lt $watermark_scale_new ]
+ then
+ max_watermark_scale=$watermark_scale_new
+ fi
+
+ i=$((i+1))
+done
+
+echo $max_watermark_scale > /proc/sys/vm/watermark_scale_factor
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index bdc2922..30e808d 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -19,6 +19,7 @@
#include <fcntl.h>
#include <fnmatch.h>
#include <glob.h>
+#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
@@ -81,9 +82,9 @@
return access("/dev/.booting", F_OK) == 0;
}
-ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
+ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
std::string handler_path)
- : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {
+ : devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
auto wildcard_position = this->devpath.find('*');
if (wildcard_position != std::string::npos) {
if (wildcard_position == this->devpath.length() - 1) {
@@ -97,13 +98,17 @@
}
}
+ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
+ std::string handler_path)
+ : ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}
+
FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
std::vector<ExternalFirmwareHandler> external_firmware_handlers)
: firmware_directories_(std::move(firmware_directories)),
external_firmware_handlers_(std::move(external_firmware_handlers)) {}
Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid,
- const Uevent& uevent) const {
+ gid_t gid, const Uevent& uevent) const {
unique_fd child_stdout;
unique_fd parent_stdout;
if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {
@@ -140,6 +145,13 @@
}
c_args.emplace_back(nullptr);
+ if (gid != 0) {
+ if (setgid(gid) != 0) {
+ fprintf(stderr, "setgid() failed: %s", strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+ }
+
if (setuid(uid) != 0) {
fprintf(stderr, "setuid() failed: %s", strerror(errno));
_exit(EXIT_FAILURE);
@@ -196,8 +208,8 @@
<< "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
<< "'";
- auto result =
- RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
+ auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
+ external_handler.gid, uevent);
if (!result.ok()) {
LOG(ERROR) << "Using default firmware; External firmware handler failed: "
<< result.error();
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 3c35b1f..d2f7347 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -16,6 +16,7 @@
#pragma once
+#include <grp.h>
#include <pwd.h>
#include <functional>
@@ -31,9 +32,11 @@
struct ExternalFirmwareHandler {
ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path);
+ ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid, std::string handler_path);
std::string devpath;
uid_t uid;
+ gid_t gid;
std::string handler_path;
std::function<bool(const std::string&)> match;
@@ -51,7 +54,7 @@
friend void FirmwareTestWithExternalHandler(const std::string& test_name,
bool expect_new_firmware);
- Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid,
+ Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid, gid_t gid,
const Uevent& uevent) const;
std::string GetFirmwarePath(const Uevent& uevent) const;
void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index e2ea0ab..67cac19 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -85,7 +85,10 @@
void StartConsole(const std::string& cmdline) {
bool console = KernelConsolePresent(cmdline);
+ // Use a simple sigchld handler -- first_stage_console doesn't need to track or log zombies
+ const struct sigaction chld_act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDWAIT };
+ sigaction(SIGCHLD, &chld_act, nullptr);
pid_t pid = fork();
if (pid != 0) {
int status;
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 78e5b60..c7b7b0c 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -330,14 +330,21 @@
// If "/force_debuggable" is present, the second-stage init will use a userdebug
// sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
if (access("/force_debuggable", F_OK) == 0) {
+ constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";
+ constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
- if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
- !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
- LOG(ERROR) << "Failed to setup debug ramdisk";
- } else {
- // setenv for second-stage init to read above kDebugRamdisk* files.
- setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+ if (access(adb_debug_prop_src, F_OK) == 0 &&
+ !fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {
+ LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
+ << ": " << ec.message();
}
+ if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
+ !fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {
+ LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
+ << kDebugRamdiskSEPolicy << ": " << ec.message();
+ }
+ // setenv for second-stage init to read above kDebugRamdisk* files.
+ setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
if (ForceNormalBoot(cmdline, bootconfig)) {
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
index 6afcb17..41c86ac 100755
--- a/init/host_builtin_map.py
+++ b/init/host_builtin_map.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
"""Generates the builtins map to be used by host_init_verifier.
It copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the
@@ -39,8 +39,7 @@
match = DO_REGEX.match(line)
if match:
if match.group(1) in check_functions:
- print line.replace('do_', 'check_'),
+ line = line.replace('do_', 'check_')
else:
- print FUNCTION_REGEX.sub('check_stub', line),
- else:
- print line,
+ line = FUNCTION_REGEX.sub('check_stub', line)
+ print(line, end=' ')
diff --git a/init/init.cpp b/init/init.cpp
index 942feb9..e3596cb 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -739,6 +739,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();
@@ -867,6 +901,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 2a57808..bce1cc3 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -82,110 +82,16 @@
return updatable;
}
-#ifdef ACTIVATE_FLATTENED_APEX
-
-static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
- if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
- return ErrnoError() << "Could not create mount point " << mount_path;
- }
- if (mount(path.c_str(), mount_path.c_str(), nullptr, MS_BIND, nullptr) != 0) {
- return ErrnoError() << "Could not bind mount " << path << " to " << mount_path;
- }
- return {};
-}
-
-static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
- const std::string manifest_path = apex_dir + "/apex_manifest.pb";
- std::string content;
- if (!android::base::ReadFileToString(manifest_path, &content)) {
- return Error() << "Failed to read manifest file: " << manifest_path;
- }
- apex::proto::ApexManifest manifest;
- if (!manifest.ParseFromString(content)) {
- return Error() << "Can't parse manifest file: " << manifest_path;
- }
- return manifest;
-}
-
-template <typename Fn>
-static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
- const std::string& to_dir, Fn on_activate) {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
- if (!dir) {
- return {};
- }
- dirent* entry;
- std::vector<std::string> entries;
-
- while ((entry = readdir(dir.get())) != nullptr) {
- if (entry->d_name[0] == '.') continue;
- if (entry->d_type == DT_DIR) {
- entries.push_back(entry->d_name);
- }
- }
-
- std::sort(entries.begin(), entries.end());
- for (const auto& name : entries) {
- const std::string apex_path = from_dir + "/" + name;
- const auto apex_manifest = GetApexManifest(apex_path);
- if (!apex_manifest.ok()) {
- LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
- continue;
- }
- const std::string mount_path = to_dir + "/" + apex_manifest->name();
- if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
- return result;
- }
- on_activate(apex_path, *apex_manifest);
- }
- return {};
-}
-
-static bool ActivateFlattenedApexesIfPossible() {
- if (IsRecoveryMode() || IsApexUpdatable()) {
- return true;
- }
-
- const std::string kApexTop = "/apex";
- const std::vector<std::string> kBuiltinDirsForApexes = {
- "/system/apex",
- "/system_ext/apex",
- "/product/apex",
- "/vendor/apex",
- };
-
- std::vector<com::android::apex::ApexInfo> apex_infos;
- 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,
- /* lastUpdateMillis= */ 0);
- };
-
- for (const auto& dir : kBuiltinDirsForApexes) {
- if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
- LOG(ERROR) << result.error();
- return false;
- }
- }
-
- std::ostringstream oss;
- com::android::apex::ApexInfoList apex_info_list(apex_infos);
- com::android::apex::write(oss, apex_info_list);
- const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
- if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
- PLOG(ERROR) << "Failed to write " << kApexInfoList;
- return false;
- }
- if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
- PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
- }
-
+// 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;
}
-#endif // ACTIVATE_FLATTENED_APEX
-
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -260,7 +166,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) {
@@ -279,9 +185,7 @@
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
}
-#ifdef ACTIVATE_FLATTENED_APEX
- success &= ActivateFlattenedApexesIfPossible();
-#endif
+
LOG(INFO) << "SetupMountNamespaces done";
return success;
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 2d67bf5..84ed58e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1017,6 +1017,31 @@
}
}
+static void property_initialize_ro_vendor_api_level() {
+ // ro.vendor.api_level shows the api_level that the vendor images (vendor, odm, ...) are
+ // required to support.
+ constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
+ // Candidate api levels. The order of the properties must be kept.
+ const char* VENDOR_API_LEVEL_PROPS[] = {
+ "ro.board.api_level", "ro.board.first_api_level", "ro.product.first_api_level",
+ "ro.vndk.version", "ro.vendor.build.version.sdk", "ro.build.version.sdk"};
+
+ for (const auto& api_level_prop : VENDOR_API_LEVEL_PROPS) {
+ int api_level = android::base::GetIntProperty(api_level_prop, 0);
+ if (api_level != 0) {
+ std::string error;
+ uint32_t res = PropertySet(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error);
+ if (res != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level
+ << ": " << error;
+ }
+ return;
+ }
+ }
+ // If no api integers are found from the vendor api level properties, ro.vendor.api_level
+ // will not be set.
+}
+
void PropertyLoadBootDefaults() {
// We read the properties and their values into a map, in order to always allow properties
// loaded in the later property files to override the properties in loaded in the earlier
@@ -1102,6 +1127,7 @@
property_derive_build_fingerprint();
property_derive_legacy_build_fingerprint();
property_initialize_ro_cpu_abilist();
+ property_initialize_ro_vendor_api_level();
update_sys_usb_config();
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index a0ae4b4..6aa9912 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -478,6 +478,11 @@
// cut the last "\n"
backing_dev.erase(backing_dev.length() - 1);
+ if (android::base::StartsWith(backing_dev, "none")) {
+ LOG(INFO) << "No zram backing device configured";
+ return {};
+ }
+
// shutdown zram handle
Timer swap_timer;
LOG(INFO) << "swapoff() start...";
@@ -634,6 +639,7 @@
abort();
}
+ bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts{"watchdogd"};
std::set<std::string> stop_first;
@@ -647,6 +653,8 @@
<< "': " << result.error();
}
s->SetShutdownCritical();
+ } else if (do_shutdown_animation) {
+ continue;
} else if (s->IsShutdownCritical()) {
// Start shutdown critical service if not started.
if (auto result = s->Start(); !result.ok()) {
@@ -659,14 +667,13 @@
}
// remaining operations (specifically fsck) may take a substantial duration
- if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
+ if (!do_shutdown_animation && (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown)) {
TurnOffBacklight();
}
Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
- bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
if (do_shutdown_animation) {
SetProperty("service.bootanim.exit", "0");
@@ -1030,6 +1037,20 @@
return;
}
}
+ } else if (reboot_target == "quiescent") {
+ bootloader_message boot = {};
+ if (std::string err; !read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << "Failed to read bootloader message: " << err;
+ }
+ // Update the boot command field if it's empty, and preserve
+ // the other arguments in the bootloader message.
+ if (!CommandIsPresent(&boot)) {
+ strlcpy(boot.command, "boot-quiescent", sizeof(boot.command));
+ if (std::string err; !write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ return;
+ }
+ }
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") {
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 42d3023..29c0ff3 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -295,6 +295,25 @@
return access(plat_policy_cil_file, R_OK) != -1;
}
+std::optional<const char*> GetUserdebugPlatformPolicyFile() {
+ // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
+ const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+ if (force_debuggable_env && "true"s == force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
+ const std::vector<const char*> debug_policy_candidates = {
+#if INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT == 1
+ "/system_ext/etc/selinux/userdebug_plat_sepolicy.cil",
+#endif
+ kDebugRamdiskSEPolicy,
+ };
+ for (const char* debug_policy : debug_policy_candidates) {
+ if (access(debug_policy, F_OK) == 0) {
+ return debug_policy;
+ }
+ }
+ }
+ return std::nullopt;
+}
+
struct PolicyFile {
unique_fd fd;
std::string path;
@@ -310,13 +329,10 @@
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
- // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
- const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
- bool use_userdebug_policy =
- ((force_debuggable_env && "true"s == force_debuggable_env) &&
- AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
+ const auto userdebug_plat_sepolicy = GetUserdebugPlatformPolicyFile();
+ const bool use_userdebug_policy = userdebug_plat_sepolicy.has_value();
if (use_userdebug_policy) {
- LOG(WARNING) << "Using userdebug system sepolicy";
+ LOG(INFO) << "Using userdebug system sepolicy " << *userdebug_plat_sepolicy;
}
// Load precompiled policy from vendor image, if a matching policy is found there. The policy
@@ -413,7 +429,7 @@
// clang-format off
std::vector<const char*> compile_args {
"/system/bin/secilc",
- use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,
+ use_userdebug_policy ? *userdebug_plat_sepolicy : plat_policy_cil_file,
"-m", "-M", "true", "-G", "-N",
"-c", version_as_string.c_str(),
plat_mapping_file.c_str(),
diff --git a/init/service.cpp b/init/service.cpp
index c3069f5..489dd67 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -269,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());
}
@@ -409,9 +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));
+ ResetFlagsForStart();
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
@@ -622,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();
@@ -792,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 043555f..ccf6899 100644
--- a/init/service.h
+++ b/init/service.h
@@ -99,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_; }
@@ -144,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/subcontext.cpp b/init/subcontext.cpp
index fa48bea..6eaa80f 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -44,6 +44,7 @@
#endif
using android::base::GetExecutablePath;
+using android::base::GetProperty;
using android::base::Join;
using android::base::Socketpair;
using android::base::Split;
@@ -337,6 +338,11 @@
}
void InitializeSubcontext() {
+ if (IsMicrodroid()) {
+ LOG(INFO) << "Not using subcontext for microdroid";
+ return;
+ }
+
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
subcontext.reset(
new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 331255b..68c6b51 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -115,11 +115,13 @@
public:
ColdBoot(UeventListener& uevent_listener,
std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers,
- bool enable_parallel_restorecon)
+ bool enable_parallel_restorecon,
+ std::vector<std::string> parallel_restorecon_queue)
: uevent_listener_(uevent_listener),
uevent_handlers_(uevent_handlers),
num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4),
- enable_parallel_restorecon_(enable_parallel_restorecon) {}
+ enable_parallel_restorecon_(enable_parallel_restorecon),
+ parallel_restorecon_queue_(parallel_restorecon_queue) {}
void Run();
@@ -142,6 +144,8 @@
std::set<pid_t> subprocess_pids_;
std::vector<std::string> restorecon_queue_;
+
+ std::vector<std::string> parallel_restorecon_queue_;
};
void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
@@ -155,17 +159,34 @@
}
void ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {
+ android::base::Timer t_process;
+
for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {
+ android::base::Timer t;
auto& dir = restorecon_queue_[i];
selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+
+ //Mark a dir restorecon operation for 50ms,
+ //Maybe you can add this dir to the ueventd.rc script to parallel processing
+ if (t.duration() > 50ms) {
+ LOG(INFO) << "took " << t.duration().count() <<"ms restorecon '"
+ << dir.c_str() << "' on process '" << process_num <<"'";
+ }
}
+
+ //Calculate process restorecon time
+ LOG(VERBOSE) << "took " << t_process.duration().count() << "ms on process '"
+ << process_num << "'";
}
void ColdBoot::GenerateRestoreCon(const std::string& directory) {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
- if (!dir) return;
+ if (!dir) {
+ PLOG(WARNING) << "opendir " << directory.c_str();
+ return;
+ }
struct dirent* dent;
while ((dent = readdir(dir.get())) != NULL) {
@@ -176,7 +197,10 @@
if (S_ISDIR(st.st_mode)) {
std::string fullpath = directory + "/" + dent->d_name;
- if (fullpath != "/sys/devices") {
+ auto parallel_restorecon =
+ std::find(parallel_restorecon_queue_.begin(),
+ parallel_restorecon_queue_.end(), fullpath);
+ if (parallel_restorecon == parallel_restorecon_queue_.end()) {
restorecon_queue_.emplace_back(fullpath);
}
}
@@ -248,11 +272,16 @@
RegenerateUevents();
if (enable_parallel_restorecon_) {
- selinux_android_restorecon("/sys", 0);
- selinux_android_restorecon("/sys/devices", 0);
- GenerateRestoreCon("/sys");
- // takes long time for /sys/devices, parallelize it
- GenerateRestoreCon("/sys/devices");
+ if (parallel_restorecon_queue_.empty()) {
+ parallel_restorecon_queue_.emplace_back("/sys");
+ // takes long time for /sys/devices, parallelize it
+ parallel_restorecon_queue_.emplace_back("/sys/devices");
+ LOG(INFO) << "Parallel processing directory is not set, set the default";
+ }
+ for (const auto& dir : parallel_restorecon_queue_) {
+ selinux_android_restorecon(dir.c_str(), 0);
+ GenerateRestoreCon(dir);
+ }
}
ForkSubProcesses();
@@ -268,14 +297,27 @@
}
static UeventdConfiguration GetConfiguration() {
- // TODO: Remove these legacy paths once Android S is no longer supported.
+ auto hardware = android::base::GetProperty("ro.hardware", "");
+ std::vector<std::string> legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
+ "/ueventd." + hardware + ".rc"};
+
+ std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
+
if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
- auto hardware = android::base::GetProperty("ro.hardware", "");
- return ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
- "/ueventd." + hardware + ".rc"});
+ // TODO: Remove these legacy paths once Android S is no longer supported.
+ canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
+ } else {
+ // Warn if newer device is using legacy paths.
+ for (const auto& path : legacy_paths) {
+ if (access(path.c_str(), F_OK) == 0) {
+ LOG(FATAL_WITHOUT_ABORT)
+ << "Legacy ueventd configuration file detected and will not be parsed: "
+ << path;
+ }
+ }
}
- return ParseConfig({"/system/etc/ueventd.rc"});
+ return ParseConfig(canonical);
}
int ueventd_main(int argc, char** argv) {
@@ -313,7 +355,8 @@
if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
ColdBoot cold_boot(uevent_listener, uevent_handlers,
- ueventd_configuration.enable_parallel_restorecon);
+ ueventd_configuration.enable_parallel_restorecon,
+ ueventd_configuration.parallel_restorecon_dirs);
cold_boot.Run();
}
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 2221228..d34672e 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -101,8 +101,8 @@
Result<void> ParseExternalFirmwareHandlerLine(
std::vector<std::string>&& args,
std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {
- if (args.size() != 4) {
- return Error() << "external_firmware_handler lines must have exactly 3 parameters";
+ if (args.size() != 4 && args.size() != 5) {
+ return Error() << "external_firmware_handler lines must have 3 or 4 parameters";
}
if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
@@ -117,7 +117,19 @@
return ErrnoError() << "invalid handler uid'" << args[2] << "'";
}
- ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3]));
+ gid_t gid = 0;
+ int handler_index = 3;
+ if (args.size() == 5) {
+ struct group* grp = getgrnam(args[3].c_str());
+ if (!grp) {
+ return ErrnoError() << "invalid handler gid '" << args[3] << "'";
+ }
+ gid = grp->gr_gid;
+ handler_index = 4;
+ }
+
+ ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, gid,
+ std::move(args[handler_index]));
external_firmware_handlers->emplace_back(std::move(handler));
return {};
@@ -139,6 +151,17 @@
return {};
}
+Result<void> ParseParallelRestoreconDirsLine(std::vector<std::string>&& args,
+ std::vector<std::string>* parallel_restorecon_dirs) {
+ if (args.size() != 2) {
+ return Error() << "parallel_restorecon_dir lines must have exactly 2 parameters";
+ }
+
+ std::move(std::next(args.begin()), args.end(), std::back_inserter(*parallel_restorecon_dirs));
+
+ return {};
+}
+
Result<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
size_t* uevent_socket_rcvbuf_size) {
if (args.size() != 2) {
@@ -256,6 +279,9 @@
parser.AddSingleLineParser("uevent_socket_rcvbuf_size",
std::bind(ParseUeventSocketRcvbufSizeLine, _1,
&ueventd_configuration.uevent_socket_rcvbuf_size));
+ parser.AddSingleLineParser("parallel_restorecon_dir",
+ std::bind(ParseParallelRestoreconDirsLine, _1,
+ &ueventd_configuration.parallel_restorecon_dirs));
parser.AddSingleLineParser("parallel_restorecon",
std::bind(ParseEnabledDisabledLine, _1,
&ueventd_configuration.enable_parallel_restorecon));
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index eaafa5a..81f4e9d 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -31,6 +31,7 @@
std::vector<Permissions> dev_permissions;
std::vector<std::string> firmware_directories;
std::vector<ExternalFirmwareHandler> external_firmware_handlers;
+ std::vector<std::string> parallel_restorecon_dirs;
bool enable_modalias_handling = false;
size_t uevent_socket_rcvbuf_size = 0;
bool enable_parallel_restorecon = false;
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index c5aa9e3..41924e2 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -49,6 +49,7 @@
const ExternalFirmwareHandler& test) {
EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
EXPECT_EQ(expected.uid, test.uid) << expected.uid;
+ EXPECT_EQ(expected.gid, test.gid) << expected.gid;
EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
}
@@ -76,6 +77,7 @@
EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
TestVector(expected.external_firmware_handlers, result.external_firmware_handlers,
TestExternalFirmwareHandler);
+ EXPECT_EQ(expected.parallel_restorecon_dirs, result.parallel_restorecon_dirs);
}
TEST(ueventd_parser, EmptyFile) {
@@ -104,7 +106,7 @@
{"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
{"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
- TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}});
+ TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}});
}
TEST(ueventd_parser, Permissions) {
@@ -130,7 +132,7 @@
{"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
};
- TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
+ TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}, {}});
}
TEST(ueventd_parser, FirmwareDirectories) {
@@ -146,7 +148,7 @@
"/more",
};
- TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}});
+ TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}, {}});
}
TEST(ueventd_parser, ExternalFirmwareHandlers) {
@@ -157,42 +159,62 @@
external_firmware_handler /devices/path/firmware/* root "/vendor/bin/firmware_handler.sh"
external_firmware_handler /devices/path/firmware/something* system "/vendor/bin/firmware_handler.sh"
external_firmware_handler /devices/path/*/firmware/something*.bin radio "/vendor/bin/firmware_handler.sh"
+external_firmware_handler /devices/path/firmware/something003.bin system system /vendor/bin/firmware_handler.sh
+external_firmware_handler /devices/path/firmware/something004.bin radio radio "/vendor/bin/firmware_handler.sh --has --arguments"
)";
auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
{
"devpath",
AID_ROOT,
+ AID_ROOT,
"handler_path",
},
{
"/devices/path/firmware/something001.bin",
AID_SYSTEM,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something002.bin",
AID_RADIO,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh --has --arguments",
},
{
"/devices/path/firmware/",
AID_ROOT,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something",
AID_SYSTEM,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/*/firmware/something*.bin",
AID_RADIO,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
+ {
+ "/devices/path/firmware/something003.bin",
+ AID_SYSTEM,
+ AID_SYSTEM,
+ "/vendor/bin/firmware_handler.sh",
+ },
+ {
+ "/devices/path/firmware/something004.bin",
+ AID_RADIO,
+ AID_RADIO,
+ "/vendor/bin/firmware_handler.sh --has --arguments",
+ },
};
- TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
+ TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
}
TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) {
@@ -205,11 +227,26 @@
{
"devpath",
AID_ROOT,
+ AID_ROOT,
"handler_path",
},
};
- TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
+ TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
+}
+
+TEST(ueventd_parser, ParallelRestoreconDirs) {
+ auto ueventd_file = R"(
+parallel_restorecon_dir /sys
+parallel_restorecon_dir /sys/devices
+)";
+
+ auto parallel_restorecon_dirs = std::vector<std::string>{
+ "/sys",
+ "/sys/devices",
+ };
+
+ TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, parallel_restorecon_dirs});
}
TEST(ueventd_parser, UeventSocketRcvbufSize) {
@@ -218,7 +255,7 @@
uevent_socket_rcvbuf_size 8M
)";
- TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
+ TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
}
TEST(ueventd_parser, EnabledDisabledLines) {
@@ -228,7 +265,7 @@
modalias_handling disabled
)";
- TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 0, true});
+ TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 0, true});
auto ueventd_file2 = R"(
parallel_restorecon enabled
@@ -236,7 +273,7 @@
parallel_restorecon disabled
)";
- TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, true, 0, false});
+ TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, true, 0, false});
}
TEST(ueventd_parser, AllTogether) {
@@ -276,6 +313,9 @@
modalias_handling enabled
parallel_restorecon enabled
+parallel_restorecon_dir /sys
+parallel_restorecon_dir /sys/devices
+
#ending comment
)";
@@ -305,14 +345,20 @@
};
auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
- {"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"},
+ {"/devices/path/firmware/firmware001.bin", AID_ROOT, AID_ROOT, "/vendor/bin/touch.sh"},
+ };
+
+ auto parallel_restorecon_dirs = std::vector<std::string>{
+ "/sys",
+ "/sys/devices",
};
size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
TestUeventdFile(ueventd_file,
{subsystems, sysfs_permissions, permissions, firmware_directories,
- external_firmware_handlers, true, uevent_socket_rcvbuf_size, true});
+ external_firmware_handlers, parallel_restorecon_dirs, true,
+ uevent_socket_rcvbuf_size, true});
}
// All of these lines are ill-formed, so test that there is 0 output.
@@ -344,6 +390,9 @@
external_firmware_handler blah blah
external_firmware_handler blah blah blah blah
+parallel_restorecon_dir
+parallel_restorecon_dir /sys /sys/devices
+
)";
TestUeventdFile(ueventd_file, {});
diff --git a/init/util.cpp b/init/util.cpp
index 9f7bfdb..d1e518b 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -757,5 +757,10 @@
is_default_mount_namespace_ready = true;
}
+bool IsMicrodroid() {
+ static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
+ return is_microdroid;
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index daba852..bf53675 100644
--- a/init/util.h
+++ b/init/util.h
@@ -103,5 +103,7 @@
bool IsDefaultMountNamespaceReady();
void SetDefaultMountNamespaceReady();
+
+bool IsMicrodroid();
} // namespace init
} // namespace android
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index b33d46d..a9f6958 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -21,6 +21,8 @@
cc_library {
name: "libcrypto_utils",
vendor_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
vndk: {
enabled: true,
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 68b21c6..0d9f2c7 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -354,18 +354,3 @@
defaults: ["libcutils_test_static_defaults"],
test_config: "KernelLibcutilsTest.xml",
}
-
-rust_bindgen {
- name: "libcutils_bindgen",
- wrapper_src: "rust/cutils.h",
- crate_name: "cutils_bindgen",
- source_stem: "bindings",
- local_include_dirs: ["include"],
- bindgen_flags: [
- "--allowlist-function", "multiuser_get_app_id",
- "--allowlist-function", "multiuser_get_uid",
- "--allowlist-function", "multiuser_get_user_id",
- "--allowlist-var", "AID_KEYSTORE",
- "--allowlist-var", "AID_USER_OFFSET",
- ],
-}
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
new file mode 100644
index 0000000..e512ab7
--- /dev/null
+++ b/libcutils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libcutils_test"
+ }
+ ]
+}
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 8f22d89..c471fa0 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -127,7 +127,7 @@
#define AID_EXT_DATA_RW 1078 /* GID for app-private data directories on external storage */
#define AID_EXT_OBB_RW 1079 /* GID for OBB directories on external storage */
#define AID_CONTEXT_HUB 1080 /* GID for access to the Context Hub */
-#define AID_VIRTMANAGER 1081 /* VirtManager daemon */
+#define AID_VIRTUALIZATIONSERVICE 1081 /* VirtualizationService daemon */
#define AID_ARTD 1082 /* ART Service daemon */
#define AID_UWB 1083 /* UWB subsystem */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
diff --git a/libcutils/rust/cutils.h b/libcutils/rust/cutils.h
deleted file mode 100644
index 9b78af6..0000000
--- a/libcutils/rust/cutils.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-#include <cutils/multiuser.h>
-#include <private/android_filesystem_config.h>
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index 86f68fb..3af07b4 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -5,16 +5,16 @@
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 {
name: "libkeyutils",
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/libkeyutils/NOTICE b/libkeyutils/NOTICE
new file mode 100644
index 0000000..5828550
--- /dev/null
+++ b/libkeyutils/NOTICE
@@ -0,0 +1,25 @@
+Copyright (C) 2017 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index ba11dc9..525a880 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -8,6 +8,7 @@
"-Werror",
],
vendor_available: true,
+ ramdisk_available: true,
recovery_available: true,
srcs: [
"libmodprobe.cpp",
diff --git a/libmodprobe/TEST_MAPPING b/libmodprobe/TEST_MAPPING
new file mode 100644
index 0000000..526b1e4
--- /dev/null
+++ b/libmodprobe/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libmodprobe_tests"
+ }
+ ]
+}
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
new file mode 100644
index 0000000..51773f9
--- /dev/null
+++ b/libpackagelistparser/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libpackagelistparser_test"
+ }
+ ]
+}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 5ca0967..0734f25 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -43,6 +43,7 @@
using android::base::GetBoolProperty;
using android::base::StringPrintf;
using android::base::unique_fd;
+using android::base::WriteStringToFile;
static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
@@ -202,3 +203,21 @@
return CgroupController(nullptr);
}
+
+int CgroupMap::ActivateControllers(const std::string& path) const {
+ if (__builtin_available(android 30, *)) {
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ if (ACgroupController_getFlags(controller) &
+ CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+ std::string str = std::string("+") + ACgroupController_getName(controller);
+ if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
+ return -errno;
+ }
+ }
+ }
+ return 0;
+ }
+ return -ENOSYS;
+}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 427d71b..22d717b 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -62,6 +62,7 @@
static CgroupMap& GetInstance();
CgroupController FindController(const std::string& name) const;
+ int ActivateControllers(const std::string& path) const;
private:
bool loaded_ = false;
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index 100d60e..e704a36 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -16,6 +16,7 @@
#pragma once
+#include <sys/cdefs.h>
#include <stdint.h>
__BEGIN_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index c824376..faf945c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -416,13 +416,15 @@
return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
}
-static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup) {
+static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup,
+ bool activate_controllers) {
auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
struct stat cgroup_stat;
mode_t cgroup_mode = 0750;
gid_t cgroup_uid = AID_SYSTEM;
uid_t cgroup_gid = AID_SYSTEM;
+ int ret = 0;
if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
PLOG(ERROR) << "Failed to get stats for " << cgroup;
@@ -436,6 +438,13 @@
PLOG(ERROR) << "Failed to make and chown " << uid_path;
return -errno;
}
+ if (activate_controllers) {
+ ret = CgroupMap::GetInstance().ActivateControllers(uid_path);
+ if (ret) {
+ LOG(ERROR) << "Failed to activate controllers in " << uid_path;
+ return ret;
+ }
+ }
auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
@@ -446,7 +455,6 @@
auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
- int ret = 0;
if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
ret = -errno;
PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
@@ -466,14 +474,14 @@
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
CgroupGetControllerPath("memory", &cgroup);
cgroup += "/apps";
- int ret = createProcessGroupInternal(uid, initialPid, cgroup);
+ int ret = createProcessGroupInternal(uid, initialPid, cgroup, false);
if (ret != 0) {
return ret;
}
}
CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
- return createProcessGroupInternal(uid, initialPid, cgroup);
+ return createProcessGroupInternal(uid, initialPid, cgroup, true);
}
static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 0b4b640..3f9aeb2 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -85,11 +85,11 @@
srcs: ["simg_dump.py"],
version: {
py2: {
- embedded_launcher: true,
- enabled: true,
+ enabled: false,
},
py3: {
- enabled: false,
+ embedded_launcher: true,
+ enabled: true,
},
},
}
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 82a03ad..b0b3b22 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
# Copyright (C) 2012 The Android Open Source Project
#
@@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from __future__ import print_function
import csv
import getopt
import hashlib
@@ -47,7 +46,7 @@
opts, args = getopt.getopt(sys.argv[1:],
"vsc:",
["verbose", "showhash", "csvfile"])
- except getopt.GetoptError, e:
+ except getopt.GetoptError as e:
print(e)
usage(me)
for o, a in opts:
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index 2a89e29..f07e32b 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -57,3 +57,13 @@
"libstatspull_bindgen",
],
}
+
+rust_test {
+ name: "libstatspull_bindgen_test",
+ srcs: [":libstatspull_bindgen"],
+ crate_name: "statspull_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 14e3e35..292425a 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -20,6 +20,16 @@
namespace android {
+namespace {
+
+constexpr uint64_t WAKE_EVENT_FD_SEQ = 1;
+
+epoll_event createEpollEvent(uint32_t events, uint64_t seq) {
+ return {.events = events, .data = {.u64 = seq}};
+}
+
+} // namespace
+
// --- WeakMessageHandler ---
WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
@@ -64,7 +74,7 @@
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
- mNextRequestSeq(0),
+ mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
@@ -137,22 +147,17 @@
mEpollFd.reset();
}
- // Allocate the new epoll instance and register the wake pipe.
+ // Allocate the new epoll instance and register the WakeEventFd.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
- struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem.events = EPOLLIN;
- eventItem.data.fd = mWakeEventFd.get();
- int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
+ epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
+ int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
- for (size_t i = 0; i < mRequests.size(); i++) {
- const Request& request = mRequests.valueAt(i);
- struct epoll_event eventItem;
- request.initEventItem(&eventItem);
+ for (const auto& [seq, request] : mRequests) {
+ epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
if (epollResult < 0) {
@@ -276,26 +281,28 @@
#endif
for (int i = 0; i < eventCount; i++) {
- int fd = eventItems[i].data.fd;
+ const SequenceNumber seq = eventItems[i].data.u64;
uint32_t epollEvents = eventItems[i].events;
- if (fd == mWakeEventFd.get()) {
+ if (seq == WAKE_EVENT_FD_SEQ) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex >= 0) {
+ const auto& request_it = mRequests.find(seq);
+ if (request_it != mRequests.end()) {
+ const auto& request = request_it->second;
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
- pushResponse(events, mRequests.valueAt(requestIndex));
+ mResponses.push({.seq = seq, .events = events, .request = request});
} else {
- ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
- "no longer registered.", epollEvents, fd);
+ ALOGW("Ignoring unexpected epoll events 0x%x for sequence number %" PRIu64
+ " that is no longer registered.",
+ epollEvents, seq);
}
}
}
@@ -354,7 +361,8 @@
// we need to be a little careful when removing the file descriptor afterwards.
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
- removeFd(fd, response.request.seq);
+ AutoMutex _l(mLock);
+ removeSequenceNumberLocked(response.seq);
}
// Clear the callback reference in the response structure promptly because we
@@ -416,13 +424,6 @@
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
-void Looper::pushResponse(int events, const Request& request) {
- Response response;
- response.events = events;
- response.request = request;
- mResponses.push(response);
-}
-
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
}
@@ -449,27 +450,27 @@
{ // acquire lock
AutoMutex _l(mLock);
+ // There is a sequence number reserved for the WakeEventFd.
+ if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
+ const SequenceNumber seq = mNextRequestSeq++;
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
- request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
- if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
- struct epoll_event eventItem;
- request.initEventItem(&eventItem);
-
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex < 0) {
+ epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
+ auto seq_it = mSequenceNumberByFd.find(fd);
+ if (seq_it == mSequenceNumberByFd.end()) {
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
return -1;
}
- mRequests.add(fd, request);
+ mRequests.emplace(seq, request);
+ mSequenceNumberByFd.emplace(fd, seq);
} else {
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
if (epollResult < 0) {
@@ -486,7 +487,7 @@
// set from scratch because it may contain an old file handle that we are
// now unable to remove since its file descriptor is no longer valid.
// No such problem would have occurred if we were using the poll system
- // call instead, but that approach carries others disadvantages.
+ // call instead, but that approach carries other disadvantages.
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
"being recycled, falling back on EPOLL_CTL_ADD: %s",
@@ -504,71 +505,69 @@
return -1;
}
}
- mRequests.replaceValueAt(requestIndex, request);
+ const SequenceNumber oldSeq = seq_it->second;
+ mRequests.erase(oldSeq);
+ mRequests.emplace(seq, request);
+ seq_it->second = seq;
}
} // release lock
return 1;
}
int Looper::removeFd(int fd) {
- return removeFd(fd, -1);
+ AutoMutex _l(mLock);
+ const auto& it = mSequenceNumberByFd.find(fd);
+ if (it == mSequenceNumberByFd.end()) {
+ return 0;
+ }
+ return removeSequenceNumberLocked(it->second);
}
-int Looper::removeFd(int fd, int seq) {
+int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq);
+ ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
#endif
- { // acquire lock
- AutoMutex _l(mLock);
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex < 0) {
- return 0;
- }
+ const auto& request_it = mRequests.find(seq);
+ if (request_it == mRequests.end()) {
+ return 0;
+ }
+ const int fd = request_it->second.fd;
- // Check the sequence number if one was given.
- if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) {
+ // Always remove the FD from the request map even if an error occurs while
+ // updating the epoll set so that we avoid accidentally leaking callbacks.
+ mRequests.erase(request_it);
+ mSequenceNumberByFd.erase(fd);
+
+ int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
+ if (epollResult < 0) {
+ if (errno == EBADF || errno == ENOENT) {
+ // Tolerate EBADF or ENOENT because it means that the file descriptor was closed
+ // before its callback was unregistered. This error may occur naturally when a
+ // callback has the side-effect of closing the file descriptor before returning and
+ // unregistering itself.
+ //
+ // Unfortunately due to kernel limitations we need to rebuild the epoll
+ // set from scratch because it may contain an old file handle that we are
+ // now unable to remove since its file descriptor is no longer valid.
+ // No such problem would have occurred if we were using the poll system
+ // call instead, but that approach carries other disadvantages.
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d",
- this, mRequests.valueAt(requestIndex).seq);
+ ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
+ "being closed: %s",
+ this, strerror(errno));
#endif
- return 0;
+ scheduleEpollRebuildLocked();
+ } else {
+ // Some other error occurred. This is really weird because it means
+ // our list of callbacks got out of sync with the epoll set somehow.
+ // We defensively rebuild the epoll set to avoid getting spurious
+ // notifications with nowhere to go.
+ ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
+ scheduleEpollRebuildLocked();
+ return -1;
}
-
- // Always remove the FD from the request map even if an error occurs while
- // updating the epoll set so that we avoid accidentally leaking callbacks.
- mRequests.removeItemsAt(requestIndex);
-
- int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
- if (epollResult < 0) {
- if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
- // Tolerate EBADF or ENOENT when the sequence number is known because it
- // means that the file descriptor was closed before its callback was
- // unregistered. This error may occur naturally when a callback has the
- // side-effect of closing the file descriptor before returning and
- // unregistering itself.
- //
- // Unfortunately due to kernel limitations we need to rebuild the epoll
- // set from scratch because it may contain an old file handle that we are
- // now unable to remove since its file descriptor is no longer valid.
- // No such problem would have occurred if we were using the poll system
- // call instead, but that approach carries others disadvantages.
-#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
- "being closed: %s", this, strerror(errno));
-#endif
- scheduleEpollRebuildLocked();
- } else {
- // Some other error occurred. This is really weird because it means
- // our list of callbacks got out of sync with the epoll set somehow.
- // We defensively rebuild the epoll set to avoid getting spurious
- // notifications with nowhere to go.
- ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
- scheduleEpollRebuildLocked();
- return -1;
- }
- }
- } // release lock
+ }
return 1;
}
@@ -656,14 +655,11 @@
return mPolling;
}
-void Looper::Request::initEventItem(struct epoll_event* eventItem) const {
- int epollEvents = 0;
+uint32_t Looper::Request::getEpollEvents() const {
+ uint32_t epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
-
- memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem->events = epollEvents;
- eventItem->data.fd = fd;
+ return epollEvents;
}
MessageHandler::~MessageHandler() { }
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 34f424b..c859f9c 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -8,6 +8,9 @@
#include <utils/Looper.h>
#include <utils/StopWatch.h>
#include <utils/Timers.h>
+#include <thread>
+#include <unordered_map>
+#include <utility>
#include "Looper_test_pipe.h"
#include <utils/threads.h>
@@ -710,4 +713,123 @@
<< "no more messages to handle";
}
+class LooperEventCallback : public LooperCallback {
+ public:
+ using Callback = std::function<int(int fd, int events)>;
+ explicit LooperEventCallback(Callback callback) : mCallback(std::move(callback)) {}
+ int handleEvent(int fd, int events, void* /*data*/) override { return mCallback(fd, events); }
+
+ private:
+ Callback mCallback;
+};
+
+// A utility class that allows for pipes to be added and removed from the looper, and polls the
+// looper from a different thread.
+class ThreadedLooperUtil {
+ public:
+ explicit ThreadedLooperUtil(const sp<Looper>& looper) : mLooper(looper), mRunning(true) {
+ mThread = std::thread([this]() {
+ while (mRunning) {
+ static constexpr std::chrono::milliseconds POLL_TIMEOUT(500);
+ mLooper->pollOnce(POLL_TIMEOUT.count());
+ }
+ });
+ }
+
+ ~ThreadedLooperUtil() {
+ mRunning = false;
+ mThread.join();
+ }
+
+ // Create a new pipe, and return the write end of the pipe and the id used to track the pipe.
+ // The read end of the pipe is added to the looper.
+ std::pair<int /*id*/, base::unique_fd> createPipe() {
+ int pipeFd[2];
+ if (pipe(pipeFd)) {
+ ADD_FAILURE() << "pipe() failed.";
+ return {};
+ }
+ const int readFd = pipeFd[0];
+ const int writeFd = pipeFd[1];
+
+ int id;
+ { // acquire lock
+ std::scoped_lock l(mLock);
+
+ id = mNextId++;
+ mFds.emplace(id, readFd);
+
+ auto removeCallback = [this, id, readFd](int fd, int events) {
+ EXPECT_EQ(readFd, fd) << "Received callback for incorrect fd.";
+ if ((events & Looper::EVENT_HANGUP) == 0) {
+ return 1; // Not a hangup, keep the callback.
+ }
+ removePipe(id);
+ return 0; // Remove the callback.
+ };
+
+ mLooper->addFd(readFd, 0, Looper::EVENT_INPUT,
+ new LooperEventCallback(std::move(removeCallback)), nullptr);
+ } // release lock
+
+ return {id, base::unique_fd(writeFd)};
+ }
+
+ // Remove the pipe with the given id.
+ void removePipe(int id) {
+ std::scoped_lock l(mLock);
+ if (mFds.find(id) == mFds.end()) {
+ return;
+ }
+ mLooper->removeFd(mFds[id].get());
+ mFds.erase(id);
+ }
+
+ // Check if the pipe with the given id exists and has not been removed.
+ bool hasPipe(int id) {
+ std::scoped_lock l(mLock);
+ return mFds.find(id) != mFds.end();
+ }
+
+ private:
+ sp<Looper> mLooper;
+ std::atomic<bool> mRunning;
+ std::thread mThread;
+
+ std::mutex mLock;
+ std::unordered_map<int, base::unique_fd> mFds GUARDED_BY(mLock);
+ int mNextId GUARDED_BY(mLock) = 0;
+};
+
+TEST_F(LooperTest, MultiThreaded_NoUnexpectedFdRemoval) {
+ ThreadedLooperUtil util(mLooper);
+
+ // Iterate repeatedly to try to recreate a flaky instance.
+ for (int i = 0; i < 1000; i++) {
+ auto [firstPipeId, firstPipeFd] = util.createPipe();
+ const int firstFdNumber = firstPipeFd.get();
+
+ // Close the first pipe's fd, causing a fd hangup.
+ firstPipeFd.reset();
+
+ // Request to remove the pipe from this test thread. This causes a race for pipe removal
+ // between the hangup in the looper's thread and this remove request from the test thread.
+ util.removePipe(firstPipeId);
+
+ // Create the second pipe. Since the fds for the first pipe are closed, this pipe should
+ // have the same fd numbers as the first pipe because the lowest unused fd number is used.
+ const auto [secondPipeId, fd] = util.createPipe();
+ EXPECT_EQ(firstFdNumber, fd.get())
+ << "The first and second fds must match for the purposes of this test.";
+
+ // Wait for unexpected hangup to occur.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ ASSERT_TRUE(util.hasPipe(secondPipeId)) << "The second pipe was removed unexpectedly.";
+
+ util.removePipe(secondPipeId);
+ }
+ SUCCEED() << "No unexpectedly removed fds.";
+}
+
} // namespace android
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index b57e287..0518927 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -170,7 +170,7 @@
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
- , mFlags(0)
+ , mFlags(OBJECT_LIFETIME_STRONG)
{
}
@@ -189,7 +189,7 @@
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
- , mFlags(0)
+ , mFlags(OBJECT_LIFETIME_STRONG)
, mStrongRefs(NULL)
, mWeakRefs(NULL)
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index d08b212..68642d8 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 c478321..c6e6f74 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 b391b1a..419b2de 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -431,24 +431,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
@@ -561,7 +554,7 @@
size_t len = length();
if (len == 0) {
// no existing filename, just use the new one
- setPathName(name);
+ setPathName(*this, name);
return *this;
}
@@ -581,7 +574,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/TEST_MAPPING b/libutils/TEST_MAPPING
new file mode 100644
index 0000000..c8ef45c
--- /dev/null
+++ b/libutils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libutils_test"
+ }
+ ]
+}
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index fd3f4a9..4cfac57 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-//
-// Timer functions.
-//
#include <utils/Timers.h>
#include <limits.h>
@@ -24,11 +21,12 @@
#include <time.h>
#include <android-base/macros.h>
+#include <utils/Log.h>
static constexpr size_t clock_id_max = 5;
static void checkClockId(int clock) {
- if (clock < 0 || clock >= clock_id_max) abort();
+ LOG_ALWAYS_FATAL_IF(clock < 0 || clock >= clock_id_max, "invalid clock id");
}
#if defined(__linux__)
@@ -56,18 +54,10 @@
}
#endif
-int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
-{
- nsecs_t timeoutDelayMillis;
- if (timeoutTime > referenceTime) {
- uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
- if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
- timeoutDelayMillis = -1;
- } else {
- timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL;
- }
- } else {
- timeoutDelayMillis = 0;
- }
- return (int)timeoutDelayMillis;
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) {
+ if (timeoutTime <= referenceTime) return 0;
+
+ uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
+ if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) return -1;
+ return (timeoutDelay + 999999LL) / 1000000LL;
}
diff --git a/libutils/Timers_test.cpp b/libutils/Timers_test.cpp
index ec0051e..c0a6d49 100644
--- a/libutils/Timers_test.cpp
+++ b/libutils/Timers_test.cpp
@@ -27,3 +27,12 @@
systemTime(SYSTEM_TIME_BOOTTIME);
EXPECT_EXIT(systemTime(SYSTEM_TIME_BOOTTIME + 1), testing::KilledBySignal(SIGABRT), "");
}
+
+TEST(Timers, toMillisecondTimeoutDelay) {
+ EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 100));
+ EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 10));
+
+ EXPECT_EQ(-1, toMillisecondTimeoutDelay(0, INT_MAX * 1000000LL));
+
+ EXPECT_EQ(123, toMillisecondTimeoutDelay(0, 123000000));
+}
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index 466fbb7..b387d68 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -17,15 +17,16 @@
#ifndef UTILS_LOOPER_H
#define UTILS_LOOPER_H
-#include <utils/threads.h>
#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
#include <utils/Timers.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
#include <sys/epoll.h>
#include <android-base/unique_fd.h>
+#include <unordered_map>
#include <utility>
namespace android {
@@ -421,18 +422,20 @@
static sp<Looper> getForThread();
private:
- struct Request {
- int fd;
- int ident;
- int events;
- int seq;
- sp<LooperCallback> callback;
- void* data;
+ using SequenceNumber = uint64_t;
- void initEventItem(struct epoll_event* eventItem) const;
- };
+ struct Request {
+ int fd;
+ int ident;
+ int events;
+ sp<LooperCallback> callback;
+ void* data;
+
+ uint32_t getEpollEvents() const;
+ };
struct Response {
+ SequenceNumber seq;
int events;
Request request;
};
@@ -463,9 +466,14 @@
android::base::unique_fd mEpollFd; // guarded by mLock but only modified on the looper thread
bool mEpollRebuildRequired; // guarded by mLock
- // Locked list of file descriptor monitoring requests.
- KeyedVector<int, Request> mRequests; // guarded by mLock
- int mNextRequestSeq;
+ // Locked maps of fds and sequence numbers monitoring requests.
+ // Both maps must be kept in sync at all times.
+ std::unordered_map<SequenceNumber, Request> mRequests; // guarded by mLock
+ std::unordered_map<int /*fd*/, SequenceNumber> mSequenceNumberByFd; // guarded by mLock
+
+ // The sequence number to use for the next fd that is added to the looper.
+ // The sequence number 0 is reserved for the WakeEventFd.
+ SequenceNumber mNextRequestSeq; // guarded by mLock
// This state is only used privately by pollOnce and does not require a lock since
// it runs on a single thread.
@@ -474,9 +482,8 @@
nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
int pollInner(int timeoutMillis);
- int removeFd(int fd, int seq);
+ int removeSequenceNumberLocked(SequenceNumber seq); // requires mLock
void awoken();
- void pushResponse(int events, const Request& request);
void rebuildEpollLocked();
void scheduleEpollRebuildLocked();
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/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
new file mode 100644
index 0000000..fcdc86a
--- /dev/null
+++ b/property_service/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "propertyinfoserializer_tests"
+ }
+ ]
+}
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 6861456..87646f9 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -18,7 +18,11 @@
"-Werror",
],
stl: "none",
- system_shared_libs: [],
- header_libs: ["libc_headers"],
+ target: {
+ bionic: {
+ system_shared_libs: [],
+ header_libs: ["libc_headers"],
+ },
+ },
export_include_dirs: ["include"],
}
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index ae21633..e98733a 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -45,4 +45,11 @@
src: "etc/public.libraries.android.txt",
filename: "public.libraries.txt",
installable: false,
-}
\ No newline at end of file
+}
+
+// adb_debug.prop in debug ramdisk
+prebuilt_root {
+ name: "adb_debug.prop",
+ src: "adb_debug.prop",
+ debug_ramdisk: true,
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 99d8f9a..9b80575 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -210,15 +210,4 @@
$(hide) $(foreach lib,$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES), \
echo $(lib) >> $@;)
-#######################################
-# adb_debug.prop in debug ramdisk
-include $(CLEAR_VARS)
-LOCAL_MODULE := adb_debug.prop
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT)
-include $(BUILD_PREBUILT)
-
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index c58f298..d62c41d 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -11,6 +11,9 @@
"libsigchain.so",
// TODO(b/122876336): Remove libpac.so once it's migrated to Webview
"libpac.so",
+ // TODO(b/184872979): Remove libbinder_rpc_unstable.so once stablized and
+ // added to libbinder_ndk.
+ "libbinder_rpc_unstable.so",
// TODO(b/120786417 or b/134659294): libicuuc.so
// and libicui18n.so are kept for app compat.
"libicui18n.so",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4515950..3831693 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -438,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
@@ -763,6 +764,8 @@
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.
+ mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
mkdir /data/preloads 0775 system system encryption=None
@@ -786,11 +789,13 @@
# Create directories to push tests to for each linker namespace.
# Create the subdirectories in case the first test is run as root
# so it doesn't end up owned by root.
- mkdir /data/local/tests 0700 shell shell
- mkdir /data/local/tests/product 0700 shell shell
- mkdir /data/local/tests/system 0700 shell shell
- mkdir /data/local/tests/unrestricted 0700 shell shell
- mkdir /data/local/tests/vendor 0700 shell shell
+ # Set directories to be executable by any process so that debuggerd,
+ # aka crash_dump, can read any executables/shared libraries.
+ mkdir /data/local/tests 0701 shell shell
+ mkdir /data/local/tests/product 0701 shell shell
+ mkdir /data/local/tests/system 0701 shell shell
+ mkdir /data/local/tests/unrestricted 0701 shell shell
+ mkdir /data/local/tests/vendor 0701 shell shell
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root encryption=Require
@@ -1014,11 +1019,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
@@ -1148,7 +1153,7 @@
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
on property:sys.sysctl.extra_free_kbytes=*
- write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
+ exec -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}
# Allow users to drop caches
on property:perf.drop_caches=3
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/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 56e774b..3101974 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -67,9 +67,9 @@
# CDMA radio interface MUX
/dev/ppp 0660 radio vpn
-# Virtualisation is managed by Virt Manager
-/dev/kvm 0600 virtmanager root
-/dev/vhost-vsock 0600 virtmanager root
+# Virtualization is managed by VirtualizationService.
+/dev/kvm 0600 virtualizationservice root
+/dev/vhost-vsock 0600 virtualizationservice root
# sysfs properties
/sys/devices/platform/trusty.* trusty_version 0440 root log
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..b557dee 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -30,9 +30,9 @@
"libhidlbase",
"liblog",
"libprotobuf-cpp-lite",
- "libsysutils",
"libutils",
"libz",
+ "packagemanager_aidl-cpp",
],
cflags: [
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/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index c0e9fce..20db638 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2015 The Android Open Source Project
#
@@ -16,7 +16,7 @@
#
# pylint: disable=bad-indentation,bad-continuation
-from __future__ import print_function
+
import os
import re
import sys
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 4aca375..c72af40 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -220,6 +220,9 @@
case APPLOADER_ERR_INTERNAL:
LOG(ERROR) << "Error: internal apploader error";
break;
+ case APPLOADER_ERR_INVALID_VERSION:
+ LOG(ERROR) << "Error: invalid application version";
+ break;
default:
LOG(ERROR) << "Unrecognized error: " << resp.error;
break;
@@ -242,6 +245,8 @@
tipc_fd = tipc_connect(dev_name, APPLOADER_PORT);
if (tipc_fd < 0) {
LOG(ERROR) << "Failed to connect to Trusty app loader: " << strerror(-tipc_fd);
+ // print this to stderr too to avoid silently exiting when run as non-root
+ fprintf(stderr, "Failed to connect to Trusty app loader: %s\n", strerror(-tipc_fd));
rc = tipc_fd;
goto err_tipc_connect;
}
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index d8c915e..6cda7c1 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -44,6 +44,7 @@
* @APPLOADER_ERR_ALREADY_EXISTS: application has already been loaded
* @APPLOADER_ERR_INTERNAL: miscellaneous or internal apploader
* error not covered by the above
+ * @APPLOADER_ERR_INVALID_VERSION: invalid application version
*/
enum apploader_error : uint32_t {
APPLOADER_NO_ERROR = 0,
@@ -54,6 +55,7 @@
APPLOADER_ERR_LOADING_FAILED,
APPLOADER_ERR_ALREADY_EXISTS,
APPLOADER_ERR_INTERNAL,
+ APPLOADER_ERR_INVALID_VERSION,
};
/**
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/Android.bp b/trusty/keymaster/Android.bp
index cf056f0..7bd1d10 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -106,9 +106,9 @@
"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",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
"lib_android_keymaster_keymint_utils",
"libbase",
"libbinder_ndk",
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 29c6f93..eb0acb5 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -45,36 +45,40 @@
static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
static const char* receiver_name = "com.android.trusty.memref.receiver";
-static const char *_sopts = "hsvD:t:r:m:b:";
+static const char* _sopts = "hsvDS:t:r:m:b:";
+/* clang-format off */
static const struct option _lopts[] = {
- {"help", no_argument, 0, 'h'},
- {"silent", no_argument, 0, 's'},
- {"variable",no_argument, 0, 'v'},
- {"dev", required_argument, 0, 'D'},
- {"repeat", required_argument, 0, 'r'},
- {"burst", required_argument, 0, 'b'},
- {"msgsize", required_argument, 0, 'm'},
- {0, 0, 0, 0}
+ {"help", no_argument, 0, 'h'},
+ {"silent", no_argument, 0, 's'},
+ {"variable",no_argument, 0, 'v'},
+ {"dev", required_argument, 0, 'D'},
+ {"srv", required_argument, 0, 'S'},
+ {"repeat", required_argument, 0, 'r'},
+ {"burst", required_argument, 0, 'b'},
+ {"msgsize", required_argument, 0, 'm'},
+ {0, 0, 0, 0}
};
+/* clang-format on */
-static const char *usage =
-"Usage: %s [options]\n"
-"\n"
-"options:\n"
-" -h, --help prints this message and exit\n"
-" -D, --dev name device name\n"
-" -t, --test name test to run\n"
-" -r, --repeat cnt repeat count\n"
-" -m, --msgsize size max message size\n"
-" -v, --variable variable message size\n"
-" -s, --silent silent\n"
-"\n"
-;
+static const char* usage =
+ "Usage: %s [options]\n"
+ "\n"
+ "options:\n"
+ " -h, --help prints this message and exit\n"
+ " -D, --dev name device name\n"
+ " -S, --srv name service name\n"
+ " -t, --test name test to run\n"
+ " -r, --repeat cnt repeat count\n"
+ " -b, --burst cnt burst count\n"
+ " -m, --msgsize size max message size\n"
+ " -v, --variable variable message size\n"
+ " -s, --silent silent\n"
+ "\n";
static const char* usage_long =
"\n"
"The following tests are available:\n"
- " connect - connect to datasink service\n"
+ " connect - connect to specified service, defaults to echo+datasink\n"
" connect_foo - connect to non existing service\n"
" burst_write - send messages to datasink service\n"
" echo - send/receive messages to echo service\n"
@@ -96,798 +100,774 @@
static uint opt_msgburst = 32;
static bool opt_variable = false;
static bool opt_silent = false;
+static char* srv_name = NULL;
static void print_usage_and_exit(const char *prog, int code, bool verbose)
{
- fprintf (stderr, usage, prog);
- if (verbose)
- fprintf (stderr, "%s", usage_long);
- exit(code);
+ fprintf(stderr, usage, prog);
+ if (verbose) fprintf(stderr, "%s", usage_long);
+ exit(code);
}
static void parse_options(int argc, char **argv)
{
- int c;
- int oidx = 0;
+ int c;
+ int oidx = 0;
- while (1)
- {
- c = getopt_long (argc, argv, _sopts, _lopts, &oidx);
- if (c == -1)
- break; /* done */
+ while (1) {
+ c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+ if (c == -1) break; /* done */
- switch (c) {
+ switch (c) {
+ case 'D':
+ dev_name = strdup(optarg);
+ break;
- case 'D':
- dev_name = strdup(optarg);
- break;
+ case 'S':
+ srv_name = strdup(optarg);
+ break;
- case 't':
- test_name = strdup(optarg);
- break;
+ case 't':
+ test_name = strdup(optarg);
+ break;
- case 'v':
- opt_variable = true;
- break;
+ case 'v':
+ opt_variable = true;
+ break;
- case 'r':
- opt_repeat = atoi(optarg);
- break;
+ case 'r':
+ opt_repeat = atoi(optarg);
+ break;
- case 'm':
- opt_msgsize = atoi(optarg);
- break;
+ case 'm':
+ opt_msgsize = atoi(optarg);
+ break;
- case 'b':
- opt_msgburst = atoi(optarg);
- break;
+ case 'b':
+ opt_msgburst = atoi(optarg);
+ break;
- case 's':
- opt_silent = true;
- break;
+ case 's':
+ opt_silent = true;
+ break;
- case 'h':
- print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
- break;
+ case 'h':
+ print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+ break;
- default:
- print_usage_and_exit(argv[0], EXIT_FAILURE, false);
- }
- }
+ default:
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
+ }
}
static int connect_test(uint repeat)
{
- uint i;
- int echo_fd;
- int dsink_fd;
+ uint i;
+ int echo_fd;
+ int dsink_fd;
+ int custom_fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "echo");
- }
- dsink_fd = tipc_connect(dev_name, datasink_name);
- if (dsink_fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "datasink");
- }
+ for (i = 0; i < repeat; i++) {
+ if (srv_name) {
+ custom_fd = tipc_connect(dev_name, srv_name);
+ if (custom_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", srv_name);
+ }
+ if (custom_fd >= 0) {
+ tipc_close(custom_fd);
+ }
+ } else {
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+ }
+ dsink_fd = tipc_connect(dev_name, datasink_name);
+ if (dsink_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
+ }
- if (echo_fd >= 0) {
- tipc_close(echo_fd);
- }
- if (dsink_fd >= 0) {
- tipc_close(dsink_fd);
- }
- }
+ if (echo_fd >= 0) {
+ tipc_close(echo_fd);
+ }
+ if (dsink_fd >= 0) {
+ tipc_close(dsink_fd);
+ }
+ }
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int connect_foo(uint repeat)
{
- uint i;
- int fd;
+ uint i;
+ int fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- fd = tipc_connect(dev_name, "foo");
- if (fd >= 0) {
- fprintf(stderr, "succeeded to connect to '%s' service\n",
- "foo");
- tipc_close(fd);
- }
- }
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, "foo");
+ if (fd >= 0) {
+ fprintf(stderr, "succeeded to connect to '%s' service\n", "foo");
+ tipc_close(fd);
+ }
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int closer1_test(uint repeat)
{
- uint i;
- int fd;
+ uint i;
+ int fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- fd = tipc_connect(dev_name, closer1_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "closer1");
- continue;
- }
- if (!opt_silent) {
- printf("%s: connected\n", __func__);
- }
- tipc_close(fd);
- }
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, closer1_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "closer1");
+ continue;
+ }
+ if (!opt_silent) {
+ printf("%s: connected\n", __func__);
+ }
+ tipc_close(fd);
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int closer2_test(uint repeat)
{
- uint i;
- int fd;
+ uint i;
+ int fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- fd = tipc_connect(dev_name, closer2_name);
- if (fd < 0) {
- if (!opt_silent) {
- printf("failed to connect to '%s' service\n", "closer2");
- }
- } else {
- /* this should always fail */
- fprintf(stderr, "connected to '%s' service\n", "closer2");
- tipc_close(fd);
- }
- }
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, closer2_name);
+ if (fd < 0) {
+ if (!opt_silent) {
+ printf("failed to connect to '%s' service\n", "closer2");
+ }
+ } else {
+ /* this should always fail */
+ fprintf(stderr, "connected to '%s' service\n", "closer2");
+ tipc_close(fd);
+ }
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int closer3_test(uint repeat)
{
- uint i, j;
- ssize_t rc;
- int fd[4];
- char buf[64];
+ uint i, j;
+ ssize_t rc;
+ int fd[4];
+ char buf[64];
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ /* open 4 connections to closer3 service */
+ for (j = 0; j < 4; j++) {
+ fd[j] = tipc_connect(dev_name, closer3_name);
+ if (fd[j] < 0) {
+ fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
+ } else {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
+ }
+ memset(buf, i + j, sizeof(buf));
+ rc = write(fd[j], buf, sizeof(buf));
+ if (rc != sizeof(buf)) {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: write returned = %zd\n", __func__, j, fd[j], rc);
+ }
+ perror("closer3_test: write");
+ }
+ }
+ }
- /* open 4 connections to closer3 service */
- for (j = 0; j < 4; j++) {
- fd[j] = tipc_connect(dev_name, closer3_name);
- if (fd[j] < 0) {
- fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
- } else {
- if (!opt_silent) {
- printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
- }
- memset(buf, i + j, sizeof(buf));
- rc = write(fd[j], buf, sizeof(buf));
- if (rc != sizeof(buf)) {
- if (!opt_silent) {
- printf("%s: fd[%d]=%d: write returned = %zd\n",
- __func__, j, fd[j], rc);
- }
- perror("closer3_test: write");
- }
- }
- }
+ /* sleep a bit */
+ sleep(1);
- /* sleep a bit */
- sleep(1);
+ /* It is expected that they will be closed by remote */
+ for (j = 0; j < 4; j++) {
+ if (fd[j] < 0) continue;
+ rc = write(fd[j], buf, sizeof(buf));
+ if (rc != sizeof(buf)) {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: write returned = %zd\n", __func__, j, fd[j], rc);
+ }
+ perror("closer3_test: write");
+ }
+ }
- /* It is expected that they will be closed by remote */
- for (j = 0; j < 4; j++) {
- if (fd[j] < 0)
- continue;
- rc = write(fd[j], buf, sizeof(buf));
- if (rc != sizeof(buf)) {
- if (!opt_silent) {
- printf("%s: fd[%d]=%d: write returned = %zd\n",
- __func__, j, fd[j], rc);
- }
- perror("closer3_test: write");
- }
- }
+ /* then they have to be closed by remote */
+ for (j = 0; j < 4; j++) {
+ if (fd[j] >= 0) {
+ tipc_close(fd[j]);
+ }
+ }
+ }
- /* then they have to be closed by remote */
- for (j = 0; j < 4; j++) {
- if (fd[j] >= 0) {
- tipc_close(fd[j]);
- }
- }
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
-
- return 0;
+ return 0;
}
static int echo_test(uint repeat, uint msgsz, bool var)
{
- uint i;
- ssize_t rc;
- size_t msg_len;
- int echo_fd =-1;
- char tx_buf[msgsz];
- char rx_buf[msgsz];
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx_buf[msgsz];
+ char rx_buf[msgsz];
- if (!opt_silent) {
- printf("%s: repeat %u: msgsz %u: variable %s\n",
- __func__, repeat, msgsz, var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+ var ? "true" : "false");
+ }
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to service\n");
- return echo_fd;
- }
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- msg_len = msgsz;
- if (opt_variable && msgsz) {
- msg_len = rand() % msgsz;
- }
+ memset(tx_buf, i + 1, msg_len);
- memset(tx_buf, i + 1, msg_len);
+ rc = write(echo_fd, tx_buf, msg_len);
+ if ((size_t)rc != msg_len) {
+ perror("echo_test: write");
+ break;
+ }
- rc = write(echo_fd, tx_buf, msg_len);
- if ((size_t)rc != msg_len) {
- perror("echo_test: write");
- break;
- }
+ rc = read(echo_fd, rx_buf, msg_len);
+ if (rc < 0) {
+ perror("echo_test: read");
+ break;
+ }
- rc = read(echo_fd, rx_buf, msg_len);
- if (rc < 0) {
- perror("echo_test: read");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "data truncated (%zu vs. %zu)\n", rc, msg_len);
+ continue;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr, "data truncated (%zu vs. %zu)\n",
- rc, msg_len);
- continue;
- }
+ if (memcmp(tx_buf, rx_buf, (size_t)rc)) {
+ fprintf(stderr, "data mismatch\n");
+ continue;
+ }
+ }
- if (memcmp(tx_buf, rx_buf, (size_t) rc)) {
- fprintf(stderr, "data mismatch\n");
- continue;
- }
- }
+ tipc_close(echo_fd);
- tipc_close(echo_fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int burst_write_test(uint repeat, uint msgburst, uint msgsz, bool var)
{
- int fd;
- uint i, j;
- ssize_t rc;
- size_t msg_len;
- char tx_buf[msgsz];
+ int fd;
+ uint i, j;
+ ssize_t rc;
+ size_t msg_len;
+ char tx_buf[msgsz];
- if (!opt_silent) {
- printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n",
- __func__, repeat, msgburst, msgsz,
- var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n", __func__, repeat, msgburst,
+ msgsz, var ? "true" : "false");
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, datasink_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
+ break;
+ }
- fd = tipc_connect(dev_name, datasink_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "datasink");
- break;
- }
+ for (j = 0; j < msgburst; j++) {
+ msg_len = msgsz;
+ if (var && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- for (j = 0; j < msgburst; j++) {
- msg_len = msgsz;
- if (var && msgsz) {
- msg_len = rand() % msgsz;
- }
+ memset(tx_buf, i + 1, msg_len);
+ rc = write(fd, tx_buf, msg_len);
+ if ((size_t)rc != msg_len) {
+ perror("burst_test: write");
+ break;
+ }
+ }
- memset(tx_buf, i + 1, msg_len);
- rc = write(fd, tx_buf, msg_len);
- if ((size_t)rc != msg_len) {
- perror("burst_test: write");
- break;
- }
- }
+ tipc_close(fd);
+ }
- tipc_close(fd);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int _wait_for_msg(int fd, uint msgsz, int timeout)
{
- int rc;
- fd_set rfds;
- uint msgcnt = 0;
- char rx_buf[msgsz];
- struct timeval tv;
+ int rc;
+ fd_set rfds;
+ uint msgcnt = 0;
+ char rx_buf[msgsz];
+ struct timeval tv;
- if (!opt_silent) {
- printf("waiting (%d) for msg\n", timeout);
- }
+ if (!opt_silent) {
+ printf("waiting (%d) for msg\n", timeout);
+ }
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
- for(;;) {
- rc = select(fd+1, &rfds, NULL, NULL, &tv);
+ for (;;) {
+ rc = select(fd + 1, &rfds, NULL, NULL, &tv);
- if (rc == 0) {
- if (!opt_silent) {
- printf("select timedout\n");
- }
- break;
- }
+ if (rc == 0) {
+ if (!opt_silent) {
+ printf("select timedout\n");
+ }
+ break;
+ }
- if (rc == -1) {
- perror("select_test: select");
- return rc;
- }
+ if (rc == -1) {
+ perror("select_test: select");
+ return rc;
+ }
- rc = read(fd, rx_buf, sizeof(rx_buf));
- if (rc < 0) {
- perror("select_test: read");
- return rc;
- } else {
- if (rc > 0) {
- msgcnt++;
- }
- }
- }
+ rc = read(fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("select_test: read");
+ return rc;
+ } else {
+ if (rc > 0) {
+ msgcnt++;
+ }
+ }
+ }
- if (!opt_silent) {
- printf("got %u messages\n", msgcnt);
- }
+ if (!opt_silent) {
+ printf("got %u messages\n", msgcnt);
+ }
- return 0;
+ return 0;
}
static int select_test(uint repeat, uint msgburst, uint msgsz)
{
- int fd;
- uint i, j;
- ssize_t rc;
- char tx_buf[msgsz];
+ int fd;
+ uint i, j;
+ ssize_t rc;
+ char tx_buf[msgsz];
- if (!opt_silent) {
- printf("%s: repeat %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u\n", __func__, repeat);
+ }
- fd = tipc_connect(dev_name, echo_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "echo");
- return fd;
- }
+ fd = tipc_connect(dev_name, echo_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+ return fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ _wait_for_msg(fd, msgsz, 1);
- _wait_for_msg(fd, msgsz, 1);
+ if (!opt_silent) {
+ printf("sending burst: %u msg\n", msgburst);
+ }
- if (!opt_silent) {
- printf("sending burst: %u msg\n", msgburst);
- }
+ for (j = 0; j < msgburst; j++) {
+ memset(tx_buf, i + j, msgsz);
+ rc = write(fd, tx_buf, msgsz);
+ if ((size_t)rc != msgsz) {
+ perror("burst_test: write");
+ break;
+ }
+ }
+ }
- for (j = 0; j < msgburst; j++) {
- memset(tx_buf, i + j, msgsz);
- rc = write(fd, tx_buf, msgsz);
- if ((size_t)rc != msgsz) {
- perror("burst_test: write");
- break;
- }
- }
- }
+ tipc_close(fd);
- tipc_close(fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int blocked_read_test(uint repeat)
{
- int fd;
- uint i;
- ssize_t rc;
- char rx_buf[512];
+ int fd;
+ uint i;
+ ssize_t rc;
+ char rx_buf[512];
- if (!opt_silent) {
- printf("%s: repeat %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u\n", __func__, repeat);
+ }
- fd = tipc_connect(dev_name, echo_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "echo");
- return fd;
- }
+ fd = tipc_connect(dev_name, echo_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+ return fd;
+ }
- for (i = 0; i < repeat; i++) {
- rc = read(fd, rx_buf, sizeof(rx_buf));
- if (rc < 0) {
- perror("select_test: read");
- break;
- } else {
- if (!opt_silent) {
- printf("got %zd bytes\n", rc);
- }
- }
- }
+ for (i = 0; i < repeat; i++) {
+ rc = read(fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("select_test: read");
+ break;
+ } else {
+ if (!opt_silent) {
+ printf("got %zd bytes\n", rc);
+ }
+ }
+ }
- tipc_close(fd);
+ tipc_close(fd);
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int ta2ta_ipc_test(void)
{
- enum test_message_header {
- TEST_PASSED = 0,
- TEST_FAILED = 1,
- TEST_MESSAGE = 2,
- };
+ enum test_message_header {
+ TEST_PASSED = 0,
+ TEST_FAILED = 1,
+ TEST_MESSAGE = 2,
+ };
- int fd;
- int ret;
- unsigned char rx_buf[256];
+ int fd;
+ int ret;
+ unsigned char rx_buf[256];
- if (!opt_silent) {
- printf("%s:\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s:\n", __func__);
+ }
- fd = tipc_connect(dev_name, main_ctrl_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "main_ctrl");
- return fd;
- }
+ fd = tipc_connect(dev_name, main_ctrl_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "main_ctrl");
+ return fd;
+ }
- /* Wait for tests to complete and read status */
- while (true) {
- ret = read(fd, rx_buf, sizeof(rx_buf));
- if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {
- fprintf(stderr, "%s: Read failed: %d\n", __func__, ret);
- tipc_close(fd);
- return -1;
- }
+ /* Wait for tests to complete and read status */
+ while (true) {
+ ret = read(fd, rx_buf, sizeof(rx_buf));
+ if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {
+ fprintf(stderr, "%s: Read failed: %d\n", __func__, ret);
+ tipc_close(fd);
+ return -1;
+ }
- if (rx_buf[0] == TEST_PASSED) {
- break;
- } else if (rx_buf[0] == TEST_FAILED) {
- break;
- } else if (rx_buf[0] == TEST_MESSAGE) {
- write(STDOUT_FILENO, rx_buf + 1, ret - 1);
- } else {
- fprintf(stderr, "%s: Bad message header: %d\n",
- __func__, rx_buf[0]);
- break;
- }
- }
+ if (rx_buf[0] == TEST_PASSED) {
+ break;
+ } else if (rx_buf[0] == TEST_FAILED) {
+ break;
+ } else if (rx_buf[0] == TEST_MESSAGE) {
+ write(STDOUT_FILENO, rx_buf + 1, ret - 1);
+ } else {
+ fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
+ break;
+ }
+ }
- tipc_close(fd);
+ tipc_close(fd);
- return rx_buf[0] == TEST_PASSED ? 0 : -1;
+ return rx_buf[0] == TEST_PASSED ? 0 : -1;
}
typedef struct uuid
{
- uint32_t time_low;
- uint16_t time_mid;
- uint16_t time_hi_and_version;
- uint8_t clock_seq_and_node[8];
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_and_node[8];
} uuid_t;
static void print_uuid(const char *dev, uuid_t *uuid)
{
- printf("%s:", dev);
- printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
- uuid->time_low,
- uuid->time_mid,
- uuid->time_hi_and_version,
- uuid->clock_seq_and_node[0],
- uuid->clock_seq_and_node[1],
- uuid->clock_seq_and_node[2],
- uuid->clock_seq_and_node[3],
- uuid->clock_seq_and_node[4],
- uuid->clock_seq_and_node[5],
- uuid->clock_seq_and_node[6],
- uuid->clock_seq_and_node[7]
- );
+ printf("%s:", dev);
+ printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", uuid->time_low,
+ uuid->time_mid, uuid->time_hi_and_version, uuid->clock_seq_and_node[0],
+ uuid->clock_seq_and_node[1], uuid->clock_seq_and_node[2], uuid->clock_seq_and_node[3],
+ uuid->clock_seq_and_node[4], uuid->clock_seq_and_node[5], uuid->clock_seq_and_node[6],
+ uuid->clock_seq_and_node[7]);
}
static int dev_uuid_test(void)
{
- int fd;
- ssize_t rc;
- uuid_t uuid;
+ int fd;
+ ssize_t rc;
+ uuid_t uuid;
- fd = tipc_connect(dev_name, uuid_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "uuid");
- return fd;
- }
+ fd = tipc_connect(dev_name, uuid_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "uuid");
+ return fd;
+ }
- /* wait for test to complete */
- rc = read(fd, &uuid, sizeof(uuid));
- if (rc < 0) {
- perror("dev_uuid_test: read");
- } else if (rc != sizeof(uuid)) {
- fprintf(stderr, "unexpected uuid size (%d vs. %d)\n",
- (int)rc, (int)sizeof(uuid));
- } else {
- print_uuid(dev_name, &uuid);
- }
+ /* wait for test to complete */
+ rc = read(fd, &uuid, sizeof(uuid));
+ if (rc < 0) {
+ perror("dev_uuid_test: read");
+ } else if (rc != sizeof(uuid)) {
+ fprintf(stderr, "unexpected uuid size (%d vs. %d)\n", (int)rc, (int)sizeof(uuid));
+ } else {
+ print_uuid(dev_name, &uuid);
+ }
- tipc_close(fd);
+ tipc_close(fd);
- return 0;
+ return 0;
}
static int ta_access_test(void)
{
- int fd;
+ int fd;
- if (!opt_silent) {
- printf("%s:\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s:\n", __func__);
+ }
- fd = tipc_connect(dev_name, ta_only_name);
- if (fd >= 0) {
- fprintf(stderr, "Succeed to connect to '%s' service\n",
- "ta_only");
- tipc_close(fd);
- }
+ fd = tipc_connect(dev_name, ta_only_name);
+ if (fd >= 0) {
+ fprintf(stderr, "Succeed to connect to '%s' service\n", "ta_only");
+ tipc_close(fd);
+ }
- fd = tipc_connect(dev_name, ns_only_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "ns_only");
- return fd;
- }
- tipc_close(fd);
+ fd = tipc_connect(dev_name, ns_only_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "ns_only");
+ return fd;
+ }
+ tipc_close(fd);
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int writev_test(uint repeat, uint msgsz, bool var)
{
- uint i;
- ssize_t rc;
- size_t msg_len;
- int echo_fd = -1;
- char tx0_buf[msgsz];
- char tx1_buf[msgsz];
- char rx_buf [msgsz];
- struct iovec iovs[2]= {{tx0_buf, 0}, {tx1_buf, 0}};
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx0_buf[msgsz];
+ char tx1_buf[msgsz];
+ char rx_buf[msgsz];
+ struct iovec iovs[2] = {{tx0_buf, 0}, {tx1_buf, 0}};
- if (!opt_silent) {
- printf("%s: repeat %u: msgsz %u: variable %s\n",
- __func__, repeat, msgsz, var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+ var ? "true" : "false");
+ }
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to service\n");
- return echo_fd;
- }
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- msg_len = msgsz;
- if (opt_variable && msgsz) {
- msg_len = rand() % msgsz;
- }
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
- iovs[0].iov_len = msg_len / 3;
- iovs[1].iov_len = msg_len - iovs[0].iov_len;
+ memset(tx0_buf, i + 1, iovs[0].iov_len);
+ memset(tx1_buf, i + 2, iovs[1].iov_len);
+ memset(rx_buf, i + 3, sizeof(rx_buf));
- memset(tx0_buf, i + 1, iovs[0].iov_len);
- memset(tx1_buf, i + 2, iovs[1].iov_len);
- memset(rx_buf, i + 3, sizeof(rx_buf));
+ rc = writev(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("writev_test: writev");
+ break;
+ }
- rc = writev(echo_fd, iovs, 2);
- if (rc < 0) {
- perror("writev_test: writev");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "writev",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "writev", (size_t)rc, msg_len);
- break;
- }
+ rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("writev_test: read");
+ break;
+ }
- rc = read(echo_fd, rx_buf, sizeof(rx_buf));
- if (rc < 0) {
- perror("writev_test: read");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "read",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "read", (size_t)rc, msg_len);
- break;
- }
+ if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
- if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
- fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
- break;
- }
+ if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
- if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
- fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
- break;
- }
- }
+ tipc_close(echo_fd);
- tipc_close(echo_fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int readv_test(uint repeat, uint msgsz, bool var)
{
- uint i;
- ssize_t rc;
- size_t msg_len;
- int echo_fd = -1;
- char tx_buf [msgsz];
- char rx0_buf[msgsz];
- char rx1_buf[msgsz];
- struct iovec iovs[2]= {{rx0_buf, 0}, {rx1_buf, 0}};
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx_buf[msgsz];
+ char rx0_buf[msgsz];
+ char rx1_buf[msgsz];
+ struct iovec iovs[2] = {{rx0_buf, 0}, {rx1_buf, 0}};
- if (!opt_silent) {
- printf("%s: repeat %u: msgsz %u: variable %s\n",
- __func__, repeat, msgsz, var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+ var ? "true" : "false");
+ }
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to service\n");
- return echo_fd;
- }
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- msg_len = msgsz;
- if (opt_variable && msgsz) {
- msg_len = rand() % msgsz;
- }
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
- iovs[0].iov_len = msg_len / 3;
- iovs[1].iov_len = msg_len - iovs[0].iov_len;
+ memset(tx_buf, i + 1, sizeof(tx_buf));
+ memset(rx0_buf, i + 2, iovs[0].iov_len);
+ memset(rx1_buf, i + 3, iovs[1].iov_len);
- memset(tx_buf, i + 1, sizeof(tx_buf));
- memset(rx0_buf, i + 2, iovs[0].iov_len);
- memset(rx1_buf, i + 3, iovs[1].iov_len);
+ rc = write(echo_fd, tx_buf, msg_len);
+ if (rc < 0) {
+ perror("readv_test: write");
+ break;
+ }
- rc = write(echo_fd, tx_buf, msg_len);
- if (rc < 0) {
- perror("readv_test: write");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "write",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "write", (size_t)rc, msg_len);
- break;
- }
+ rc = readv(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("readv_test: readv");
+ break;
+ }
- rc = readv(echo_fd, iovs, 2);
- if (rc < 0) {
- perror("readv_test: readv");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "write",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "write", (size_t)rc, msg_len);
- break;
- }
+ if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
- if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
- fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
- break;
- }
+ if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
- if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
- fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
- break;
- }
- }
+ tipc_close(echo_fd);
- tipc_close(echo_fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int send_fd_test(void) {
@@ -964,51 +944,51 @@
int main(int argc, char **argv)
{
- int rc = 0;
+ int rc = 0;
- if (argc <= 1) {
- print_usage_and_exit(argv[0], EXIT_FAILURE, false);
- }
+ if (argc <= 1) {
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
- parse_options(argc, argv);
+ parse_options(argc, argv);
- if (!dev_name) {
- dev_name = TIPC_DEFAULT_DEVNAME;
- }
+ if (!dev_name) {
+ dev_name = TIPC_DEFAULT_DEVNAME;
+ }
- if (!test_name) {
- fprintf(stderr, "need a Test to run\n");
- print_usage_and_exit(argv[0], EXIT_FAILURE, true);
- }
+ if (!test_name) {
+ fprintf(stderr, "need a Test to run\n");
+ print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+ }
- if (strcmp(test_name, "connect") == 0) {
- rc = connect_test(opt_repeat);
- } else if (strcmp(test_name, "connect_foo") == 0) {
- rc = connect_foo(opt_repeat);
- } else if (strcmp(test_name, "burst_write") == 0) {
- rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
- } else if (strcmp(test_name, "select") == 0) {
- rc = select_test(opt_repeat, opt_msgburst, opt_msgsize);
- } else if (strcmp(test_name, "blocked_read") == 0) {
- rc = blocked_read_test(opt_repeat);
- } else if (strcmp(test_name, "closer1") == 0) {
- rc = closer1_test(opt_repeat);
- } else if (strcmp(test_name, "closer2") == 0) {
- rc = closer2_test(opt_repeat);
- } else if (strcmp(test_name, "closer3") == 0) {
- rc = closer3_test(opt_repeat);
- } else if (strcmp(test_name, "echo") == 0) {
- rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
- } else if(strcmp(test_name, "ta2ta-ipc") == 0) {
- rc = ta2ta_ipc_test();
- } else if (strcmp(test_name, "dev-uuid") == 0) {
- rc = dev_uuid_test();
- } else if (strcmp(test_name, "ta-access") == 0) {
- rc = ta_access_test();
- } else if (strcmp(test_name, "writev") == 0) {
- rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
- } else if (strcmp(test_name, "readv") == 0) {
- rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
+ if (strcmp(test_name, "connect") == 0) {
+ rc = connect_test(opt_repeat);
+ } else if (strcmp(test_name, "connect_foo") == 0) {
+ rc = connect_foo(opt_repeat);
+ } else if (strcmp(test_name, "burst_write") == 0) {
+ rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "select") == 0) {
+ rc = select_test(opt_repeat, opt_msgburst, opt_msgsize);
+ } else if (strcmp(test_name, "blocked_read") == 0) {
+ rc = blocked_read_test(opt_repeat);
+ } else if (strcmp(test_name, "closer1") == 0) {
+ rc = closer1_test(opt_repeat);
+ } else if (strcmp(test_name, "closer2") == 0) {
+ rc = closer2_test(opt_repeat);
+ } else if (strcmp(test_name, "closer3") == 0) {
+ rc = closer3_test(opt_repeat);
+ } else if (strcmp(test_name, "echo") == 0) {
+ rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "ta2ta-ipc") == 0) {
+ rc = ta2ta_ipc_test();
+ } else if (strcmp(test_name, "dev-uuid") == 0) {
+ rc = dev_uuid_test();
+ } else if (strcmp(test_name, "ta-access") == 0) {
+ rc = ta_access_test();
+ } else if (strcmp(test_name, "writev") == 0) {
+ rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "readv") == 0) {
+ rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
} else if (strcmp(test_name, "send-fd") == 0) {
rc = send_fd_test();
} else {
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index b42d665..21ea7ae 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",
],
}