Merge "Add versionCode parser for package list."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 0e43dae..ebe5f4a 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -78,3 +78,5 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/charger)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin/charger)
diff --git a/adb/Android.bp b/adb/Android.bp
index 3813578..01e00dd 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -24,7 +24,8 @@
"-Wno-missing-field-initializers",
"-Wthread-safety",
"-Wvla",
- "-DADB_HOST=1", // overridden by adbd_defaults
+ "-DADB_HOST=1", // overridden by adbd_defaults
+ "-DALLOW_ADBD_ROOT=0", // overridden by adbd_defaults
],
cpp_std: "experimental",
@@ -79,7 +80,8 @@
product_variables: {
debuggable: {
cflags: [
- "-DALLOW_ADBD_ROOT",
+ "-UALLOW_ADBD_ROOT",
+ "-DALLOW_ADBD_ROOT=1",
"-DALLOW_ADBD_DISABLE_VERITY",
"-DALLOW_ADBD_NO_AUTH",
],
@@ -402,6 +404,14 @@
"liblog",
],
+ product_variables: {
+ debuggable: {
+ required: [
+ "remount",
+ ],
+ },
+ },
+
target: {
android: {
srcs: [
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
index e56ef5a..4d0cf49 100755
--- a/adb/benchmark_device.py
+++ b/adb/benchmark_device.py
@@ -17,6 +17,8 @@
import os
import statistics
+import subprocess
+import tempfile
import time
import adb
@@ -56,6 +58,41 @@
msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
print(msg % (name, len(speeds), median, mean, stddev))
+def benchmark_sink(device=None, size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ speeds = list()
+ cmd = device.adb_cmd + ["raw", "sink:%d" % (size_mb * 1024 * 1024)]
+
+ with tempfile.TemporaryFile() as tmpfile:
+ tmpfile.truncate(size_mb * 1024 * 1024)
+
+ for _ in range(0, 10):
+ tmpfile.seek(0)
+ begin = time.time()
+ subprocess.check_call(cmd, stdin=tmpfile)
+ end = time.time()
+ speeds.append(size_mb / float(end - begin))
+
+ analyze("sink %dMiB" % size_mb, speeds)
+
+def benchmark_source(device=None, size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ speeds = list()
+ cmd = device.adb_cmd + ["raw", "source:%d" % (size_mb * 1024 * 1024)]
+
+ with open(os.devnull, 'w') as devnull:
+ for _ in range(0, 10):
+ begin = time.time()
+ subprocess.check_call(cmd, stdout=devnull)
+ end = time.time()
+ speeds.append(size_mb / float(end - begin))
+
+ analyze("source %dMiB" % size_mb, speeds)
+
def benchmark_push(device=None, file_size_mb=100):
if device == None:
device = adb.get_device()
@@ -110,6 +147,8 @@
def main():
device = adb.get_device()
unlock(device)
+ benchmark_sink(device)
+ benchmark_source(device)
benchmark_push(device)
benchmark_pull(device)
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 4cf3a74..5a7bc8d 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -31,10 +31,12 @@
#include <condition_variable>
#include <mutex>
+#include <optional>
#include <string>
#include <thread>
#include <vector>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
@@ -214,15 +216,26 @@
return adb_connect(nullptr, service, error);
}
-int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
- // first query the adb server's version
+#if defined(__linux__)
+std::optional<std::string> adb_get_server_executable_path() {
+ int port;
+ std::string error;
+ if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) {
+ LOG(FATAL) << "failed to parse server socket spec: " << error;
+ }
+
+ return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port);
+}
+#endif
+
+static bool __adb_check_server_version(std::string* error) {
unique_fd fd(_adb_connect("host:version", nullptr, error));
- LOG(DEBUG) << "adb_connect: service: " << service;
- if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
+ bool local = is_local_socket_spec(__adb_server_socket_spec);
+ if (fd == -2 && !local) {
fprintf(stderr, "* cannot start server on remote host\n");
// error is the original network connection error
- return fd;
+ return false;
} else if (fd == -2) {
fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
start_server:
@@ -232,7 +245,7 @@
// return a generic error string about the overall adb_connect()
// that the caller requested.
*error = "cannot connect to daemon";
- return -1;
+ return false;
} else {
fprintf(stderr, "* daemon started successfully\n");
}
@@ -246,7 +259,7 @@
if (fd >= 0) {
std::string version_string;
if (!ReadProtocolString(fd, &version_string, error)) {
- return -1;
+ return false;
}
ReadOrderlyShutdown(fd);
@@ -254,18 +267,39 @@
if (sscanf(&version_string[0], "%04x", &version) != 1) {
*error = android::base::StringPrintf("cannot parse version string: %s",
version_string.c_str());
- return -1;
+ return false;
}
} else {
// If fd is -1 check for "unknown host service" which would
// indicate a version of adb that does not support the
// version command, in which case we should fall-through to kill it.
if (*error != "unknown host service") {
- return fd;
+ return false;
}
}
if (version != ADB_SERVER_VERSION) {
+#if defined(__linux__)
+ if (version > ADB_SERVER_VERSION && local) {
+ // Try to re-exec the existing adb server's binary.
+ constexpr const char* adb_reexeced = "adb (re-execed)";
+ if (strcmp(adb_reexeced, *__adb_argv) != 0) {
+ __adb_argv[0] = adb_reexeced;
+ std::optional<std::string> server_path_path = adb_get_server_executable_path();
+ std::string server_path;
+ if (server_path_path &&
+ android::base::ReadFileToString(*server_path_path, &server_path)) {
+ if (execve(server_path.c_str(), const_cast<char**>(__adb_argv),
+ const_cast<char**>(__adb_envp)) == -1) {
+ LOG(ERROR) << "failed to exec newer version at " << server_path;
+ }
+
+ // Fall-through to restarting the server.
+ }
+ }
+ }
+#endif
+
fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
version, ADB_SERVER_VERSION);
adb_kill_server();
@@ -273,12 +307,36 @@
}
}
+ return true;
+}
+
+bool adb_check_server_version(std::string* error) {
+ // Only check the version once per process, since this isn't atomic anyway.
+ static std::once_flag once;
+ static bool result;
+ static std::string* err;
+ std::call_once(once, []() {
+ err = new std::string();
+ result = __adb_check_server_version(err);
+ });
+ *error = *err;
+ return result;
+}
+
+int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
+ LOG(DEBUG) << "adb_connect: service: " << service;
+
+ // Query the adb server's version.
+ if (!adb_check_server_version(error)) {
+ return -1;
+ }
+
// if the command is start-server, we are done.
if (service == "host:start-server") {
return 0;
}
- fd.reset(_adb_connect(service, transport, error));
+ unique_fd fd(_adb_connect(service, transport, error));
if (fd == -1) {
D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index 0a73787..8d32c93 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -20,8 +20,14 @@
#include "sysdeps.h"
#include "transport.h"
+#include <optional>
#include <string>
+// Explicitly check the adb server version.
+// All of the commands below do this implicitly.
+// Only the first invocation of this function will check the server version.
+bool adb_check_server_version(std::string* _Nonnull error);
+
// Connect to adb, connect to the named service, and return a valid fd for
// interacting with that service upon success or a negative number on failure.
int adb_connect(std::string_view service, std::string* _Nonnull error);
@@ -65,3 +71,13 @@
// Get the feature set of the current preferred transport.
bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
+
+#if defined(__linux__)
+// Get the path of a file containing the path to the server executable, if the socket spec set via
+// adb_set_socket_spec is a local one.
+std::optional<std::string> adb_get_server_executable_path();
+#endif
+
+// Globally acccesible argv/envp, for the purpose of re-execing adb.
+extern const char* _Nullable * _Nullable __adb_argv;
+extern const char* _Nullable * _Nullable __adb_envp;
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 2bf2924..16fa215 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -638,39 +638,43 @@
fprintf(stdout, "Created child session ID %d.\n", session_id);
session_ids.push_back(session_id);
- struct stat sb;
- if (stat(file, &sb) == -1) {
- fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
- goto finalize_multi_package_session;
+ // Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument.
+ std::vector<std::string> splits = android::base::Split(file, ":");
+
+ for (const std::string& split : splits) {
+ struct stat sb;
+ if (stat(split.c_str(), &sb) == -1) {
+ fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno));
+ goto finalize_multi_package_session;
+ }
+
+ std::string cmd = android::base::StringPrintf(
+ "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
+ static_cast<uint64_t>(sb.st_size), session_id, i,
+ android::base::Basename(split).c_str());
+
+ unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
+ if (local_fd < 0) {
+ fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno));
+ goto finalize_multi_package_session;
+ }
+
+ std::string error;
+ unique_fd remote_fd(adb_connect(cmd, &error));
+ if (remote_fd < 0) {
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+ goto finalize_multi_package_session;
+ }
+
+ copy_to_file(local_fd.get(), remote_fd.get());
+ read_status_line(remote_fd.get(), buf, sizeof(buf));
+
+ if (strncmp("Success", buf, 7)) {
+ fprintf(stderr, "adb: failed to write %s\n", split.c_str());
+ fputs(buf, stderr);
+ goto finalize_multi_package_session;
+ }
}
-
- std::string cmd =
- android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
- install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
- session_id, i, android::base::Basename(file).c_str());
-
- unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
- if (local_fd < 0) {
- fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
- goto finalize_multi_package_session;
- }
-
- std::string error;
- unique_fd remote_fd(adb_connect(cmd, &error));
- if (remote_fd < 0) {
- fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
- goto finalize_multi_package_session;
- }
-
- copy_to_file(local_fd.get(), remote_fd.get());
- read_status_line(remote_fd.get(), buf, sizeof(buf));
-
- if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "adb: failed to write %s\n", file);
- fputs(buf, stderr);
- goto finalize_multi_package_session;
- }
-
all_session_ids += android::base::StringPrintf(" %d", session_id);
}
@@ -718,6 +722,7 @@
fputs(buf, stderr);
}
+ session_ids.push_back(parent_session_id);
// try to abandon all remaining sessions
for (std::size_t i = 0; i < session_ids.size(); i++) {
service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 3286959..43a3e5e 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -222,7 +222,9 @@
" all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
" $ADB_VENDOR_KEYS colon-separated list of keys (files or directories)\n"
" $ANDROID_SERIAL serial number to connect to (see -s)\n"
- " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n");
+ " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n"
+ " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+ );
// clang-format on
}
@@ -293,7 +295,10 @@
callback->OnStderr(buffer_ptr, length);
break;
case ShellProtocol::kIdExit:
- exit_code = protocol->data()[0];
+ // data() returns a char* which doesn't have defined signedness.
+ // Cast to uint8_t to prevent 255 from being sign extended to INT_MIN,
+ // which doesn't get truncated on Windows.
+ exit_code = static_cast<uint8_t>(protocol->data()[0]);
continue;
default:
continue;
@@ -1003,7 +1008,8 @@
#endif /* !defined(_WIN32) */
}
-static bool wait_for_device(const char* service) {
+static bool wait_for_device(const char* service,
+ std::optional<std::chrono::milliseconds> timeout = std::nullopt) {
std::vector<std::string> components = android::base::Split(service, "-");
if (components.size() < 3 || components.size() > 4) {
fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
@@ -1041,6 +1047,13 @@
}
std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
+ if (timeout) {
+ std::thread([timeout]() {
+ std::this_thread::sleep_for(*timeout);
+ fprintf(stderr, "timeout expired while waiting for device\n");
+ _exit(1);
+ }).detach();
+ }
return adb_command(cmd);
}
@@ -1082,8 +1095,21 @@
}
// Wait for the device to go away.
+ TransportType previous_type;
+ const char* previous_serial;
+ TransportId previous_id;
+ adb_get_transport(&previous_type, &previous_serial, &previous_id);
+
adb_set_transport(kTransportAny, nullptr, transport_id);
wait_for_device("wait-for-disconnect");
+
+ // Wait for the device to come back.
+ // If we were using a specific transport ID, there's nothing we can wait for.
+ if (previous_id == 0) {
+ adb_set_transport(previous_type, previous_serial, 0);
+ wait_for_device("wait-for-device", 3000ms);
+ }
+
return true;
}
@@ -1280,9 +1306,9 @@
}
}
-static int adb_connect_command(const std::string& command) {
+static int adb_connect_command(const std::string& command, TransportId* transport = nullptr) {
std::string error;
- unique_fd fd(adb_connect(command, &error));
+ unique_fd fd(adb_connect(transport, command, &error));
if (fd < 0) {
fprintf(stderr, "error: %s\n", error.c_str());
return 1;
@@ -1371,9 +1397,9 @@
TransportId transport_id = 0;
while (argc > 0) {
- if (!strcmp(argv[0],"server")) {
+ if (!strcmp(argv[0], "server")) {
is_server = true;
- } else if (!strcmp(argv[0],"nodaemon")) {
+ } else if (!strcmp(argv[0], "nodaemon")) {
no_daemon = true;
} else if (!strcmp(argv[0], "fork-server")) {
/* this is a special flag used only when the ADB client launches the ADB Server */
@@ -1410,11 +1436,11 @@
if (*id != '\0') {
error_exit("invalid transport id");
}
- } else if (!strcmp(argv[0],"-d")) {
+ } else if (!strcmp(argv[0], "-d")) {
transport_type = kTransportUsb;
- } else if (!strcmp(argv[0],"-e")) {
+ } else if (!strcmp(argv[0], "-e")) {
transport_type = kTransportLocal;
- } else if (!strcmp(argv[0],"-a")) {
+ } else if (!strcmp(argv[0], "-a")) {
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
if (argv[0][2] == '\0') {
@@ -1546,6 +1572,10 @@
}
std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
+ std::string error;
+ if (!adb_check_server_version(&error)) {
+ error_exit("failed to check server version: %s", error.c_str());
+ }
printf("List of devices attached\n");
return adb_query_command(query);
}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 2ee81a9..0c5c28f 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -32,12 +32,16 @@
#include "adb.h"
#include "adb_auth.h"
+#include "adb_client.h"
#include "adb_listeners.h"
#include "adb_utils.h"
#include "commandline.h"
#include "sysdeps/chrono.h"
#include "transport.h"
+const char** __adb_argv;
+const char** __adb_envp;
+
static void setup_daemon_logging() {
const std::string log_file_path(GetLogFilePath());
int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
@@ -191,13 +195,29 @@
notify_thread.detach();
}
+#if defined(__linux__)
+ // Write our location to .android/adb.$PORT, so that older clients can exec us.
+ std::string path;
+ if (!android::base::Readlink("/proc/self/exe", &path)) {
+ PLOG(ERROR) << "failed to readlink /proc/self/exe";
+ }
+
+ std::optional<std::string> server_executable_path = adb_get_server_executable_path();
+ if (server_executable_path) {
+ if (!android::base::WriteStringToFile(path, *server_executable_path)) {
+ PLOG(ERROR) << "failed to write server path to " << path;
+ }
+ }
+#endif
+
D("Event loop starting");
fdevent_loop();
-
return 0;
}
-int main(int argc, char** argv) {
+int main(int argc, char* argv[], char* envp[]) {
+ __adb_argv = const_cast<const char**>(argv);
+ __adb_envp = const_cast<const char**>(envp);
adb_trace_init(argv);
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 70deb31..29bd798 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -25,7 +25,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -211,22 +210,6 @@
return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
}
-static bool is_mountpoint(const std::string& path, pid_t tid) {
- const std::string mountinfo_path = "/proc/" + std::to_string(tid) + "/mountinfo";
- std::string mountinfo;
- if (!android::base::ReadFileToString(mountinfo_path, &mountinfo)) {
- PLOG(ERROR) << "Failed to open " << mountinfo_path;
- return false;
- }
- std::vector<std::string> lines = android::base::Split(mountinfo, "\n");
- return std::find_if(lines.begin(), lines.end(), [&path](const auto& line) {
- auto tokens = android::base::Split(line, " ");
- // line format is ...
- // mountid parentmountid major:minor sourcepath targetpath option ...
- return tokens.size() >= 4 && tokens[4] == path;
- }) != lines.end();
-}
-
// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
#pragma GCC poison SendFail
@@ -432,18 +415,6 @@
struct stat st;
bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
(S_ISLNK(st.st_mode) && !S_ISLNK(mode));
-
- // If the path is a file that is a mount point, don't unlink it, but instead
- // truncate to zero. If unlinked, existing mounts on the path is all
- // unmounted
- if (S_ISREG(st.st_mode) && is_mountpoint(path, getpid())) {
- do_unlink = false;
- if (truncate(path.c_str(), 0) == -1) {
- SendSyncFail(s, "truncate to zero failed");
- return false;
- }
- }
-
if (do_unlink) {
adb_unlink(path.c_str());
}
@@ -591,64 +562,7 @@
return true;
}
-#if defined(__ANDROID__)
-class FileSyncPreparer {
- public:
- FileSyncPreparer() : saved_ns_fd_(-1), rooted_(getuid() == 0) {
- const std::string namespace_path = "/proc/" + std::to_string(gettid()) + "/ns/mnt";
- const int ns_fd = adb_open(namespace_path.c_str(), O_RDONLY | O_CLOEXEC);
- if (ns_fd == -1) {
- if (rooted_) PLOG(ERROR) << "Failed to save mount namespace";
- return;
- }
- saved_ns_fd_.reset(ns_fd);
-
- // Note: this is for the current thread only
- if (unshare(CLONE_NEWNS) != 0) {
- if (rooted_) PLOG(ERROR) << "Failed to clone mount namespace";
- return;
- }
-
- // Set the propagation type of / to private so that unmount below is
- // not propagated to other mount namespaces.
- if (mount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr) == -1) {
- if (rooted_) PLOG(ERROR) << "Could not change propagation type of / to MS_PRIVATE";
- return;
- }
-
- // unmount /bionic which is bind-mount to itself by init. Under /bionic,
- // there are other bind mounts for the bionic files. By unmounting this,
- // we unmount them all thus revealing the raw file system that is the
- // same as the local file system seen by the adb client.
- if (umount2("/bionic", MNT_DETACH) == -1 && errno != ENOENT) {
- if (rooted_) PLOG(ERROR) << "Could not unmount /bionic to reveal raw filesystem";
- return;
- }
- }
-
- ~FileSyncPreparer() {
- if (saved_ns_fd_.get() != -1) {
- // In fact, this is not strictly required because this thread for file
- // sync service will be destroyed after the current transfer is all
- // done. However, let's restore the ns in case the same thread is
- // reused by multiple transfers in the future refactoring.
- if (setns(saved_ns_fd_, CLONE_NEWNS) == -1) {
- PLOG(ERROR) << "Failed to restore saved mount namespace";
- }
- }
- }
-
- private:
- unique_fd saved_ns_fd_;
- bool rooted_;
-};
-#endif
-
void file_sync_service(unique_fd fd) {
-#if defined(__ANDROID__)
- FileSyncPreparer preparer;
-#endif
-
std::vector<char> buffer(SYNC_DATA_MAX);
while (handle_sync_command(fd.get(), buffer)) {
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index fce3a4f..e5a4917 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -58,17 +58,23 @@
#if defined(__ANDROID__)
static const char* root_seclabel = nullptr;
+static inline bool is_device_unlocked() {
+ return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
+}
+
static bool should_drop_capabilities_bounding_set() {
-#if defined(ALLOW_ADBD_ROOT)
- if (__android_log_is_debuggable()) {
- return false;
+ if (ALLOW_ADBD_ROOT || is_device_unlocked()) {
+ if (__android_log_is_debuggable()) {
+ return false;
+ }
}
-#endif
return true;
}
static bool should_drop_privileges() {
-#if defined(ALLOW_ADBD_ROOT)
+ // "adb root" not allowed, always drop privileges.
+ if (!ALLOW_ADBD_ROOT && !is_device_unlocked()) return true;
+
// The properties that affect `adb root` and `adb unroot` are ro.secure and
// ro.debuggable. In this context the names don't make the expected behavior
// particularly obvious.
@@ -98,9 +104,6 @@
}
return drop;
-#else
- return true; // "adb root" not allowed, always drop privileges.
-#endif // ALLOW_ADBD_ROOT
}
static void drop_privileges(int server_port) {
@@ -205,6 +208,10 @@
#if defined(ALLOW_ADBD_NO_AUTH)
// If ro.adb.secure is unset, default to no authentication required.
auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+#elif defined(__ANDROID__)
+ if (is_device_unlocked()) { // allows no authentication when the device is unlocked.
+ auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+ }
#endif
adbd_auth_init();
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 5e6d416..ce494ee 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -14,339 +14,75 @@
* limitations under the License.
*/
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-
#include <errno.h>
#include <fcntl.h>
-#include <mntent.h>
-#include <spawn.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <sys/mount.h>
-#include <sys/statvfs.h>
-#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
-#include <memory>
-#include <set>
#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
-#include <fs_mgr.h>
-#include <fs_mgr_overlayfs.h>
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "set_verity_enable_state_service.h"
-using android::base::Realpath;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::ReadDefaultFstab;
+static constexpr char kRemountCmd[] = "/system/bin/remount";
-// Returns the last device used to mount a directory in /proc/mounts.
-// This will find overlayfs entry where upperdir=lowerdir, to make sure
-// remount is associated with the correct directory.
-static std::string find_proc_mount(const char* dir) {
- std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
- std::string mnt_fsname;
- if (!fp) return mnt_fsname;
-
- // dir might be a symlink, e.g., /product -> /system/product in GSI.
- std::string canonical_path;
- if (!Realpath(dir, &canonical_path)) {
- PLOG(ERROR) << "Realpath failed: " << dir;
- }
-
- mntent* e;
- while ((e = getmntent(fp.get())) != nullptr) {
- if (canonical_path == e->mnt_dir) {
- mnt_fsname = e->mnt_fsname;
- }
- }
- return mnt_fsname;
-}
-
-// Returns the device used to mount a directory in the fstab.
-static std::string find_fstab_mount(const char* dir) {
- Fstab fstab;
- if (!ReadDefaultFstab(&fstab)) {
- return "";
- }
-
- auto entry = std::find_if(fstab.begin(), fstab.end(),
- [&dir](const auto& entry) { return entry.mount_point == dir; });
- if (entry == fstab.end()) {
- return "";
- }
- if (entry->fs_mgr_flags.logical) {
- fs_mgr_update_logical_partition(&(*entry));
- }
- return entry->blk_device;
-}
-
-// The proc entry for / is full of lies, so check fstab instead.
-// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
-static std::string find_mount(const char* dir, bool is_root) {
- if (is_root) {
- return find_fstab_mount(dir);
- } else {
- return find_proc_mount(dir);
- }
-}
-
-bool dev_is_overlayfs(const std::string& dev) {
- return (dev == "overlay") || (dev == "overlayfs");
-}
-
-bool make_block_device_writable(const std::string& dev) {
- if (dev_is_overlayfs(dev)) return true;
- int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
+static bool do_remount(int fd, const std::string& cmd) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
return false;
}
- int OFF = 0;
- bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
- unix_close(fd);
- return result;
-}
-
-static bool can_unshare_blocks(int fd, const char* dev) {
- const char* E2FSCK_BIN = "/system/bin/e2fsck";
- if (access(E2FSCK_BIN, X_OK)) {
- WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev);
+ auto pid = fork();
+ if (pid < 0) {
+ WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
return false;
}
- pid_t child;
- char* env[] = {nullptr};
- const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr};
- if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast<char**>(argv), env)) {
- WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno));
+ if (pid == 0) {
+ // child side of the fork
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+
+ execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
+ _exit(errno);
+ }
+
+ int wstatus = 0;
+ auto ret = waitpid(pid, &wstatus, 0);
+
+ if (ret == -1) {
+ WriteFdFmt(fd, "Failed to wait for %s: %s\n", kRemountCmd, strerror(errno));
+ return false;
+ } else if (ret != pid) {
+ WriteFdFmt(fd, "pid %d and waitpid return %d do not match for %s\n",
+ static_cast<int>(pid), static_cast<int>(ret), kRemountCmd);
return false;
}
- int status = 0;
- int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
- if (ret < 0) {
- WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno));
+
+ if (WIFSIGNALED(wstatus)) {
+ WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
+ strsignal(WTERMSIG(wstatus)));
return false;
}
- if (!WIFEXITED(status)) {
- WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+
+ if (!WIFEXITED(wstatus)) {
+ WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
return false;
}
- int rc = WEXITSTATUS(status);
- if (rc != 0) {
- WriteFdFmt(fd,
- "%s is deduplicated, and an e2fsck check failed. It might not "
- "have enough free-space to be remounted as writable.\n",
- dev);
+
+ if (WEXITSTATUS(wstatus)) {
+ WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
return false;
}
+
return true;
}
-static unsigned long get_mount_flags(int fd, const char* dir) {
- struct statvfs st_vfs;
- if (statvfs(dir, &st_vfs) == -1) {
- // Even though we could not get the original mount flags, assume that
- // the mount was originally read-only.
- WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
- return MS_RDONLY;
- }
- return st_vfs.f_flag;
-}
-
-static bool remount_partition(int fd, const char* dir) {
- if (!directory_exists(dir)) {
- return true;
- }
- bool is_root = strcmp(dir, "/") == 0;
- if (is_root && dev_is_overlayfs(find_mount("/system", false))) {
- dir = "/system";
- is_root = false;
- }
- std::string dev = find_mount(dir, is_root);
- if (is_root && dev.empty()) {
- // The fstab entry will be /system if the device switched roots during
- // first-stage init.
- dev = find_mount("/system", true);
- }
- // Even if the device for the root is not found, we still try to remount it
- // as rw. This typically only happens when running Android in a container:
- // the root will almost always be in a loop device, which is dynamic, so
- // it's not convenient to put in the fstab.
- if (dev.empty() && !is_root) {
- return true;
- }
- if (!dev.empty() && !make_block_device_writable(dev)) {
- WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
- dir, dev.c_str(), strerror(errno));
- return false;
- }
-
- unsigned long remount_flags = get_mount_flags(fd, dir);
- remount_flags &= ~MS_RDONLY;
- remount_flags |= MS_REMOUNT;
-
- if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) {
- // This is useful for cases where the superblock is already marked as
- // read-write, but the mount itself is read-only, such as containers
- // where the remount with just MS_REMOUNT is forbidden by the kernel.
- WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
- return false;
- }
- if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
- WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
- return false;
- }
- return true;
-}
-
-static void reboot_for_remount(int fd, bool need_fsck) {
- std::string reboot_cmd = "reboot";
- if (need_fsck) {
- const std::vector<std::string> options = {"--fsck_unshare_blocks"};
- std::string err;
- if (!write_bootloader_message(options, &err)) {
- WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
- return;
- }
-
- WriteFdExactly(fd,
- "The device will now reboot to recovery and attempt "
- "un-deduplication.\n");
- reboot_cmd = "reboot,recovery";
- }
-
- sync();
- android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
-}
-
-static void try_unmount_bionic(int fd) {
- static constexpr const char* kBionic = "/bionic";
- struct statfs buf;
- if (statfs(kBionic, &buf) == -1) {
- WriteFdFmt(fd, "statfs of the %s mount failed: %s.\n", kBionic, strerror(errno));
- return;
- }
- if (buf.f_flags & ST_RDONLY) {
- // /bionic is on a read-only partition; can happen for
- // non-system-as-root-devices. Don' try to unmount.
- return;
- }
- // Success/Fail of the actual remount will be reported by the function.
- remount_partition(fd, kBionic);
- return;
-}
-
void remount_service(unique_fd fd, const std::string& cmd) {
- bool user_requested_reboot = cmd == "-R";
-
- if (getuid() != 0) {
- WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
- return;
- }
-
- bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
- bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
-
- std::vector<std::string> partitions{"/", "/odm", "/oem", "/product_services",
- "/product", "/vendor"};
-
- if (system_verified || vendor_verified) {
- // Disable verity automatically (reboot will be required).
- set_verity_enabled_state_service(unique_fd(dup(fd.get())), false);
-
- // If overlayfs is not supported, we try and remount or set up
- // un-deduplication. If it is supported, we can go ahead and wait for
- // a reboot.
- if (fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported) {
- if (user_requested_reboot) {
- if (android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot")) {
- WriteFdExactly(fd.get(), "rebooting device\n");
- } else {
- WriteFdExactly(fd.get(), "reboot failed\n");
- }
- }
- return;
- }
- } else if (fs_mgr_overlayfs_setup()) {
- // If we can use overlayfs, lets get it in place first before we
- // struggle with determining deduplication operations.
- Fstab fstab;
- if (ReadDefaultFstab(&fstab) && fs_mgr_overlayfs_mount_all(&fstab)) {
- WriteFdExactly(fd.get(), "overlayfs mounted\n");
- }
- }
-
- // If overlayfs is supported, we don't bother trying to un-deduplicate
- // partitions.
- std::set<std::string> dedup;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
- // Find partitions that are deduplicated, and can be un-deduplicated.
- for (const auto& part : partitions) {
- auto partition = part;
- if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system";
- std::string dev = find_mount(partition.c_str(), partition == "/");
- if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
- continue;
- }
- if (can_unshare_blocks(fd.get(), dev.c_str())) {
- dedup.emplace(partition);
- }
- }
-
- // Reboot now if the user requested it (and an operation needs a reboot).
- if (user_requested_reboot) {
- if (!dedup.empty()) {
- reboot_for_remount(fd.get(), !dedup.empty());
- return;
- }
- WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
- }
- }
-
- bool success = true;
- for (const auto& partition : partitions) {
- // Don't try to remount partitions that need an fsck in recovery.
- if (dedup.count(partition)) {
- continue;
- }
- success &= remount_partition(fd.get(), partition.c_str());
- }
-
- if (!dedup.empty()) {
- WriteFdExactly(fd.get(),
- "The following partitions are deduplicated and cannot "
- "yet be remounted:\n");
- for (const std::string& name : dedup) {
- WriteFdFmt(fd.get(), " %s\n", name.c_str());
- }
-
- WriteFdExactly(fd.get(),
- "To reboot and un-deduplicate the listed partitions, "
- "please retry with adb remount -R.\n");
- if (system_verified || vendor_verified) {
- WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
- }
- return;
- }
-
- try_unmount_bionic(fd.get());
-
- if (!success) {
- WriteFdExactly(fd.get(), "remount failed\n");
- } else {
- WriteFdExactly(fd.get(), "remount succeeded\n");
- }
+ const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
+ WriteFdFmt(fd.get(), "remount %s\n", success);
}
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
index c847403..522a5da 100644
--- a/adb/daemon/remount_service.h
+++ b/adb/daemon/remount_service.h
@@ -21,6 +21,5 @@
#include "adb_unique_fd.h"
#if defined(__ANDROID__)
-bool make_block_device_writable(const std::string&);
void remount_service(unique_fd, const std::string&);
#endif
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 92851c0..889229f 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -25,6 +25,7 @@
#include <libavb_user/libavb_user.h>
#include <stdarg.h>
#include <stdio.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <android-base/properties.h>
@@ -37,7 +38,6 @@
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
-#include "remount_service.h"
#include "fec/io.h"
@@ -51,6 +51,17 @@
if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
}
+static bool make_block_device_writable(const std::string& dev) {
+ unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ return false;
+ }
+
+ int OFF = 0;
+ bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
+ return result;
+}
+
/* Turn verity on/off */
static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
bool enable) {
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f0e2861..b42236e 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -29,6 +29,7 @@
#include <linux/usb/functionfs.h>
#include <sys/eventfd.h>
+#include <algorithm>
#include <array>
#include <future>
#include <memory>
@@ -56,10 +57,11 @@
// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
static std::optional<bool> gFfsAioSupported;
-static constexpr size_t kUsbReadQueueDepth = 16;
-static constexpr size_t kUsbReadSize = 16384;
+static constexpr size_t kUsbReadQueueDepth = 32;
+static constexpr size_t kUsbReadSize = 8 * PAGE_SIZE;
-static constexpr size_t kUsbWriteQueueDepth = 16;
+static constexpr size_t kUsbWriteQueueDepth = 32;
+static constexpr size_t kUsbWriteSize = 8 * PAGE_SIZE;
static const char* to_string(enum usb_functionfs_event_type type) {
switch (type) {
@@ -113,9 +115,9 @@
};
struct IoBlock {
- bool pending;
+ bool pending = false;
struct iocb control;
- Block payload;
+ std::shared_ptr<Block> payload;
TransferId id() const { return TransferId::from_value(control.aio_data); }
};
@@ -207,8 +209,20 @@
std::lock_guard<std::mutex> lock(write_mutex_);
write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
if (!packet->payload.empty()) {
- write_requests_.push_back(
- CreateWriteBlock(std::move(packet->payload), next_write_id_++));
+ // The kernel attempts to allocate a contiguous block of memory for each write,
+ // which can fail if the write is large and the kernel heap is fragmented.
+ // Split large writes into smaller chunks to avoid this.
+ std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+ size_t offset = 0;
+ size_t len = payload->size();
+
+ while (len > 0) {
+ size_t write_size = std::min(kUsbWriteSize, len);
+ write_requests_.push_back(
+ CreateWriteBlock(payload, offset, write_size, next_write_id_++));
+ len -= write_size;
+ offset += write_size;
+ }
}
SubmitWrites();
return true;
@@ -246,7 +260,6 @@
// until it dies, and then report failure to the transport via HandleError, which will
// eventually result in the transport being destroyed, which will result in UsbFfsConnection
// being destroyed, which unblocks the open thread and restarts this entire process.
- static constexpr int kInterruptionSignal = SIGUSR1;
static std::once_flag handler_once;
std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
@@ -257,28 +270,15 @@
bool started = false;
bool running = true;
while (running) {
- int timeout = -1;
- if (!bound || !started) {
- timeout = 5000 /*ms*/;
- }
-
adb_pollfd pfd[2] = {
{ .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
{ .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
};
- int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout));
+ int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, -1));
if (rc == -1) {
PLOG(FATAL) << "poll on USB control fd failed";
} else if (rc == 0) {
- // Something in the kernel presumably went wrong.
- // Close our endpoints, wait for a bit, and then try again.
- aio_context_.reset();
- read_fd_.reset();
- write_fd_.reset();
- control_fd_.reset();
- std::this_thread::sleep_for(5s);
- HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
- return;
+ LOG(FATAL) << "poll on USB control fd returned 0";
}
if (pfd[1].revents) {
@@ -297,7 +297,7 @@
switch (event.type) {
case FUNCTIONFS_BIND:
- CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+ CHECK(!bound) << "received FUNCTIONFS_BIND while already bound?";
bound = true;
break;
@@ -313,28 +313,7 @@
}
}
- pthread_t worker_thread_handle = worker_thread_.native_handle();
- while (true) {
- int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
- if (rc != 0) {
- LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
- break;
- }
-
- std::this_thread::sleep_for(100ms);
-
- rc = pthread_kill(worker_thread_handle, 0);
- if (rc == 0) {
- continue;
- } else if (rc == ESRCH) {
- break;
- } else {
- LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
- }
- }
-
- worker_thread_.join();
-
+ StopWorker();
aio_context_.reset();
read_fd_.reset();
write_fd_.reset();
@@ -365,12 +344,36 @@
});
}
+ void StopWorker() {
+ pthread_t worker_thread_handle = worker_thread_.native_handle();
+ while (true) {
+ int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+ if (rc != 0) {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ break;
+ }
+
+ std::this_thread::sleep_for(100ms);
+
+ rc = pthread_kill(worker_thread_handle, 0);
+ if (rc == 0) {
+ continue;
+ } else if (rc == ESRCH) {
+ break;
+ } else {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ }
+ }
+
+ worker_thread_.join();
+ }
+
void PrepareReadBlock(IoBlock* block, uint64_t id) {
block->pending = false;
- block->payload.resize(kUsbReadSize);
+ block->payload = std::make_shared<Block>(kUsbReadSize);
block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
- block->control.aio_nbytes = block->payload.size();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
+ block->control.aio_nbytes = block->payload->size();
}
IoBlock CreateReadBlock(uint64_t id) {
@@ -421,7 +424,7 @@
uint64_t read_idx = id.id % kUsbReadQueueDepth;
IoBlock* block = &read_requests_[read_idx];
block->pending = false;
- block->payload.resize(size);
+ block->payload->resize(size);
// Notification for completed reads can be received out of order.
if (block->id().id != needed_read_id_) {
@@ -442,16 +445,16 @@
}
void ProcessRead(IoBlock* block) {
- if (!block->payload.empty()) {
+ if (!block->payload->empty()) {
if (!incoming_header_.has_value()) {
- CHECK_EQ(sizeof(amessage), block->payload.size());
+ CHECK_EQ(sizeof(amessage), block->payload->size());
amessage msg;
- memcpy(&msg, block->payload.data(), sizeof(amessage));
+ memcpy(&msg, block->payload->data(), sizeof(amessage));
LOG(DEBUG) << "USB read:" << dump_header(&msg);
incoming_header_ = msg;
} else {
size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
- Block payload = std::move(block->payload);
+ Block payload = std::move(*block->payload);
CHECK_LE(payload.size(), bytes_left);
incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
}
@@ -506,7 +509,8 @@
SubmitWrites();
}
- std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+ std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
+ size_t len, uint64_t id) {
auto block = std::make_unique<IoBlock>();
block->payload = std::move(payload);
block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
@@ -514,14 +518,20 @@
block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
block->control.aio_reqprio = 0;
block->control.aio_fildes = write_fd_.get();
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
- block->control.aio_nbytes = block->payload.size();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
+ block->control.aio_nbytes = len;
block->control.aio_offset = 0;
block->control.aio_flags = IOCB_FLAG_RESFD;
block->control.aio_resfd = worker_event_fd_.get();
return block;
}
+ std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+ std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
+ size_t len = block->size();
+ return CreateWriteBlock(std::move(block), 0, len, id);
+ }
+
void SubmitWrites() REQUIRES(write_mutex_) {
if (writes_submitted_ == kUsbWriteQueueDepth) {
return;
@@ -594,6 +604,8 @@
std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+
+ static constexpr int kInterruptionSignal = SIGUSR1;
};
void usb_init_legacy();
@@ -628,9 +640,13 @@
}
void usb_init() {
- if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
- usb_init_legacy();
- } else {
+ bool use_nonblocking = android::base::GetBoolProperty(
+ "persist.adb.nonblocking_ffs",
+ android::base::GetBoolProperty("ro.adb.nonblocking_ffs", true));
+
+ if (use_nonblocking) {
std::thread(usb_ffs_open_thread).detach();
+ } else {
+ usb_init_legacy();
}
}
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 687d751..7cc2fac 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -29,6 +29,5 @@
std::string* error);
int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
-// Exposed for testing.
bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
std::string* serial, std::string* error);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9a74fb3..b9f738d 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -53,12 +53,27 @@
// Android Wear has been using port 5601 in all of its documentation/tooling,
// but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
-// Avoid stomping on their port by limiting the number of emulators that can be
-// connected.
-#define ADB_LOCAL_TRANSPORT_MAX 16
+// Avoid stomping on their port by restricting the active scanning range.
+// Once emulators self-(re-)register, they'll have to avoid 5601 in their own way.
+static int adb_local_transport_max_port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT + 16 * 2 - 1;
static std::mutex& local_transports_lock = *new std::mutex();
+static void adb_local_transport_max_port_env_override() {
+ const char* env_max_s = getenv("ADB_LOCAL_TRANSPORT_MAX_PORT");
+ if (env_max_s != nullptr) {
+ size_t env_max;
+ if (ParseUint(&env_max, env_max_s, nullptr) && env_max < 65536) {
+ // < DEFAULT_ADB_LOCAL_TRANSPORT_PORT harmlessly mimics ADB_EMU=0
+ adb_local_transport_max_port = env_max;
+ D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT read as %d", adb_local_transport_max_port);
+ } else {
+ D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT '%s' invalid or >= 65536, so ignored",
+ env_max_s);
+ }
+ }
+}
+
// We keep a map from emulator port to transport.
// TODO: weak_ptr?
static auto& local_transports GUARDED_BY(local_transports_lock) =
@@ -110,7 +125,6 @@
D("reconnect failed: %s", response.c_str());
return ReconnectResult::Retry;
}
-
// This invokes the part of register_socket_transport() that needs to be
// invoked if the atransport* has already been setup. This eventually
// calls atransport->SetConnection() with a newly created Connection*
@@ -168,12 +182,10 @@
#if ADB_HOST
static void PollAllLocalPortsForEmulator() {
- int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
- int count = ADB_LOCAL_TRANSPORT_MAX;
-
// Try to connect to any number of running emulator instances.
- for ( ; count > 0; count--, port += 2 ) {
- local_connect(port);
+ for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
+ port += 2) {
+ local_connect(port); // Note, uses port and port-1, so '=max_port' is OK.
}
}
@@ -289,6 +301,7 @@
#if ADB_HOST
D("transport: local client init");
std::thread(client_socket_thread, port).detach();
+ adb_local_transport_max_port_env_override();
#elif !defined(__ANDROID__)
// Host adbd.
D("transport: local server init");
@@ -371,10 +384,6 @@
if (existing_transport != nullptr) {
D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
fail = -1;
- } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
- // Too many emulators.
- D("cannot register more emulators. Maximum is %d", ADB_LOCAL_TRANSPORT_MAX);
- fail = -1;
} else {
local_transports[adb_port] = t;
}
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index faa845d..7c65dc3 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -41,7 +41,14 @@
HANDLE handle =
CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
(prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
- if (handle == nullptr) return nullptr;
+ if (handle == nullptr) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // Return a MappedFile that's only valid for reading the size.
+ if (length == 0) {
+ return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+ }
+ return nullptr;
+ }
void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
file_offset, file_length);
if (base == nullptr) {
diff --git a/bootstat/OWNERS b/bootstat/OWNERS
index 7fe0443..50b2097 100644
--- a/bootstat/OWNERS
+++ b/bootstat/OWNERS
@@ -1 +1,2 @@
jhawkins@google.com
+salyzyn@google.com
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index ed955ea..ea49798 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -293,6 +293,8 @@
{"kernel_panic,dsps", 166},
{"kernel_panic,wcnss", 167},
{"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
+ {"recovery,quiescent", 169},
+ {"reboot,quiescent", 170},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index a064ca0..bca5e36 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -383,7 +383,9 @@
execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
nullptr, nullptr);
- fatal_errno("exec failed");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to exec crash_dump helper: %s",
+ strerror(errno));
+ return 1;
}
input_write.reset();
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 8798ad3..c08afda 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -316,7 +316,7 @@
std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
std::string line;
- for (unwindstack::MapInfo* map_info : *maps) {
+ for (auto const& map_info : *maps) {
line = " ";
if (print_fault_address_marker) {
if (addr < map_info->start) {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 65a5247..fea0a77 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -403,7 +403,7 @@
RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
int* dsize) {
char status[FB_RESPONSE_SZ + 1];
- auto start = std::chrono::system_clock::now();
+ auto start = std::chrono::steady_clock::now();
auto set_response = [response](std::string s) {
if (response) *response = std::move(s);
@@ -414,7 +414,7 @@
// erase response
set_response("");
- while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
+ while ((std::chrono::steady_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
int r = transport_->Read(status, FB_RESPONSE_SZ);
if (r < 0) {
error_ = ErrnoStr("Status read failed");
@@ -427,6 +427,11 @@
std::string tmp = input.substr(strlen("INFO"));
info_(tmp);
add_info(std::move(tmp));
+ // We may receive one or more INFO packets during long operations,
+ // e.g. flash/erase if they are back by slow media like NAND/NOR
+ // flash. In that case, reset the timer since it's not a real
+ // timeout.
+ start = std::chrono::steady_clock::now();
} else if (android::base::StartsWith(input, "OKAY")) {
set_response(input.substr(strlen("OKAY")));
return SUCCESS;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 974e13e..4ee9624 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -111,3 +111,35 @@
"libgsi_headers",
],
}
+
+cc_binary {
+ name: "remount",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libavb_user",
+ ],
+ shared_libs: [
+ "libbootloader_message",
+ "libbase",
+ "libcrypto",
+ "libfec",
+ "libfs_mgr",
+ ],
+ header_libs: [
+ "libcutils_headers",
+ ],
+ srcs: [
+ "fs_mgr_remount.cpp",
+ ],
+ cppflags: [
+ "-DALLOW_ADBD_DISABLE_VERITY=0",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-UALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_DISABLE_VERITY=1",
+ ],
+ },
+ },
+}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 0c904c4..d26f2d5 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -307,7 +307,7 @@
return false;
}
- if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+ if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), 1024)) != sizeof(*sb)) {
PERROR << "Can't read '" << blk_device << "' superblock";
return false;
}
@@ -326,6 +326,17 @@
return true;
}
+// exported silent version of the above that just answer the question is_ext4
+bool fs_mgr_is_ext4(const std::string& blk_device) {
+ android::base::ErrnoRestorer restore;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) return false;
+ ext4_super_block sb;
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), 1024)) != sizeof(sb)) return false;
+ if (!is_ext4_superblock_valid(&sb)) return false;
+ return true;
+}
+
// Some system images do not have tune2fs for licensing reasons.
// Detect these and skip running it.
static bool tune2fs_available(void) {
@@ -494,11 +505,12 @@
return false;
}
- if (pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET) != sizeof(sb1)) {
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET)) != sizeof(sb1)) {
PERROR << "Can't read '" << blk_device << "' superblock1";
return false;
}
- if (pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET) != sizeof(sb2)) {
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+ sizeof(sb2)) {
PERROR << "Can't read '" << blk_device << "' superblock2";
return false;
}
@@ -511,6 +523,23 @@
return true;
}
+// exported silent version of the above that just answer the question is_f2fs
+bool fs_mgr_is_f2fs(const std::string& blk_device) {
+ android::base::ErrnoRestorer restore;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) return false;
+ __le32 sb;
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_SUPER_OFFSET)) != sizeof(sb)) {
+ return false;
+ }
+ if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+ sizeof(sb)) {
+ return false;
+ }
+ return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
+}
+
//
// Prepare the filesystem on the given block device to be mounted.
//
@@ -1089,7 +1118,7 @@
// Skips mounting the device.
continue;
}
- } else if (!current_entry.avb_key.empty()) {
+ } else if (!current_entry.avb_keys.empty()) {
if (AvbHandle::SetUpStandaloneAvbHashtree(¤t_entry) == AvbHashtreeResult::kFail) {
LERROR << "Failed to set up AVB on standalone partition: "
<< current_entry.mount_point << ", skipping!";
@@ -1320,7 +1349,7 @@
// Skips mounting the device.
continue;
}
- } else if (!fstab_entry.avb_key.empty()) {
+ } else if (!fstab_entry.avb_keys.empty()) {
if (AvbHandle::SetUpStandaloneAvbHashtree(&fstab_entry) == AvbHashtreeResult::kFail) {
LERROR << "Failed to set up AVB on standalone partition: "
<< fstab_entry.mount_point << ", skipping!";
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 82d9144..2f1e41f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -307,6 +307,8 @@
} else {
entry->logical_blk_size = val;
}
+ } else if (StartsWith(flag, "avb_keys=")) { // must before the following "avb"
+ entry->avb_keys = arg;
} else if (StartsWith(flag, "avb")) {
entry->fs_mgr_flags.avb = true;
entry->vbmeta_partition = arg;
@@ -325,8 +327,6 @@
}
} else if (StartsWith(flag, "zram_backing_dev_path=")) {
entry->zram_backing_dev_path = arg;
- } else if (StartsWith(flag, "avb_key=")) {
- entry->avb_key = arg;
} else {
LWARNING << "Warning: unknown flag: " << flag;
}
@@ -759,7 +759,8 @@
.fs_type = "ext4",
.flags = MS_RDONLY,
.fs_options = "barrier=1",
- .avb_key = "/gsi.avbpubkey",
+ // could add more keys separated by ':'.
+ .avb_keys = "/avb/gsi.avbpubkey:",
.logical_partition_name = "system"};
system.fs_mgr_flags.wait = true;
system.fs_mgr_flags.logical = true;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 40da36d..dea4844 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -543,6 +543,10 @@
if (!fs_mgr_rw_access(device_path)) return false;
}
+ auto f2fs = fs_mgr_is_f2fs(device_path);
+ auto ext4 = fs_mgr_is_ext4(device_path);
+ if (!f2fs && !ext4) return false;
+
if (setfscreatecon(kOverlayfsFileContext)) {
PERROR << "setfscreatecon " << kOverlayfsFileContext;
}
@@ -554,6 +558,8 @@
entry.blk_device = device_path;
entry.mount_point = kScratchMountPoint;
entry.fs_type = mnt_type;
+ if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
+ if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
entry.flags = MS_RELATIME;
if (readonly) {
entry.flags |= MS_RDONLY;
@@ -563,12 +569,13 @@
auto save_errno = errno;
auto mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) {
- if (mnt_type == "f2fs") {
+ if ((entry.fs_type == "f2fs") && ext4) {
entry.fs_type = "ext4";
- } else {
+ mounted = fs_mgr_do_mount_one(entry) == 0;
+ } else if ((entry.fs_type == "ext4") && f2fs) {
entry.fs_type = "f2fs";
+ mounted = fs_mgr_do_mount_one(entry) == 0;
}
- mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) save_errno = errno;
}
setfscreatecon(nullptr);
@@ -769,9 +776,10 @@
// in recovery, fastbootd, or gsi mode, not allowed!
if (fs_mgr_access("/system/bin/recovery")) return true;
- if (android::gsi::IsGsiRunning()) return true;
-
- return false;
+ auto save_errno = errno;
+ auto ret = android::gsi::IsGsiRunning();
+ errno = save_errno;
+ return ret;
}
} // namespace
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 166c32b..11602ea 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -100,3 +100,6 @@
const std::string& get_android_dt_dir();
bool is_dt_compatible();
int load_verity_state(const android::fs_mgr::FstabEntry& entry, int* mode);
+
+bool fs_mgr_is_ext4(const std::string& blk_device);
+bool fs_mgr_is_f2fs(const std::string& blk_device);
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
new file mode 100644
index 0000000..e2a4d16
--- /dev/null
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <libavb_user/libavb_user.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <fec/io.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_priv.h>
+#include <fstab/fstab.h>
+
+namespace {
+
+[[noreturn]] void usage(int exit_status) {
+ LOG(INFO) << getprogname()
+ << " [-h] [-R] [-T fstab_file] [partition]...\n"
+ "\t-h --help\tthis help\n"
+ "\t-R --reboot\tdisable verity & reboot to facilitate remount\n"
+ "\t-T --fstab\tcustom fstab file location\n"
+ "\tpartition\tspecific partition(s) (empty does all)\n"
+ "\n"
+ "Remount specified partition(s) read-write, by name or mount point.\n"
+ "-R notwithstanding, verity must be disabled on partition(s).";
+
+ ::exit(exit_status);
+}
+
+bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
+ if (entry.fs_mgr_flags.vold_managed) return false;
+ if (entry.fs_mgr_flags.recovery_only) return false;
+ if (entry.fs_mgr_flags.slot_select_other) return false;
+ if (!(entry.flags & MS_RDONLY)) return false;
+ if (entry.fs_type == "vfat") return false;
+ return true;
+}
+
+const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
+ if (entry.mount_point == "/") return "/system";
+ return entry.mount_point;
+}
+
+const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
+ const android::fs_mgr::FstabEntry& entry) {
+ auto mount_point = system_mount_point(entry);
+ auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
+ [&mount_point](const auto& entry) {
+ return android::base::StartsWith(mount_point,
+ system_mount_point(entry) + "/");
+ });
+ if (it == overlayfs_candidates.end()) return nullptr;
+ return &(*it);
+}
+
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ static const char log_characters[] = "VD\0WEFF";
+ if (severity < sizeof(log_characters)) {
+ auto severity_char = log_characters[severity];
+ if (severity_char) fprintf(stderr, "%c ", severity_char);
+ }
+ fprintf(stderr, "%s\n", message);
+
+ static auto logd = android::base::LogdLogger();
+ logd(id, severity, tag, file, line, message);
+}
+
+[[noreturn]] void reboot(bool dedupe) {
+ if (dedupe) {
+ LOG(INFO) << "The device will now reboot to recovery and attempt un-deduplication.";
+ } else {
+ LOG(INFO) << "Successfully disabled verity\nrebooting device";
+ }
+ ::sync();
+ android::base::SetProperty(ANDROID_RB_PROPERTY, dedupe ? "reboot,recovery" : "reboot,remount");
+ ::sleep(60);
+ ::exit(0); // SUCCESS
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, MyLogger);
+
+ enum {
+ SUCCESS,
+ NOT_USERDEBUG,
+ BADARG,
+ NOT_ROOT,
+ NO_FSTAB,
+ UNKNOWN_PARTITION,
+ INVALID_PARTITION,
+ VERITY_PARTITION,
+ BAD_OVERLAY,
+ NO_MOUNTS,
+ REMOUNT_FAILED,
+ } retval = SUCCESS;
+
+ // If somehow this executable is delivered on a "user" build, it can
+ // not function, so providing a clear message to the caller rather than
+ // letting if fall through and provide a lot of confusing failure messages.
+ if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
+ LOG(ERROR) << "only functions on userdebug or eng builds";
+ return NOT_USERDEBUG;
+ }
+
+ const char* fstab_file = nullptr;
+ auto can_reboot = false;
+
+ struct option longopts[] = {
+ {"fstab", required_argument, nullptr, 'T'},
+ {"help", no_argument, nullptr, 'h'},
+ {"reboot", no_argument, nullptr, 'R'},
+ {0, 0, nullptr, 0},
+ };
+ for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) {
+ switch (opt) {
+ case 'R':
+ can_reboot = true;
+ break;
+ case 'T':
+ if (fstab_file) {
+ LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
+ usage(BADARG);
+ }
+ fstab_file = optarg;
+ break;
+ default:
+ LOG(ERROR) << "Bad Argument -" << char(opt);
+ usage(BADARG);
+ break;
+ case 'h':
+ usage(SUCCESS);
+ break;
+ }
+ }
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "must be run as root";
+ return NOT_ROOT;
+ }
+
+ // Read the selected fstab.
+ android::fs_mgr::Fstab fstab;
+ auto fstab_read = false;
+ if (fstab_file) {
+ fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
+ } else {
+ fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
+ }
+ if (!fstab_read || fstab.empty()) {
+ PLOG(ERROR) << "Failed to read fstab";
+ return NO_FSTAB;
+ }
+
+ // Generate the list of supported overlayfs mount points.
+ auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+ // Generate the all remountable partitions sub-list
+ android::fs_mgr::Fstab all;
+ for (auto const& entry : fstab) {
+ if (!remountable_partition(entry)) continue;
+ if (overlayfs_candidates.empty() ||
+ GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
+ (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
+ all.emplace_back(entry);
+ }
+ }
+
+ // Parse the unique list of valid partition arguments.
+ android::fs_mgr::Fstab partitions;
+ for (; argc > optind; ++optind) {
+ auto partition = std::string(argv[optind]);
+ if (partition.empty()) continue;
+ if (partition == "/") partition = "/system";
+ auto find_part = [&partition](const auto& entry) {
+ const auto mount_point = system_mount_point(entry);
+ if (partition == mount_point) return true;
+ if (partition == android::base::Basename(mount_point)) return true;
+ return false;
+ };
+ // Do we know about the partition?
+ auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
+ if (it == fstab.end()) {
+ LOG(ERROR) << "Unknown partition " << partition << ", skipping";
+ retval = UNKNOWN_PARTITION;
+ continue;
+ }
+ // Is that one covered by an existing overlayfs?
+ auto wrap = is_wrapped(overlayfs_candidates, *it);
+ if (wrap) {
+ LOG(INFO) << "partition " << partition << " covered by overlayfs for "
+ << wrap->mount_point << ", switching";
+ partition = system_mount_point(*wrap);
+ }
+ // Is it a remountable partition?
+ it = std::find_if(all.begin(), all.end(), find_part);
+ if (it == all.end()) {
+ LOG(ERROR) << "Invalid partition " << partition << ", skipping";
+ retval = INVALID_PARTITION;
+ continue;
+ }
+ if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
+ partitions.emplace_back(*it);
+ }
+ }
+
+ if (partitions.empty() && !retval) {
+ partitions = all;
+ }
+
+ // Check verity and optionally setup overlayfs backing.
+ auto reboot_later = false;
+ auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
+ for (auto it = partitions.begin(); it != partitions.end();) {
+ auto& entry = *it;
+ auto& mount_point = entry.mount_point;
+ if (fs_mgr_is_verity_enabled(entry)) {
+ retval = VERITY_PARTITION;
+ if (android::base::GetProperty("ro.boot.vbmeta.devices_state", "") != "locked") {
+ if (AvbOps* ops = avb_ops_user_new()) {
+ auto ret = avb_user_verity_set(
+ ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
+ false);
+ avb_ops_user_free(ops);
+ if (ret) {
+ LOG(WARNING) << "Disable verity for " << mount_point;
+ reboot_later = can_reboot;
+ if (reboot_later) {
+ // w/o overlayfs available, also check for dedupe
+ if (!uses_overlayfs) {
+ ++it;
+ continue;
+ }
+ reboot(false);
+ }
+ } else if (fs_mgr_set_blk_ro(entry.blk_device, false)) {
+ fec::io fh(entry.blk_device.c_str(), O_RDWR);
+ if (fh && fh.set_verity_status(false)) {
+ LOG(WARNING) << "Disable verity for " << mount_point;
+ reboot_later = can_reboot;
+ if (reboot_later && !uses_overlayfs) {
+ ++it;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ LOG(ERROR) << "Skipping " << mount_point;
+ it = partitions.erase(it);
+ continue;
+ }
+
+ auto change = false;
+ errno = 0;
+ if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change)) {
+ if (change) {
+ LOG(INFO) << "Using overlayfs for " << mount_point;
+ }
+ } else if (errno) {
+ PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+ retval = BAD_OVERLAY;
+ it = partitions.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (partitions.empty()) {
+ if (reboot_later) reboot(false);
+ LOG(WARNING) << "No partitions to remount";
+ return retval;
+ }
+
+ // Mount overlayfs.
+ errno = 0;
+ if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
+ retval = BAD_OVERLAY;
+ PLOG(ERROR) << "Can not mount overlayfs for partitions";
+ }
+
+ // Get actual mounts _after_ overlayfs has been added.
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ retval = NO_MOUNTS;
+ }
+
+ // Remount selected partitions.
+ for (auto& entry : partitions) {
+ // unlock the r/o key for the mount point device
+ if (entry.fs_mgr_flags.logical) {
+ fs_mgr_update_logical_partition(&entry);
+ }
+ auto blk_device = entry.blk_device;
+ auto mount_point = entry.mount_point;
+
+ for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+ auto& rentry = *it;
+ if (mount_point == rentry.mount_point) {
+ blk_device = rentry.blk_device;
+ break;
+ }
+ if ((mount_point == "/") && (rentry.mount_point == "/system")) {
+ blk_device = rentry.blk_device;
+ mount_point = "/system";
+ break;
+ }
+ }
+ if (blk_device == "/dev/root") {
+ auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+ if (from_fstab) blk_device = from_fstab->blk_device;
+ }
+ fs_mgr_set_blk_ro(blk_device, false);
+
+ // Now remount!
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ continue;
+ }
+ if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
+ mount_point = entry.mount_point;
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ continue;
+ }
+ }
+ PLOG(WARNING) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+ // If errno = EROFS at this point, we are dealing with r/o
+ // filesystem types like squashfs, erofs or ext4 dedupe. We will
+ // consider such a device that does not have CONFIG_OVERLAY_FS
+ // in the kernel as a misconfigured; except for ext4 dedupe.
+ if ((errno == EROFS) && can_reboot) {
+ const std::vector<std::string> msg = {"--fsck_unshare_blocks"};
+ std::string err;
+ if (write_bootloader_message(msg, &err)) reboot(true);
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ errno = EROFS;
+ }
+ retval = REMOUNT_FAILED;
+ }
+
+ if (reboot_later) reboot(false);
+
+ return retval;
+}
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index a3d9fdd..e811447 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -55,7 +55,7 @@
std::string zram_loopback_path;
uint64_t zram_loopback_size = 512 * 1024 * 1024; // 512MB by default;
std::string zram_backing_dev_path;
- std::string avb_key;
+ std::string avb_keys;
struct FsMgrFlags {
bool wait : 1;
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
index 33c3cad..32fc3d2 100644
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -20,13 +20,17 @@
recovery_available: true,
export_include_dirs: ["include"],
cflags: [
- // TODO(b/121211685): Allows us to create a skeleton of required classes
- "-Wno-unused-private-field",
- "-Wno-unused-parameter",
+ "-D_FILE_OFFSET_BITS=64",
],
srcs: [
"fiemap_writer.cpp",
+ "split_fiemap_writer.cpp",
+ "utility.cpp",
+ ],
+
+ static_libs: [
+ "libext4_utils",
],
header_libs: [
@@ -37,6 +41,9 @@
cc_test {
name: "fiemap_writer_test",
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
static_libs: [
"libbase",
"libdm",
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 5b8a9c2..85589cc 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -27,6 +27,7 @@
#include <sys/vfs.h>
#include <unistd.h>
+#include <limits>
#include <string>
#include <utility>
#include <vector>
@@ -50,6 +51,9 @@
FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
+// Large file support must be enabled.
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
static inline void cleanup(const std::string& file_path, bool created) {
if (created) {
unlink(file_path.c_str());
@@ -126,7 +130,8 @@
return DeviceMapperStackPop(bdev_next, bdev_raw);
}
-static bool FileToBlockDevicePath(const std::string& file_path, std::string* bdev_path) {
+bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
+ bool* uses_dm) {
struct stat sb;
if (stat(file_path.c_str(), &sb)) {
PLOG(ERROR) << "Failed to get stat for: " << file_path;
@@ -146,6 +151,10 @@
return false;
}
+ if (uses_dm) {
+ *uses_dm = (bdev_raw != bdev);
+ }
+
LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
<< bdev << ")";
@@ -195,17 +204,21 @@
return false;
}
- if (file_size % sfs.f_bsize) {
- LOG(ERROR) << "File size " << file_size << " is not aligned to optimal block size "
- << sfs.f_bsize << " for file " << file_path;
+ if (!sfs.f_bsize) {
+ LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
return false;
}
// Check if the filesystem is of supported types.
- // Only ext4 and f2fs are tested and supported.
- if ((sfs.f_type != EXT4_SUPER_MAGIC) && (sfs.f_type != F2FS_SUPER_MAGIC)) {
- LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
- return false;
+ // Only ext4, f2fs, and vfat are tested and supported.
+ switch (sfs.f_type) {
+ case EXT4_SUPER_MAGIC:
+ case F2FS_SUPER_MAGIC:
+ case MSDOS_SUPER_MAGIC:
+ break;
+ default:
+ LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
+ return false;
}
uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
@@ -219,15 +232,65 @@
return true;
}
+static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
+ const std::string& file_path,
+ const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+ // Even though this is much faster than writing zeroes, it is still slow
+ // enough that we need to fire the progress callback periodically. To
+ // easily achieve this, we seek in chunks. We use 1000 chunks since
+ // normally we only fire the callback on 1/1000th increments.
+ uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
+
+ // Seek just to the end of each chunk and write a single byte, causing
+ // the filesystem to allocate blocks.
+ off_t cursor = 0;
+ off_t end = static_cast<off_t>(file_size);
+ while (cursor < end) {
+ cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
+ auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
+ if (rv < 0) {
+ PLOG(ERROR) << "Failed to lseek " << file_path;
+ return false;
+ }
+ if (rv != cursor - 1) {
+ LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
+ return false;
+ }
+ char buffer[] = {0};
+ if (!android::base::WriteFully(file_fd, buffer, 1)) {
+ PLOG(ERROR) << "Write failed: " << file_path;
+ return false;
+ }
+ if (on_progress && !on_progress(cursor, file_size)) {
+ return false;
+ }
+ }
+ return true;
+}
+
static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
- uint64_t file_size, std::function<bool(uint64_t, uint64_t)> on_progress) {
+ uint64_t file_size, unsigned int fs_type,
+ std::function<bool(uint64_t, uint64_t)> on_progress) {
// Reserve space for the file on the file system and write it out to make sure the extents
// don't come back unwritten. Return from this function with the kernel file offset set to 0.
// If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
// aren't moved around.
- if (fallocate64(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
- PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
- return false;
+ switch (fs_type) {
+ case EXT4_SUPER_MAGIC:
+ case F2FS_SUPER_MAGIC:
+ if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+ PLOG(ERROR) << "Failed to allocate space for file: " << file_path
+ << " size: " << file_size;
+ return false;
+ }
+ break;
+ case MSDOS_SUPER_MAGIC:
+ // fallocate() is not supported, and not needed, since VFAT does not support holes.
+ // Instead we can perform a much faster allocation.
+ return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
+ default:
+ LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
+ return false;
}
// write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
@@ -246,16 +309,19 @@
}
int permille = -1;
- for (; offset < file_size; offset += blocksz) {
+ while (offset < file_size) {
if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
<< " in file " << file_path;
return false;
}
+
+ offset += blocksz;
+
// Don't invoke the callback every iteration - wait until a significant
// chunk (here, 1/1000th) of the data has been processed.
int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
- if (new_permille != permille) {
+ if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
if (on_progress && !on_progress(offset, file_size)) {
return false;
}
@@ -282,9 +348,9 @@
}
static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type == EXT4_SUPER_MAGIC) {
- // No pinning necessary for ext4. The blocks, once allocated, are expected
- // to be fixed.
+ if (fs_type != F2FS_SUPER_MAGIC) {
+ // No pinning necessary for ext4/msdos. The blocks, once allocated, are
+ // expected to be fixed.
return true;
}
@@ -319,9 +385,9 @@
}
static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type == EXT4_SUPER_MAGIC) {
- // No pinning necessary for ext4. The blocks, once allocated, are expected
- // to be fixed.
+ if (fs_type != F2FS_SUPER_MAGIC) {
+ // No pinning necessary for ext4 or vfat. The blocks, once allocated,
+ // are expected to be fixed.
return true;
}
@@ -433,6 +499,54 @@
return last_extent_seen;
}
+static bool ReadFibmap(int file_fd, const std::string& file_path,
+ std::vector<struct fiemap_extent>* extents) {
+ struct stat s;
+ if (fstat(file_fd, &s)) {
+ PLOG(ERROR) << "Failed to stat " << file_path;
+ return false;
+ }
+
+ unsigned int blksize;
+ if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
+ PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
+ return false;
+ }
+ if (!blksize) {
+ LOG(ERROR) << "Invalid filesystem block size: " << blksize;
+ return false;
+ }
+
+ uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
+ if (num_blocks > std::numeric_limits<uint32_t>::max()) {
+ LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
+ return false;
+ }
+
+ for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
+ uint32_t block = block_number;
+ if (ioctl(file_fd, FIBMAP, &block)) {
+ PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
+ return false;
+ }
+ if (!block) {
+ LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
+ return false;
+ }
+
+ if (!extents->empty() && block == last_block + 1) {
+ extents->back().fe_length += blksize;
+ } else {
+ extents->push_back(fiemap_extent{.fe_logical = block_number,
+ .fe_physical = static_cast<uint64_t>(block) * blksize,
+ .fe_length = static_cast<uint64_t>(blksize),
+ .fe_flags = 0});
+ }
+ last_block = block;
+ }
+ return true;
+}
+
FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
std::function<bool(uint64_t, uint64_t)> progress) {
// if 'create' is false, open an existing file and do not truncate.
@@ -458,7 +572,7 @@
}
std::string bdev_path;
- if (!FileToBlockDevicePath(abs_path, &bdev_path)) {
+ if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
cleanup(abs_path, create);
return nullptr;
@@ -495,8 +609,13 @@
return nullptr;
}
+ // Align up to the nearest block size.
+ if (file_size % blocksz) {
+ file_size += blocksz - (file_size % blocksz);
+ }
+
if (create) {
- if (!AllocateFile(file_fd, abs_path, blocksz, file_size, std::move(progress))) {
+ if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
<< " bytes";
cleanup(abs_path, create);
@@ -513,28 +632,35 @@
// now allocate the FiemapWriter and start setting it up
FiemapUniquePtr fmap(new FiemapWriter());
- if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
- LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
- cleanup(abs_path, create);
- return nullptr;
+ switch (fs_type) {
+ case EXT4_SUPER_MAGIC:
+ case F2FS_SUPER_MAGIC:
+ if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
+ LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
+ cleanup(abs_path, create);
+ return nullptr;
+ }
+ break;
+ case MSDOS_SUPER_MAGIC:
+ if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
+ LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
+ cleanup(abs_path, create);
+ return nullptr;
+ }
+ break;
}
fmap->file_path_ = abs_path;
fmap->bdev_path_ = bdev_path;
- fmap->file_fd_ = std::move(file_fd);
fmap->file_size_ = file_size;
fmap->bdev_size_ = bdevsz;
fmap->fs_type_ = fs_type;
fmap->block_size_ = blocksz;
- LOG(INFO) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
- << bdev_path;
+ LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
+ << bdev_path;
return fmap;
}
-bool FiemapWriter::Read(off64_t off, uint8_t* buffer, uint64_t size) {
- return false;
-}
-
} // namespace fiemap_writer
} // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index 3e8381b..ca51689 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -22,6 +22,7 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/vfs.h>
#include <unistd.h>
#include <string>
@@ -32,8 +33,13 @@
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <libdm/loop_control.h>
-
#include <libfiemap_writer/fiemap_writer.h>
+#include <libfiemap_writer/split_fiemap_writer.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fiemap_writer {
using namespace std;
using namespace std::string_literals;
@@ -43,6 +49,7 @@
std::string gTestDir;
uint64_t testfile_size = 536870912; // default of 512MiB
+size_t gBlockSize = 0;
class FiemapWriterTest : public ::testing::Test {
protected:
@@ -57,6 +64,24 @@
std::string testfile;
};
+class SplitFiemapTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ testfile = gTestDir + "/"s + tinfo->name();
+ }
+
+ void TearDown() override {
+ std::string message;
+ if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
+ cerr << "Could not remove all split files: " << message;
+ }
+ }
+
+ // name of the file we use for testing
+ std::string testfile;
+};
+
TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
// Try creating a file of size ~100TB but aligned to
// 512 byte to make sure block alignment tests don't
@@ -69,39 +94,48 @@
TEST_F(FiemapWriterTest, CreateUnalignedFile) {
// Try creating a file of size 4097 bytes which is guaranteed
- // to be unaligned to all known block sizes. The creation must
- // fail.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4097);
- EXPECT_EQ(fptr, nullptr);
- EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
- EXPECT_EQ(errno, ENOENT);
+ // to be unaligned to all known block sizes.
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
+ ASSERT_NE(fptr, nullptr);
+ ASSERT_EQ(fptr->size(), gBlockSize * 2);
}
TEST_F(FiemapWriterTest, CheckFilePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
ASSERT_NE(fptr, nullptr);
- EXPECT_EQ(fptr->size(), 4096);
+ EXPECT_EQ(fptr->size(), gBlockSize);
EXPECT_EQ(fptr->file_path(), testfile);
EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
}
+TEST_F(FiemapWriterTest, CheckFileSize) {
+ // Create a large-ish file and test that the expected size matches.
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
+ ASSERT_NE(fptr, nullptr);
+
+ struct stat s;
+ ASSERT_EQ(stat(testfile.c_str(), &s), 0);
+ EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
+}
+
TEST_F(FiemapWriterTest, CheckProgress) {
- std::vector<uint64_t> expected{
- 0,
- 4096,
- };
+ std::vector<uint64_t> expected;
size_t invocations = 0;
auto callback = [&](uint64_t done, uint64_t total) -> bool {
- EXPECT_LT(invocations, expected.size());
+ if (invocations >= expected.size()) {
+ return false;
+ }
EXPECT_EQ(done, expected[invocations]);
- EXPECT_EQ(total, 4096);
+ EXPECT_EQ(total, gBlockSize);
invocations++;
return true;
};
- auto ptr = FiemapWriter::Open(testfile, 4096, true, std::move(callback));
+ expected.push_back(gBlockSize);
+
+ auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(invocations, 2);
+ EXPECT_EQ(invocations, expected.size());
}
TEST_F(FiemapWriterTest, CheckPinning) {
@@ -111,8 +145,8 @@
}
TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
- EXPECT_EQ(fptr->size(), 4096);
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
+ EXPECT_EQ(fptr->size(), gBlockSize);
EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
}
@@ -130,7 +164,7 @@
struct stat sb;
ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
- EXPECT_EQ(sb.st_size, testfile_size);
+ EXPECT_GE(sb.st_size, testfile_size);
}
TEST_F(FiemapWriterTest, CheckFileExtents) {
@@ -139,44 +173,223 @@
EXPECT_GT(fptr->extents().size(), 0);
}
-class TestExistingFile : public ::testing::Test {
- protected:
- void SetUp() override {
- unaligned_file_ = gTestDir + "/unaligned_file";
- file_4k_ = gTestDir + "/file_4k";
- file_32k_ = gTestDir + "/file_32k";
+TEST_F(FiemapWriterTest, ExistingFile) {
+ // Create the file.
+ { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
+ // Test that we can still open it.
+ {
+ auto ptr = FiemapWriter::Open(testfile, 0, false);
+ ASSERT_NE(ptr, nullptr);
+ EXPECT_GT(ptr->extents().size(), 0);
+ }
+}
- CleanupFiles();
- fptr_unaligned = FiemapWriter::Open(unaligned_file_, 4097);
- fptr_4k = FiemapWriter::Open(file_4k_, 4096);
- fptr_32k = FiemapWriter::Open(file_32k_, 32768);
+TEST_F(FiemapWriterTest, FileDeletedOnError) {
+ auto callback = [](uint64_t, uint64_t) -> bool { return false; };
+ auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
+ EXPECT_EQ(ptr, nullptr);
+ EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_F(FiemapWriterTest, MaxBlockSize) {
+ ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
+}
+
+TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
+ ASSERT_NE(fptr, nullptr);
+
+ switch (fptr->fs_type()) {
+ case F2FS_SUPER_MAGIC:
+ case EXT4_SUPER_MAGIC:
+ // Skip the test for FIEMAP supported filesystems. This is really
+ // because f2fs/ext4 have caches that seem to defeat reading back
+ // directly from the block device, and writing directly is too
+ // dangerous.
+ std::cout << "Skipping test, filesystem does not use FIBMAP\n";
+ return;
}
- void TearDown() { CleanupFiles(); }
+ bool uses_dm;
+ std::string bdev_path;
+ ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
- void CleanupFiles() {
- unlink(unaligned_file_.c_str());
- unlink(file_4k_.c_str());
- unlink(file_32k_.c_str());
+ if (uses_dm) {
+ // We could use a device-mapper wrapper here to bypass encryption, but
+ // really this test is for FIBMAP correctness on VFAT (where encryption
+ // is never used), so we don't bother.
+ std::cout << "Skipping test, block device is metadata encrypted\n";
+ return;
}
- std::string unaligned_file_;
- std::string file_4k_;
- std::string file_32k_;
- FiemapUniquePtr fptr_unaligned;
- FiemapUniquePtr fptr_4k;
- FiemapUniquePtr fptr_32k;
-};
+ std::string data(fptr->size(), '\0');
+ for (size_t i = 0; i < data.size(); i++) {
+ data[i] = 'A' + static_cast<char>(data.size() % 26);
+ }
-TEST_F(TestExistingFile, ErrorChecks) {
- EXPECT_EQ(fptr_unaligned, nullptr);
- EXPECT_NE(fptr_4k, nullptr);
- EXPECT_NE(fptr_32k, nullptr);
+ {
+ unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
+ ASSERT_GE(fd, 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
+ ASSERT_EQ(fsync(fd), 0);
+ }
- EXPECT_EQ(fptr_4k->size(), 4096);
- EXPECT_EQ(fptr_32k->size(), 32768);
- EXPECT_GT(fptr_4k->extents().size(), 0);
- EXPECT_GT(fptr_32k->extents().size(), 0);
+ ASSERT_FALSE(fptr->extents().empty());
+ const auto& first_extent = fptr->extents()[0];
+
+ unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(bdev, 0);
+
+ off_t where = first_extent.fe_physical;
+ ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
+
+ // Note: this will fail on encrypted folders.
+ std::string actual(data.size(), '\0');
+ ASSERT_GE(first_extent.fe_length, data.size());
+ ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
+ EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, Create) {
+ auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+ ASSERT_NE(ptr, nullptr);
+
+ auto extents = ptr->extents();
+
+ // Destroy the fiemap, closing file handles. This should not delete them.
+ ptr = nullptr;
+
+ std::vector<std::string> files;
+ ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
+ for (const auto& path : files) {
+ EXPECT_EQ(access(path.c_str(), F_OK), 0);
+ }
+
+ ASSERT_GE(extents.size(), files.size());
+}
+
+TEST_F(SplitFiemapTest, Open) {
+ {
+ auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+ ASSERT_NE(ptr, nullptr);
+ }
+
+ auto ptr = SplitFiemap::Open(testfile);
+ ASSERT_NE(ptr, nullptr);
+
+ auto extents = ptr->extents();
+ ASSERT_GE(extents.size(), 24);
+}
+
+TEST_F(SplitFiemapTest, DeleteOnFail) {
+ auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
+ ASSERT_EQ(ptr, nullptr);
+
+ std::string first_file = testfile + ".0001";
+ ASSERT_NE(access(first_file.c_str(), F_OK), 0);
+ ASSERT_EQ(errno, ENOENT);
+ ASSERT_NE(access(testfile.c_str(), F_OK), 0);
+ ASSERT_EQ(errno, ENOENT);
+}
+
+static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
+ std::string result;
+ for (int i = 0; i < num_files; i++) {
+ std::string path = base_path + android::base::StringPrintf(".%04d", i);
+ std::string data;
+ if (!android::base::ReadFileToString(path, &data)) {
+ return {};
+ }
+ result += data;
+ }
+ return result;
+}
+
+TEST_F(SplitFiemapTest, WriteWholeFile) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+ ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+
+ std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+ auto actual = ReadSplitFiles(testfile, 3);
+ ASSERT_EQ(expected.size(), actual.size());
+ EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks1) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+
+ // Write in chunks of 1000 (so some writes straddle the boundary of two
+ // files).
+ size_t bytes_written = 0;
+ while (bytes_written < kSize) {
+ size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
+ char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+ ASSERT_TRUE(ptr->Write(data, to_write));
+ bytes_written += to_write;
+ }
+
+ std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+ auto actual = ReadSplitFiles(testfile, 3);
+ ASSERT_EQ(expected.size(), actual.size());
+ EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks2) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+
+ // Write in chunks of 32KiB so every write is exactly at the end of the
+ // current file.
+ size_t bytes_written = 0;
+ while (bytes_written < kSize) {
+ size_t to_write = std::min(kSize - bytes_written, kChunkSize);
+ char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+ ASSERT_TRUE(ptr->Write(data, to_write));
+ bytes_written += to_write;
+ }
+
+ std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+ auto actual = ReadSplitFiles(testfile, 3);
+ ASSERT_EQ(expected.size(), actual.size());
+ EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WritePastEnd) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+ ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+ ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
}
class VerifyBlockWritesExt4 : public ::testing::Test {
@@ -263,22 +476,45 @@
std::string fs_path;
};
+bool DetermineBlockSize() {
+ struct statfs s;
+ if (statfs(gTestDir.c_str(), &s)) {
+ std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!s.f_bsize) {
+ std::cerr << "Invalid block size: " << s.f_bsize << "\n";
+ return false;
+ }
+
+ gBlockSize = s.f_bsize;
+ return true;
+}
+
+} // namespace fiemap_writer
+} // namespace android
+
+using namespace android::fiemap_writer;
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (argc <= 1) {
cerr << "Usage: <test_dir> [file_size]\n";
cerr << "\n";
- cerr << "Note: test_dir must be a writable directory.\n";
+ cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
exit(EXIT_FAILURE);
}
::android::base::InitLogging(argv, ::android::base::StderrLogger);
std::string tempdir = argv[1] + "/XXXXXX"s;
if (!mkdtemp(tempdir.data())) {
- cerr << "unable to create tempdir on " << argv[1];
+ cerr << "unable to create tempdir on " << argv[1] << "\n";
exit(EXIT_FAILURE);
}
- gTestDir = tempdir;
+ if (!android::base::Realpath(tempdir, &gTestDir)) {
+ cerr << "unable to find realpath for " << tempdir;
+ exit(EXIT_FAILURE);
+ }
if (argc > 2) {
testfile_size = strtoull(argv[2], NULL, 0);
@@ -287,6 +523,10 @@
}
}
+ if (!DetermineBlockSize()) {
+ exit(EXIT_FAILURE);
+ }
+
auto result = RUN_ALL_TESTS();
std::string cmd = "rm -rf " + gTestDir;
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
index edbae77..9486122 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -41,6 +41,9 @@
// invoked, if create is true, while the file is being initialized. It receives the bytes
// written and the number of total bytes. If the callback returns false, the operation will
// fail.
+ //
+ // Note: when create is true, the file size will be aligned up to the nearest file system
+ // block.
static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
bool create = true,
std::function<bool(uint64_t, uint64_t)> progress = {});
@@ -57,10 +60,12 @@
// FiemapWriter::Open).
static bool HasPinnedExtents(const std::string& file_path);
- // The counter part of Write(). It is an error for the offset to be unaligned with
- // the block device's block size.
- // In case of error, the contents of buffer MUST be discarded.
- bool Read(off64_t off, uint8_t* buffer, uint64_t size);
+ // Returns the underlying block device of a file. This will look past device-mapper layers.
+ // If an intermediate device-mapper layer would not maintain a 1:1 mapping (i.e. is a non-
+ // trivial dm-linear), then this will fail. If device-mapper nodes are encountered, then
+ // |uses_dm| will be set to true.
+ static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
+ bool* uses_dm = nullptr);
~FiemapWriter() = default;
@@ -69,6 +74,7 @@
const std::string& bdev_path() const { return bdev_path_; };
uint64_t block_size() const { return block_size_; };
const std::vector<struct fiemap_extent>& extents() { return extents_; };
+ uint32_t fs_type() const { return fs_type_; }
// Non-copyable & Non-movable
FiemapWriter(const FiemapWriter&) = delete;
@@ -82,9 +88,6 @@
// Block device on which we have created the file.
std::string bdev_path_;
- // File descriptors for the file and block device
- ::android::base::unique_fd file_fd_;
-
// Size in bytes of the file this class is writing
uint64_t file_size_;
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
new file mode 100644
index 0000000..7b977e1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "fiemap_writer.h"
+
+namespace android {
+namespace fiemap_writer {
+
+// Wrapper around FiemapWriter that is able to split images across files if
+// necessary.
+class SplitFiemap final {
+ public:
+ using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
+
+ // Create a new split fiemap file. If |max_piece_size| is 0, the number of
+ // pieces will be determined automatically by detecting the filesystem.
+ // Otherwise, the file will be split evenly (with the remainder in the
+ // final file).
+ static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
+ uint64_t max_piece_size,
+ ProgressCallback progress = {});
+
+ // Open an existing split fiemap file.
+ static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
+
+ ~SplitFiemap();
+
+ // Return a list of all files created for a split file.
+ static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
+
+ // Destroy all components of a split file. If the root file does not exist,
+ // this returns true and does not report an error.
+ static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
+
+ // Return whether all components of a split file still have pinned extents.
+ bool HasPinnedExtents() const;
+
+ // Helper method for writing data that spans files. Note there is no seek
+ // method (yet); this starts at 0 and increments the position by |bytes|.
+ bool Write(const void* data, uint64_t bytes);
+
+ // Flush all writes to all split files.
+ bool Flush();
+
+ const std::vector<struct fiemap_extent>& extents();
+ uint32_t block_size() const;
+ uint64_t size() const { return total_size_; }
+ const std::string& bdev_path() const;
+
+ // Non-copyable & Non-movable
+ SplitFiemap(const SplitFiemap&) = delete;
+ SplitFiemap& operator=(const SplitFiemap&) = delete;
+ SplitFiemap& operator=(SplitFiemap&&) = delete;
+ SplitFiemap(SplitFiemap&&) = delete;
+
+ private:
+ SplitFiemap() = default;
+ void AddFile(FiemapUniquePtr&& file);
+
+ bool creating_ = false;
+ std::string list_file_;
+ std::vector<FiemapUniquePtr> files_;
+ std::vector<struct fiemap_extent> extents_;
+ uint64_t total_size_ = 0;
+
+ // Most recently open file and position for Write().
+ size_t cursor_index_ = 0;
+ uint64_t cursor_file_pos_ = 0;
+ android::base::unique_fd cursor_fd_;
+};
+
+} // namespace fiemap_writer
+} // namespace android
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
new file mode 100644
index 0000000..a0ccc10
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2019 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 <libfiemap_writer/split_fiemap_writer.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fiemap_writer {
+
+using android::base::unique_fd;
+
+// We use a four-digit suffix at the end of filenames.
+static const size_t kMaxFilePieces = 500;
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
+ uint64_t max_piece_size,
+ ProgressCallback progress) {
+ if (!file_size) {
+ LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
+ return nullptr;
+ }
+
+ if (!max_piece_size) {
+ max_piece_size = DetermineMaximumFileSize(file_path);
+ if (!max_piece_size) {
+ LOG(ERROR) << "Could not determine maximum file size for " << file_path;
+ return nullptr;
+ }
+ }
+
+ // Call |progress| only when the total percentage would significantly change.
+ int permille = -1;
+ uint64_t total_bytes_written = 0;
+ auto on_progress = [&](uint64_t written, uint64_t) -> bool {
+ uint64_t actual_written = total_bytes_written + written;
+ int new_permille = (actual_written * 1000) / file_size;
+ if (new_permille != permille && actual_written < file_size) {
+ if (progress && !progress(actual_written, file_size)) {
+ return false;
+ }
+ permille = new_permille;
+ }
+ return true;
+ };
+
+ std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+ out->creating_ = true;
+ out->list_file_ = file_path;
+
+ // Create the split files.
+ uint64_t remaining_bytes = file_size;
+ while (remaining_bytes) {
+ if (out->files_.size() >= kMaxFilePieces) {
+ LOG(ERROR) << "Requested size " << file_size << " created too many split files";
+ return nullptr;
+ }
+ std::string chunk_path =
+ android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
+ uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
+ auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
+ if (!writer) {
+ return nullptr;
+ }
+
+ // To make sure the alignment doesn't create too much inconsistency, we
+ // account the *actual* size, not the requested size.
+ total_bytes_written += writer->size();
+ remaining_bytes -= writer->size();
+
+ out->AddFile(std::move(writer));
+ }
+
+ // Create the split file list.
+ unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open " << file_path;
+ return nullptr;
+ }
+
+ for (const auto& writer : out->files_) {
+ std::string line = android::base::Basename(writer->file_path()) + "\n";
+ if (!android::base::WriteFully(fd, line.data(), line.size())) {
+ PLOG(ERROR) << "Write failed " << file_path;
+ return nullptr;
+ }
+ }
+
+ // Unset this bit, so we don't unlink on destruction.
+ out->creating_ = false;
+ return out;
+}
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
+ std::vector<std::string> files;
+ if (!GetSplitFileList(file_path, &files)) {
+ return nullptr;
+ }
+
+ std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+ out->list_file_ = file_path;
+
+ for (const auto& file : files) {
+ auto writer = FiemapWriter::Open(file, 0, false);
+ if (!writer) {
+ // Error was logged in Open().
+ return nullptr;
+ }
+ out->AddFile(std::move(writer));
+ }
+ return out;
+}
+
+bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
+ // This is not the most efficient thing, but it is simple and recovering
+ // the fiemap/fibmap is much more expensive.
+ std::string contents;
+ if (!android::base::ReadFileToString(file_path, &contents, true)) {
+ PLOG(ERROR) << "Error reading file: " << file_path;
+ return false;
+ }
+
+ std::vector<std::string> names = android::base::Split(contents, "\n");
+ std::string dir = android::base::Dirname(file_path);
+ for (const auto& name : names) {
+ if (!name.empty()) {
+ list->emplace_back(dir + "/" + name);
+ }
+ }
+ return true;
+}
+
+bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
+ // Early exit if this does not exist, and do not report an error.
+ if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
+ return true;
+ }
+
+ bool ok = true;
+ std::vector<std::string> files;
+ if (GetSplitFileList(file_path, &files)) {
+ for (const auto& file : files) {
+ ok &= android::base::RemoveFileIfExists(file, message);
+ }
+ }
+ ok &= android::base::RemoveFileIfExists(file_path, message);
+ return ok;
+}
+
+bool SplitFiemap::HasPinnedExtents() const {
+ for (const auto& file : files_) {
+ if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
+ if (extents_.empty()) {
+ for (const auto& file : files_) {
+ const auto& extents = file->extents();
+ extents_.insert(extents_.end(), extents.begin(), extents.end());
+ }
+ }
+ return extents_;
+}
+
+bool SplitFiemap::Write(const void* data, uint64_t bytes) {
+ // Open the current file.
+ FiemapWriter* file = files_[cursor_index_].get();
+
+ const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
+ uint64_t bytes_remaining = bytes;
+ while (bytes_remaining) {
+ // How many bytes can we write into the current file?
+ uint64_t file_bytes_left = file->size() - cursor_file_pos_;
+ if (!file_bytes_left) {
+ if (cursor_index_ == files_.size() - 1) {
+ LOG(ERROR) << "write past end of file requested";
+ return false;
+ }
+
+ // No space left in the current file, but we have more files to
+ // use, so prep the next one.
+ cursor_fd_ = {};
+ cursor_file_pos_ = 0;
+ file = files_[++cursor_index_].get();
+ file_bytes_left = file->size();
+ }
+
+ // Open the current file if it's not open.
+ if (cursor_fd_ < 0) {
+ cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
+ if (cursor_fd_ < 0) {
+ PLOG(ERROR) << "open failed: " << file->file_path();
+ return false;
+ }
+ CHECK(cursor_file_pos_ == 0);
+ }
+
+ if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+ LOG(ERROR) << "file is no longer pinned: " << file->file_path();
+ return false;
+ }
+
+ uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
+ if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
+ PLOG(ERROR) << "write failed: " << file->file_path();
+ return false;
+ }
+ data_ptr += bytes_to_write;
+ bytes_remaining -= bytes_to_write;
+ cursor_file_pos_ += bytes_to_write;
+ }
+
+ // If we've reached the end of the current file, close it for sanity.
+ if (cursor_file_pos_ == file->size()) {
+ cursor_fd_ = {};
+ }
+ return true;
+}
+
+bool SplitFiemap::Flush() {
+ for (const auto& file : files_) {
+ unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << file->file_path();
+ return false;
+ }
+ if (fsync(fd)) {
+ PLOG(ERROR) << "fsync failed: " << file->file_path();
+ return false;
+ }
+ }
+ return true;
+}
+
+SplitFiemap::~SplitFiemap() {
+ if (!creating_) {
+ return;
+ }
+
+ // We failed to finish creating, so unlink everything.
+ unlink(list_file_.c_str());
+ for (auto&& file : files_) {
+ std::string path = file->file_path();
+ file = nullptr;
+
+ unlink(path.c_str());
+ }
+}
+
+void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
+ total_size_ += file->size();
+ files_.emplace_back(std::move(file));
+}
+
+uint32_t SplitFiemap::block_size() const {
+ return files_[0]->block_size();
+}
+
+const std::string& SplitFiemap::bdev_path() const {
+ return files_[0]->bdev_path();
+}
+
+} // namespace fiemap_writer
+} // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.cpp b/fs_mgr/libfiemap_writer/utility.cpp
new file mode 100644
index 0000000..192ec16
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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 "utility.h"
+
+#include <stdint.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <libfiemap_writer/fiemap_writer.h>
+
+namespace android {
+namespace fiemap_writer {
+
+uint64_t DetermineMaximumFileSize(const std::string& file_path) {
+ // Create the smallest file possible (one block).
+ auto writer = FiemapWriter::Open(file_path, 1);
+ if (!writer) {
+ return 0;
+ }
+
+ uint64_t result = 0;
+ switch (writer->fs_type()) {
+ case EXT4_SUPER_MAGIC:
+ // The minimum is 16GiB, so just report that. If we wanted we could parse the
+ // superblock and figure out if 64-bit support is enabled.
+ result = 17179869184ULL;
+ break;
+ case F2FS_SUPER_MAGIC:
+ // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
+ // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
+ result = 4329690886144ULL;
+ break;
+ case MSDOS_SUPER_MAGIC:
+ // 4GB-1, which we want aligned to the block size.
+ result = 4294967295;
+ result -= (result % writer->block_size());
+ break;
+ default:
+ LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
+ break;
+ }
+
+ // Close and delete the temporary file.
+ writer = nullptr;
+ unlink(file_path.c_str());
+
+ return result;
+}
+
+} // namespace fiemap_writer
+} // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.h b/fs_mgr/libfiemap_writer/utility.h
new file mode 100644
index 0000000..2d418da
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace fiemap_writer {
+
+// Given a file that will be created, determine the maximum size its containing
+// filesystem allows. Note this is a theoretical maximum size; free space is
+// ignored entirely.
+uint64_t DetermineMaximumFileSize(const std::string& file_path);
+
+} // namespace fiemap_writer
+} // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 80fa5c4..f4e4d4e 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -28,6 +28,7 @@
#include "util.h"
using android::base::Basename;
+using android::base::ReadFileToString;
using android::base::StartsWith;
using android::base::unique_fd;
@@ -311,7 +312,8 @@
return footer;
}
-bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob) {
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length,
+ const std::string& expected_key_blob) {
if (expected_key_blob.empty()) { // no expectation of the key, return true.
return true;
}
@@ -324,6 +326,21 @@
return false;
}
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+ const std::vector<std::string>& allowed_key_paths) {
+ std::string allowed_key_blob;
+ if (key_blob_to_validate.empty()) {
+ LWARNING << "Failed to validate an empty key";
+ return false;
+ }
+ for (const auto& path : allowed_key_paths) {
+ if (ReadFileToString(path, &allowed_key_blob)) {
+ if (key_blob_to_validate == allowed_key_blob) return true;
+ }
+ }
+ return false;
+}
+
VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
const std::string& expected_public_key_blob,
std::string* out_public_key_data) {
@@ -347,7 +364,7 @@
<< ": Error verifying vbmeta image: failed to get public key";
return VBMetaVerifyResult::kError;
}
- if (!VerifyPublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
+ if (!ValidatePublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
<< " sign data does not match key in chain descriptor";
return VBMetaVerifyResult::kErrorVerification;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 5f413e3..09c786a 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -78,7 +78,10 @@
const std::string& expected_public_key_blob,
std::string* out_public_key_data);
-bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+ const std::vector<std::string>& expected_key_paths);
// Detects if whether a partition contains a rollback image.
bool RollbackDetected(const std::string& partition_name, uint64_t rollback_index);
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 02902f0..f0767dc 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <libavb/libavb.h>
#include <libdm/dm.h>
@@ -40,6 +41,7 @@
using android::base::Basename;
using android::base::ParseUint;
using android::base::ReadFileToString;
+using android::base::Split;
using android::base::StringPrintf;
namespace android {
@@ -264,8 +266,8 @@
}
AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
- if (fstab_entry.avb_key.empty()) {
- LERROR << "avb_key=/path/to/key is missing for " << fstab_entry.mount_point;
+ if (fstab_entry.avb_keys.empty()) {
+ LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
return nullptr;
}
@@ -273,24 +275,14 @@
bool allow_verification_error = IsDeviceUnlocked();
bool rollback_protection = !allow_verification_error;
- std::string expected_key_blob;
- if (!ReadFileToString(fstab_entry.avb_key, &expected_key_blob)) {
- if (!allow_verification_error) {
- LERROR << "Failed to load avb_key: " << fstab_entry.avb_key
- << " for mount point: " << fstab_entry.mount_point;
- return nullptr;
- }
- LWARNING << "Allowing no expected key blob when verification error is permitted";
- expected_key_blob.clear();
- }
-
+ std::string public_key_data;
bool verification_disabled = false;
VBMetaVerifyResult verify_result = VBMetaVerifyResult::kError;
std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
fstab_entry.blk_device, "" /* partition_name, no need for a standalone path */,
- expected_key_blob, allow_verification_error, rollback_protection,
- false /* not is_chained_vbmeta */, nullptr /* out_public_key_data */,
- &verification_disabled, &verify_result);
+ "" /* expected_public_key_blob, */, allow_verification_error, rollback_protection,
+ false /* not is_chained_vbmeta */, &public_key_data, &verification_disabled,
+ &verify_result);
if (!vbmeta) {
LERROR << "Failed to load vbmeta: " << fstab_entry.blk_device;
@@ -316,6 +308,15 @@
return nullptr;
}
+ if (!ValidatePublicKeyBlob(public_key_data, Split(fstab_entry.avb_keys, ":"))) {
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
+ LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
+ if (!allow_verification_error) {
+ LERROR << "Unknown public key is not allowed";
+ return nullptr;
+ }
+ }
+
if (verification_disabled) {
LINFO << "AVB verification disabled on: " << fstab_entry.mount_point;
avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index e4213b7..0d342d3 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -35,9 +35,9 @@
using android::fs_mgr::GetTotalSize;
using android::fs_mgr::LoadAndVerifyVbmetaByPartition;
using android::fs_mgr::LoadAndVerifyVbmetaByPath;
+using android::fs_mgr::ValidatePublicKeyBlob;
using android::fs_mgr::VBMetaData;
using android::fs_mgr::VBMetaVerifyResult;
-using android::fs_mgr::VerifyPublicKeyBlob;
using android::fs_mgr::VerifyVBMetaData;
using android::fs_mgr::VerifyVBMetaSignature;
@@ -415,7 +415,7 @@
EXPECT_EQ(content_padding.size() - padding.size(), vbmeta_padding.size());
}
-TEST_F(AvbUtilTest, VerifyPublicKeyBlob) {
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob) {
// Generates a raw key.bin
const size_t key_size = 2048;
base::FilePath key_path = GenerateImage("key.bin", key_size);
@@ -425,12 +425,12 @@
std::string expected_key_blob;
EXPECT_TRUE(base::ReadFileToString(key_path, &expected_key_blob));
- EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+ EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
key_data[10] ^= 0x80; // toggles a bit and expects a failure
- EXPECT_FALSE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+ EXPECT_FALSE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
key_data[10] ^= 0x80; // toggles the bit again, should pass
- EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+ EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
}
TEST_F(AvbUtilTest, VerifyEmptyPublicKeyBlob) {
@@ -442,7 +442,37 @@
EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
std::string expected_key_blob = ""; // empty means no expectation, thus return true.
- EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+ EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob_MultipleAllowedKeys) {
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ base::FilePath rsa8192_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+ std::vector<std::string> allowed_key_paths;
+ allowed_key_paths.push_back(rsa2048_public_key.value());
+ allowed_key_paths.push_back(rsa4096_public_key.value());
+
+ std::string expected_key_blob_2048;
+ EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+ std::string expected_key_blob_4096;
+ EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+ std::string expected_key_blob_8192;
+ EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+ EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_2048, allowed_key_paths));
+ EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_4096, allowed_key_paths));
+
+ EXPECT_FALSE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
+ EXPECT_FALSE(ValidatePublicKeyBlob("invalid_content", allowed_key_paths));
+ EXPECT_FALSE(ValidatePublicKeyBlob("", allowed_key_paths));
+
+ allowed_key_paths.push_back(rsa8192_public_key.value());
+ EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
}
TEST_F(AvbUtilTest, VerifyVBMetaSignature) {
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
index 4631330..c8605d7 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
+++ b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
@@ -114,8 +114,8 @@
// Use the 2nd fstab entry, which is for physical system_other partition.
FstabEntry* system_other_entry = &fstab[1];
// Assign the default key if it's not specified in the fstab.
- if (system_other_entry->avb_key.empty()) {
- system_other_entry->avb_key = "/system/etc/security/avb/system_other.avbpubkey";
+ if (system_other_entry->avb_keys.empty()) {
+ system_other_entry->avb_keys = "/system/etc/security/avb/system_other.avbpubkey";
}
auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(*system_other_entry);
EXPECT_NE(nullptr, avb_handle) << "Failed to load system_other vbmeta. Try 'adb root'?";
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index c39fbe7..27222af 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -550,11 +550,11 @@
}
bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
- uint64_t new_size) {
+ uint64_t new_size, bool force_check) {
PartitionGroup* group = FindGroup(partition->group_name());
CHECK(group);
- if (new_size <= old_size) {
+ if (!force_check && new_size <= old_size) {
return true;
}
@@ -591,11 +591,25 @@
free_regions = PrioritizeSecondHalfOfSuper(free_regions);
}
- // Find gaps that we can use for new extents. Note we store new extents in a
- // temporary vector, and only commit them if we are guaranteed enough free
- // space.
+ // Note we store new extents in a temporary vector, and only commit them
+ // if we are guaranteed enough free space.
std::vector<std::unique_ptr<LinearExtent>> new_extents;
+
+ // If the last extent in the partition has a size < alignment, then the
+ // difference is unallocatable due to being misaligned. We peek for that
+ // case here to avoid wasting space.
+ if (auto extent = ExtendFinalExtent(partition, free_regions, sectors_needed)) {
+ sectors_needed -= extent->num_sectors();
+ new_extents.emplace_back(std::move(extent));
+ }
+
for (auto& region : free_regions) {
+ // Note: this comes first, since we may enter the loop not needing any
+ // more sectors.
+ if (!sectors_needed) {
+ break;
+ }
+
if (region.length() % sectors_per_block != 0) {
// This should never happen, because it would imply that we
// once allocated an extent that was not a multiple of the
@@ -613,22 +627,10 @@
}
uint64_t sectors = std::min(sectors_needed, region.length());
- if (sectors < region.length()) {
- const auto& block_device = block_devices_[region.device_index];
- if (block_device.alignment) {
- const uint64_t alignment = block_device.alignment / LP_SECTOR_SIZE;
- sectors = AlignTo(sectors, alignment);
- sectors = std::min(sectors, region.length());
- }
- }
CHECK(sectors % sectors_per_block == 0);
auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
new_extents.push_back(std::move(extent));
- if (sectors >= sectors_needed) {
- sectors_needed = 0;
- break;
- }
sectors_needed -= sectors;
}
if (sectors_needed) {
@@ -677,6 +679,64 @@
return second_half;
}
+std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(
+ Partition* partition, const std::vector<Interval>& free_list,
+ uint64_t sectors_needed) const {
+ if (partition->extents().empty()) {
+ return nullptr;
+ }
+ LinearExtent* extent = partition->extents().back()->AsLinearExtent();
+ if (!extent) {
+ return nullptr;
+ }
+
+ // If the sector ends where the next aligned chunk begins, then there's
+ // no missing gap to try and allocate.
+ const auto& block_device = block_devices_[extent->device_index()];
+ uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+ if (extent->end_sector() == next_aligned_sector) {
+ return nullptr;
+ }
+
+ uint64_t num_sectors = std::min(next_aligned_sector - extent->end_sector(), sectors_needed);
+ auto new_extent = std::make_unique<LinearExtent>(num_sectors, extent->device_index(),
+ extent->end_sector());
+ if (IsAnyRegionAllocated(*new_extent.get()) ||
+ IsAnyRegionCovered(free_list, *new_extent.get())) {
+ LERROR << "Misaligned region " << new_extent->physical_sector() << ".."
+ << new_extent->end_sector() << " was allocated or marked allocatable.";
+ return nullptr;
+ }
+ return new_extent;
+}
+
+bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
+ const LinearExtent& candidate) const {
+ for (const auto& region : regions) {
+ if (region.device_index == candidate.device_index() &&
+ (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const {
+ for (const auto& partition : partitions_) {
+ for (const auto& extent : partition->extents()) {
+ LinearExtent* linear = extent->AsLinearExtent();
+ if (!linear || linear->device_index() != candidate.device_index()) {
+ continue;
+ }
+ if (linear->OwnsSector(candidate.physical_sector()) ||
+ linear->OwnsSector(candidate.end_sector() - 1)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
partition->ShrinkTo(aligned_size);
}
@@ -861,7 +921,7 @@
uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
uint64_t old_size = partition->size();
- if (!ValidatePartitionSizeChange(partition, old_size, aligned_size)) {
+ if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
return false;
}
@@ -973,7 +1033,12 @@
ImportExtents(partition, metadata, source);
- if (!ValidatePartitionSizeChange(partition, 0, partition->size())) {
+ // Note: we've already increased the partition size by calling
+ // ImportExtents(). In order to figure out the size before that,
+ // we would have to iterate the extents and add up the linear
+ // segments. Instead, we just force ValidatePartitionSizeChange
+ // to check if the current configuration is acceptable.
+ if (!ValidatePartitionSizeChange(partition, partition->size(), partition->size(), true)) {
partition->RemoveExtents();
return false;
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 69724f8..46bfe92 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -209,8 +209,8 @@
ASSERT_TRUE(builder->ResizePartition(a, a->size() + 4096));
ASSERT_TRUE(builder->ResizePartition(b, b->size() + 4096));
}
- EXPECT_EQ(a->size(), 7864320);
- EXPECT_EQ(b->size(), 7864320);
+ EXPECT_EQ(a->size(), 40960);
+ EXPECT_EQ(b->size(), 40960);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
@@ -218,7 +218,7 @@
// Check that each starting sector is aligned.
for (const auto& extent : exported->extents) {
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(extent.num_sectors, 1536);
+ EXPECT_EQ(extent.num_sectors, 80);
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
@@ -226,7 +226,7 @@
}
// Sanity check one extent.
- EXPECT_EQ(exported->extents.back().target_data, 30656);
+ EXPECT_EQ(exported->extents.back().target_data, 3008);
}
TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -698,7 +698,7 @@
EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
EXPECT_EQ(metadata->extents[1].target_data, 1472);
EXPECT_EQ(metadata->extents[1].target_source, 1);
- EXPECT_EQ(metadata->extents[2].num_sectors, 129600);
+ EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
EXPECT_EQ(metadata->extents[2].target_data, 1472);
EXPECT_EQ(metadata->extents[2].target_source, 2);
@@ -805,7 +805,7 @@
EXPECT_EQ(exported->extents[0].target_data, 10487808);
EXPECT_EQ(exported->extents[0].num_sectors, 10483712);
EXPECT_EQ(exported->extents[1].target_data, 6292992);
- EXPECT_EQ(exported->extents[1].num_sectors, 2099712);
+ EXPECT_EQ(exported->extents[1].num_sectors, 2099200);
EXPECT_EQ(exported->extents[2].target_data, 1536);
EXPECT_EQ(exported->extents[2].num_sectors, 6291456);
}
@@ -821,7 +821,7 @@
ASSERT_NE(vendor, nullptr);
ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment + 4096));
ASSERT_TRUE(builder->ResizePartition(vendor, device_info.alignment));
- ASSERT_EQ(system->size(), device_info.alignment * 2);
+ ASSERT_EQ(system->size(), device_info.alignment + 4096);
ASSERT_EQ(vendor->size(), device_info.alignment);
ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment * 2));
@@ -835,3 +835,73 @@
EXPECT_EQ(exported->extents[1].target_data, 4608);
EXPECT_EQ(exported->extents[1].num_sectors, 1536);
}
+
+TEST_F(BuilderTest, UpdateSuper) {
+ // Build the on-disk metadata that we saw before flashing.
+ auto builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+
+ Partition* partition = builder->AddPartition("system_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 3608576));
+
+ partition = builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 5510144));
+
+ partition = builder->AddPartition("product_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 2048));
+
+ partition = builder->AddPartition("system_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 7955456));
+
+ partition = builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 9857024));
+
+ partition = builder->AddPartition("product_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 11378688));
+
+ auto on_disk = builder->Export();
+ ASSERT_NE(on_disk, nullptr);
+
+ // Build the super_empty from the new build.
+ builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+ ASSERT_NE(builder->AddPartition("system_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("system_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("product_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("product_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+
+ std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
+ ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 53f480f..c706f2a 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -66,6 +66,10 @@
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
uint32_t device_index() const { return device_index_; }
+ bool OwnsSector(uint64_t sector) const {
+ return sector >= physical_sector_ && sector < end_sector();
+ }
+
private:
uint32_t device_index_;
uint64_t physical_sector_;
@@ -297,7 +301,8 @@
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
- bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size);
+ bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size,
+ bool force_check);
void ImportExtents(Partition* dest, const LpMetadata& metadata,
const LpMetadataPartition& source);
bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
@@ -321,9 +326,15 @@
}
};
std::vector<Interval> GetFreeRegions() const;
+ bool IsAnyRegionCovered(const std::vector<Interval>& regions,
+ const LinearExtent& candidate) const;
+ bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
void ExtentsToFreeList(const std::vector<Interval>& extents,
std::vector<Interval>* free_regions) const;
std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);
+ std::unique_ptr<LinearExtent> ExtendFinalExtent(Partition* partition,
+ const std::vector<Interval>& free_list,
+ uint64_t sectors_needed) const;
static bool sABOverrideValue;
static bool sABOverrideSet;
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index fd53ed4..ec6eb52 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -48,6 +48,7 @@
TMPDIR=${TMPDIR:-/tmp}
print_time=false
start_time=`date +%s`
+ACTIVE_SLOT=
##
## Helper Functions
@@ -70,7 +71,7 @@
Returns: true if device is in adb mode" ]
inAdb() {
adb devices |
- grep -v -e 'List of devices attached' -e '^$' |
+ grep -v -e 'List of devices attached' -e '^$' -e "[${SPACE}${TAB}]recovery\$" |
if [ -n "${ANDROID_SERIAL}" ]; then
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
else
@@ -78,11 +79,30 @@
fi
}
+[ "USAGE: inRecovery
+
+Returns: true if device is in recovery mode" ]
+inRecovery() {
+ local list="`adb devices |
+ grep -v -e 'List of devices attached' -e '^$'`"
+ if [ -n "${ANDROID_SERIAL}" ]; then
+ echo "${list}" |
+ grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null
+ return ${?}
+ fi
+ if echo "${list}" | wc -l | grep '^1$' >/dev/null; then
+ echo "${list}" |
+ grep "[${SPACE}${TAB}]recovery\$" >/dev/null
+ return ${?}
+ fi
+ false
+}
+
[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
Returns: true if the command succeeded" ]
adb_sh() {
- args=
+ local args=
for i in "${@}"; do
[ -z "${args}" ] || args="${args} "
if [ X"${i}" != X"${i#\'}" ]; then
@@ -143,10 +163,10 @@
Returns: content of file to stdout with carriage returns skipped,
true of the file exists" ]
adb_cat() {
- OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
- retval=${?}
+ local OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
+ local ret=${?}
echo "${OUTPUT}" | tr -d '\r'
- return ${retval}
+ return ${ret}
}
[ "USAGE: adb_reboot
@@ -165,7 +185,7 @@
echo unknown
return
fi
- duration="${1}"
+ local duration="${1}"
if [ X"${duration}" != X"${duration%s}" ]; then
duration=${duration%s}
elif [ X"${duration}" != X"${duration%m}" ]; then
@@ -175,9 +195,9 @@
elif [ X"${duration}" != X"${duration%d}" ]; then
duration=`expr ${duration%d} \* 86400`
fi
- seconds=`expr ${duration} % 60`
- minutes=`expr \( ${duration} / 60 \) % 60`
- hours=`expr ${duration} / 3600`
+ local seconds=`expr ${duration} % 60`
+ local minutes=`expr \( ${duration} / 60 \) % 60`
+ local hours=`expr ${duration} / 3600`
if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then
if [ 1 -eq ${duration} ]; then
echo 1 second
@@ -203,25 +223,36 @@
Returns: waits until the device has returned for adb or optional timeout" ]
adb_wait() {
+ local ret
if [ -n "${1}" ]; then
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
- timeout --preserve-status --signal=KILL ${1} adb wait-for-device
- retval=${?}
+ timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
+ ret=${?}
echo -n " ${CR}"
- return ${retval}
else
adb wait-for-device
+ ret=${?}
fi
+ if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ fi
+ fi
+ return ${ret}
}
[ "USAGE: usb_status > stdout
-If adb_wait failed, check if device is in fastboot mode and report status
+If adb_wait failed, check if device is in adb, recovery or fastboot mode
+and report status string.
Returns: \"(USB stack borken?)\", \"(In fastboot mode)\" or \"(in adb mode)\"" ]
usb_status() {
if inFastboot; then
echo "(In fastboot mode)"
+ elif inRecovery; then
+ echo "(In recovery mode)"
elif inAdb; then
echo "(In adb mode)"
else
@@ -233,18 +264,67 @@
Returns: waits until the device has returned for fastboot or optional timeout" ]
fastboot_wait() {
+ local ret
# fastboot has no wait-for-device, but it does an automatic
# wait and requires (even a nonsensical) command to do so.
if [ -n "${1}" ]; then
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
- retval=${?}
+ ret=${?}
echo -n " ${CR}"
- ( exit ${retval} )
+ ( exit ${ret} )
else
fastboot wait-for-device >/dev/null 2>/dev/null
fi ||
inFastboot
+ ret=${?}
+ if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ fi
+ fi
+ return ${ret}
+}
+
+[ "USAGE: recovery_wait [timeout]
+
+Returns: waits until the device has returned for recovery or optional timeout" ]
+recovery_wait() {
+ local ret
+ if [ -n "${1}" ]; then
+ echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
+ timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
+ ret=${?}
+ echo -n " ${CR}"
+ else
+ adb wait-for-recovery
+ ret=${?}
+ fi
+ if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ fi
+ fi
+ return ${ret}
+}
+
+[ "any_wait [timeout]
+
+Returns: waits until a device has returned or optional timeout" ]
+any_wait() {
+ (
+ adb_wait ${1} &
+ adb_pid=${!}
+ fastboot_wait ${1} &
+ fastboot_pid=${!}
+ recovery_wait ${1} &
+ recovery_pid=${!}
+ wait -n
+ kill "${adb_pid}" "${fastboot_pid}" "${recovery_pid}"
+ ) >/dev/null 2>/dev/null
+ inFastboot || inAdb || inRecovery
}
[ "USAGE: adb_root
@@ -273,15 +353,15 @@
[ root != "`adb_sh echo '${USER}' </dev/null`" ]
}
-[ "USAGE: fastboot_getvar var expected
+[ "USAGE: fastboot_getvar var expected >/dev/stderr
Returns: true if var output matches expected" ]
fastboot_getvar() {
- O=`fastboot getvar ${1} 2>&1`
- err=${?}
+ local O=`fastboot getvar ${1} 2>&1`
+ local ret=${?}
O="${O#< waiting for * >?}"
O="${O%%?Finished. Total time: *}"
- if [ 0 -ne ${err} ]; then
+ if [ 0 -ne ${ret} ]; then
echo ${O} >&2
false
return
@@ -297,6 +377,19 @@
echo ${O} >&2
}
+[ "USAGE: get_active_slot >/dev/stdout
+
+Returns: with a or b string reporting active slot" ]
+get_active_slot() {
+ if inAdb || inRecovery; then
+ get_property ro.boot.slot_suffix | tr -d _
+ elif inFastboot; then
+ fastboot_getvar current-slot 2>&1 | sed -n 's/current-slot: //p'
+ else
+ false
+ fi
+}
+
[ "USAGE: restore
Do nothing: should be redefined when necessary. Called after cleanup.
@@ -325,7 +418,7 @@
echo "${BLUE}[ INFO ]${NORMAL} end `date`"
[ -n "${start_time}" ] || return
end_time=`date +%s`
- diff_time=`expr ${end_time} - ${start_time}`
+ local diff_time=`expr ${end_time} - ${start_time}`
echo "${BLUE}[ INFO ]${NORMAL} duration `format_duration ${diff_time}`"
fi >&2
}
@@ -358,8 +451,8 @@
Returns true if (regex) lval matches rval" ]
EXPECT_EQ() {
- lval="${1}"
- rval="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
@@ -404,10 +497,10 @@
Exits if (regex) lval mismatches rval" ]
check_eq() {
- left="${1}"
- right="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
- EXPECT_EQ "${left}" "${right}" ||
+ EXPECT_EQ "${lval}" "${rval}" ||
die "${@}"
}
@@ -498,15 +591,16 @@
fi
inFastboot && die "device in fastboot mode"
+inRecovery && die "device in recovery mode"
if ! inAdb; then
- echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode"
+ echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" >&2
adb_wait 2m
fi
inAdb || die "specified device not in adb mode"
isDebuggable || die "device not a debug build"
enforcing=true
if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
- echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode"
+ echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
enforcing=false
fi
@@ -529,6 +623,9 @@
BUILD_DESCRIPTION=`get_property ro.build.description`
[ -z "${BUILD_DESCRIPTION}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+ACTIVE_SLOT=`get_active_slot`
+[ -z "${ACTIVE_SLOT}" ] ||
+ echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
# Report existing partition sizes
adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
@@ -563,6 +660,19 @@
adb_reboot &&
adb_wait 2m
}
+
+ echo "${GREEN}[ RUN ]${NORMAL} Testing adb shell su root remount -R command" >&2
+
+ adb_su remount -R system </dev/null || true
+ sleep 2
+ adb_wait 2m ||
+ die "waiting for device after remount -R `usb_status`"
+ if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+ "2" = "`get_property partition.system.verified`" ]; then
+ die "remount -R command failed"
+ fi
+
+ echo "${GREEN}[ OK ]${NORMAL} adb shell su root remount -R command" >&2
fi
echo "${GREEN}[ RUN ]${NORMAL} Testing kernel support for overlayfs" >&2
@@ -577,7 +687,7 @@
) ||
overlayfs_supported=false
if ${overlayfs_supported}; then
- adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
+ adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null 2>/dev/null &&
echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
case `adb_sh uname -r </dev/null` in
4.[456789].* | 4.[1-9][0-9]* | [56789].*)
@@ -841,7 +951,7 @@
adb_reboot &&
adb_wait 2m ||
- die "reboot after override content added failed"
+ die "reboot after override content added failed `usb_status`"
if ${overlayfs_needed}; then
D=`adb_su df -k </dev/null` &&
@@ -857,21 +967,20 @@
echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
fi
-B="`adb_cat /system/hello`" ||
- die "re-read /system/hello after reboot"
-check_eq "${A}" "${B}" /system after reboot
-echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2
-# Only root can read vendor if sepolicy permissions are as expected.
if ${enforcing}; then
- adb_unroot
- B="`adb_cat /vendor/hello`" &&
- die "re-read /vendor/hello after reboot w/o root"
+ adb_unroot ||
+ die "device not in unroot'd state"
+ B="`adb_cat /vendor/hello 2>&1`"
check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
echo "${GREEN}[ OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
fi
-adb_root &&
- B="`adb_cat /vendor/hello`" ||
- die "re-read /vendor/hello after reboot"
+B="`adb_cat /system/hello`"
+check_eq "${A}" "${B}" /system after reboot
+echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2
+# Only root can read vendor if sepolicy permissions are as expected.
+adb_root ||
+ die "adb root"
+B="`adb_cat /vendor/hello`"
check_eq "${A}" "${B}" vendor after reboot
echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2
@@ -901,8 +1010,9 @@
else
adb reboot-fastboot ||
die "fastbootd not supported (wrong adb in path?)"
- fastboot_wait 2m ||
- die "reboot into fastboot to flash vendor `usb_status`"
+ any_wait 2m &&
+ inFastboot ||
+ die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
fastboot flash vendor ||
( fastboot reboot && false) ||
die "fastboot flash vendor"
@@ -956,26 +1066,27 @@
if ${is_userspace_fastboot}; then
die "overlay supposed to be minus /vendor takeover after flash vendor"
else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
- ( die "overlay supposed to be minus /vendor takeover after flash vendor" )
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
fi
fi
- B="`adb_cat /system/hello`" ||
- die "re-read /system/hello after flash vendor"
+ B="`adb_cat /system/hello`"
check_eq "${A}" "${B}" system after flash vendor
adb_root ||
die "adb root"
- B="`adb_cat /vendor/hello`" &&
- if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
- die "re-read /vendor/hello after flash vendor"
- else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
- ( die "re-read /vendor/hello after flash vendor" )
- fi
+ B="`adb_cat /vendor/hello`"
if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
- check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+ check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+ vendor content after flash vendor
else
- ( check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor )
+ (
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ restore() {
+ true
+ }
+ check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+ vendor content after flash vendor
+ )
fi
fi
@@ -1003,12 +1114,10 @@
adb_sh rm /system/hello </dev/null ||
( [ -n "${L}" ] && echo "${L}" && false ) ||
die -t ${T} "cleanup hello"
-B="`adb_cat /system/hello`" &&
- die "re-read /system/hello after rm"
-check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
-B="`adb_cat /vendor/hello`" &&
- die "re-read /vendor/hello after rm"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
+B="`adb_cat /system/hello`"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /vendor/hello`"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
if [ -n "${scratch_partition}" ]; then
@@ -1081,9 +1190,60 @@
die "/vendor is not read-write"
echo "${GREEN}[ OK ]${NORMAL} mount -o rw,remount command works" >&2
+# Prerequisite is a prepped device from above.
+adb_reboot &&
+ adb_wait 2m ||
+ die "lost device after reboot to ro state (USB stack broken?)"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+ die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+ die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/vendor is not read-only"
+echo "${GREEN}[ OK ]${NORMAL} remount command works from setup" >&2
+
+# Prerequisite is an overlayfs deconstructed device but with verity disabled.
+# This also saves a lot of 'noise' from the command doing a mkfs on backing
+# storage and all the related tuning and adjustment.
+for d in ${OVERLAYFS_BACKING}; do
+ adb_su rm -rf /${d}/overlay </dev/null ||
+ die "/${d}/overlay wipe"
+done
+adb_reboot &&
+ adb_wait 2m ||
+ die "lost device after reboot after wipe (USB stack broken?)"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+ die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+ die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/system is not read-only"
+echo "${GREEN}[ OK ]${NORMAL} remount command works from scratch" >&2
+
restore
err=${?}
+if [ ${err} = 0 ] && ${overlayfs_supported}; then
+ echo "${GREEN}[ RUN ]${NORMAL} test 'adb remount -R'" >&2
+ adb_root &&
+ adb remount -R &&
+ adb_wait 2m ||
+ die "adb remount -R"
+ if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+ "2" = "`get_property partition.system.verified`" ]; then
+ die "remount -R command failed to disable verity"
+ fi
+
+ echo "${GREEN}[ OK ]${NORMAL} 'adb remount -R' command" >&2
+
+ restore
+ err=${?}
+fi
+
restore() {
true
}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6afc8d2..72afa69 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -948,13 +948,14 @@
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
source none0 swap defaults avb=vbmeta_partition
+source none1 swap defaults avb_keys=/path/to/test.avbpubkey
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_EQ(1U, fstab.size());
+ ASSERT_EQ(2U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
@@ -964,6 +965,12 @@
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
EXPECT_EQ("vbmeta_partition", entry->vbmeta_partition);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ FstabEntry::FsMgrFlags empty_flags = {}; // no flags should be set for avb_keys.
+ EXPECT_TRUE(CompareFlags(empty_flags, entry->fs_mgr_flags));
+ EXPECT_EQ("/path/to/test.avbpubkey", entry->avb_keys);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_KeyDirectory) {
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 8e9c7ea..2b7db79 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -31,6 +31,7 @@
shared_libs: [
"libbinder",
"libgatekeeper",
+ "libgsi",
"liblog",
"libhardware",
"libbase",
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 446b66e..8700c34 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -26,6 +26,8 @@
#include <memory>
#include <android/security/keystore/IKeystoreService.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -34,6 +36,7 @@
#include <hardware/hw_auth_token.h>
#include <keystore/keystore.h> // For error code
#include <keystore/keystore_return_types.h>
+#include <libgsi/libgsi.h>
#include <log/log.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -59,6 +62,7 @@
GateKeeperProxy() {
clear_state_if_needed_done = false;
hw_device = IGatekeeper::getService();
+ is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
if (hw_device == nullptr) {
ALOGW("falling back to software GateKeeper");
@@ -86,7 +90,7 @@
return;
}
- if (mark_cold_boot()) {
+ if (mark_cold_boot() && !is_running_gsi) {
ALOGI("cold boot: clearing state");
if (hw_device != nullptr) {
hw_device->deleteAllUsers([](const GatekeeperResponse &){});
@@ -138,6 +142,18 @@
}
}
+ // This should only be called on uids being passed to the GateKeeper HAL. It ensures that
+ // secure storage shared across a GSI image and a host image will not overlap.
+ uint32_t adjust_uid(uint32_t uid) {
+ static constexpr uint32_t kGsiOffset = 1000000;
+ CHECK(uid < kGsiOffset);
+ CHECK(hw_device != nullptr);
+ if (is_running_gsi) {
+ return uid + kGsiOffset;
+ }
+ return uid;
+ }
+
virtual int enroll(uint32_t uid,
const uint8_t *current_password_handle, uint32_t current_password_handle_length,
const uint8_t *current_password, uint32_t current_password_length,
@@ -181,7 +197,8 @@
newPwd.setToExternal(const_cast<uint8_t*>(desired_password),
desired_password_length);
- Return<void> hwRes = hw_device->enroll(uid, curPwdHandle, curPwd, newPwd,
+ uint32_t hw_uid = adjust_uid(uid);
+ Return<void> hwRes = hw_device->enroll(hw_uid, curPwdHandle, curPwd, newPwd,
[&ret, enrolled_password_handle, enrolled_password_handle_length]
(const GatekeeperResponse &rsp) {
ret = static_cast<int>(rsp.code); // propagate errors
@@ -266,13 +283,14 @@
// handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
// a HAL if there was none before
if (handle->version == 0 || handle->hardware_backed) {
+ uint32_t hw_uid = adjust_uid(uid);
android::hardware::hidl_vec<uint8_t> curPwdHandle;
curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolled_password_handle),
enrolled_password_handle_length);
android::hardware::hidl_vec<uint8_t> enteredPwd;
enteredPwd.setToExternal(const_cast<uint8_t*>(provided_password),
provided_password_length);
- Return<void> hwRes = hw_device->verify(uid, challenge, curPwdHandle, enteredPwd,
+ Return<void> hwRes = hw_device->verify(hw_uid, challenge, curPwdHandle, enteredPwd,
[&ret, request_reenroll, auth_token, auth_token_length]
(const GatekeeperResponse &rsp) {
ret = static_cast<int>(rsp.code); // propagate errors
@@ -354,7 +372,8 @@
clear_sid(uid);
if (hw_device != nullptr) {
- hw_device->deleteUser(uid, [] (const GatekeeperResponse &){});
+ uint32_t hw_uid = adjust_uid(uid);
+ hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
}
}
@@ -394,6 +413,7 @@
std::unique_ptr<SoftGateKeeperDevice> soft_device;
bool clear_state_if_needed_done;
+ bool is_running_gsi;
};
}// namespace android
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 6b00f81..2cf6be9 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -84,3 +84,29 @@
"manifest_healthd.xml"
],
}
+
+cc_library_static {
+ name: "libhealthd_charger_nops",
+
+ srcs: [
+ "healthd_mode_charger_nops.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "libhealthd_headers",
+ ],
+
+ static_libs: [
+ "android.hardware.health@2.0-impl",
+ ],
+
+ shared_libs: [
+ "android.hardware.health@2.0",
+ "libutils",
+ ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 823ed06..d18f15a 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,14 +2,14 @@
LOCAL_PATH := $(call my-dir)
+### libhealthd_draw ###
include $(CLEAR_VARS)
LOCAL_MODULE := libhealthd_draw
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-LOCAL_STATIC_LIBRARIES := \
- libminui \
- libbase
+LOCAL_STATIC_LIBRARIES := libminui
+LOCAL_SHARED_LIBRARIES := libbase
LOCAL_SRC_FILES := healthd_draw.cpp
ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
@@ -28,6 +28,7 @@
include $(BUILD_STATIC_LIBRARY)
+### libhealthd_charger ###
include $(CLEAR_VARS)
LOCAL_CFLAGS := -Werror
@@ -49,24 +50,22 @@
$(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := \
- android.hardware.health@2.0 \
android.hardware.health@2.0-impl \
- android.hardware.health@1.0 \
android.hardware.health@1.0-convert \
libhealthstoragedefault \
+ libhealthd_draw \
libminui \
- libpng \
- libz \
- libutils \
+
+LOCAL_SHARED_LIBRARIES := \
+ android.hardware.health@2.0 \
libbase \
libcutils \
- libhealthd_draw \
liblog \
- libm \
- libc \
+ libpng \
+ libutils \
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
+LOCAL_SHARED_LIBRARIES += libsuspend
endif
include $(BUILD_STATIC_LIBRARY)
@@ -81,10 +80,6 @@
charger.cpp \
LOCAL_MODULE := charger
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Werror
@@ -94,8 +89,6 @@
CHARGER_STATIC_LIBRARIES := \
android.hardware.health@2.0-impl \
- android.hardware.health@2.0 \
- android.hardware.health@1.0 \
android.hardware.health@1.0-convert \
libbinderthreadstate \
libhidltransport \
@@ -104,54 +97,97 @@
libhealthstoragedefault \
libvndksupport \
libhealthd_charger \
+ libhealthd_charger_nops \
libhealthd_draw \
libbatterymonitor \
+
+CHARGER_SHARED_LIBRARIES := \
+ android.hardware.health@2.0 \
libbase \
- libutils \
libcutils \
libjsoncpp \
libprocessgroup \
liblog \
- libm \
- libc \
-
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+ libutils \
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_STATIC_LIBRARIES += \
- libminui \
- libpng \
- libz \
-
+CHARGER_STATIC_LIBRARIES += libminui
+CHARGER_SHARED_LIBRARIES += libpng
endif
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
+CHARGER_SHARED_LIBRARIES += libsuspend
endif
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
+
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-# Symlink /charger to /sbin/charger
+# Symlink /charger to /system/bin/charger
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
- && ln -sf /sbin/charger $(TARGET_ROOT_OUT)/charger
+ && ln -sf /system/bin/charger $(TARGET_ROOT_OUT)/charger
include $(BUILD_EXECUTABLE)
+### charger.recovery ###
include $(CLEAR_VARS)
-LOCAL_MODULE := charger_test
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+
LOCAL_SRC_FILES := \
charger.cpp \
+
+LOCAL_MODULE := charger.recovery
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_MODULE_STEM := charger
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+
+# charger.recovery doesn't link against libhealthd_{charger,draw} or libminui, since it doesn't need
+# any UI support.
+LOCAL_STATIC_LIBRARIES := \
+ android.hardware.health@2.0-impl \
+ android.hardware.health@1.0-convert \
+ libbinderthreadstate \
+ libhidltransport \
+ libhidlbase \
+ libhwbinder_noltopgo \
+ libhealthstoragedefault \
+ libvndksupport \
+ libhealthd_charger_nops \
+ libbatterymonitor \
+
+# These shared libs will be installed to recovery image because of the dependency in `recovery`
+# module.
+LOCAL_SHARED_LIBRARIES := \
+ android.hardware.health@2.0 \
+ libbase \
+ libcutils \
+ liblog \
+ libutils \
+
+# The use of LOCAL_HAL_STATIC_LIBRARIES prevents from building this module with Android.bp.
+LOCAL_HAL_STATIC_LIBRARIES := libhealthd
+
+include $(BUILD_EXECUTABLE)
+
+### charger_test ###
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_test
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror -DCHARGER_NO_UI
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
+LOCAL_SRC_FILES := \
charger_test.cpp \
include $(BUILD_EXECUTABLE)
CHARGER_STATIC_LIBRARIES :=
+CHARGER_SHARED_LIBRARIES :=
+### charger_res_images ###
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
include $$(CLEAR_VARS)
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 43e7fd5..085cceb 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -14,98 +14,13 @@
* limitations under the License.
*/
-#define LOG_TAG "charger"
-#define KLOG_LEVEL 6
+#include "healthd_mode_charger.h"
+#include "healthd_mode_charger_nops.h"
-#include <health2/Health.h>
-#include <healthd/healthd.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <cutils/klog.h>
-
-using namespace android;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// Charger mode
-
-extern void healthd_mode_charger_init(struct healthd_config *config);
-extern int healthd_mode_charger_preparetowait(void);
-extern void healthd_mode_charger_heartbeat(void);
-extern void healthd_mode_charger_battery_update(
- struct android::BatteryProperties *props);
-
-// NOPs for modes that need no special action
-
-static void healthd_mode_nop_init(struct healthd_config *config);
-static int healthd_mode_nop_preparetowait(void);
-static void healthd_mode_nop_heartbeat(void);
-static void healthd_mode_nop_battery_update(
- struct android::BatteryProperties *props);
-
-static struct healthd_mode_ops healthd_nops = {
- .init = healthd_mode_nop_init,
- .preparetowait = healthd_mode_nop_preparetowait,
- .heartbeat = healthd_mode_nop_heartbeat,
- .battery_update = healthd_mode_nop_battery_update,
-};
-
-#ifdef CHARGER_NO_UI
-static struct healthd_mode_ops charger_ops = healthd_nops;
-#else
-static struct healthd_mode_ops charger_ops = {
- .init = healthd_mode_charger_init,
- .preparetowait = healthd_mode_charger_preparetowait,
- .heartbeat = healthd_mode_charger_heartbeat,
- .battery_update = healthd_mode_charger_battery_update,
-};
-#endif
-
-static void healthd_mode_nop_init(struct healthd_config* config) {
- using android::hardware::health::V2_0::implementation::Health;
- Health::initInstance(config);
-}
-
-static int healthd_mode_nop_preparetowait(void) {
- return -1;
-}
-
-static void healthd_mode_nop_heartbeat(void) {
-}
-
-static void healthd_mode_nop_battery_update(
- struct android::BatteryProperties* /*props*/) {
-}
-
-int healthd_charger_main(int argc, char** argv) {
- int ch;
-
- healthd_mode_ops = &charger_ops;
-
- while ((ch = getopt(argc, argv, "cr")) != -1) {
- switch (ch) {
- case 'c':
- // -c is now a noop
- break;
- case 'r':
- // force nops for recovery
- healthd_mode_ops = &healthd_nops;
- break;
- case '?':
- default:
- KLOG_ERROR(LOG_TAG, "Unrecognized charger option: %c\n",
- optopt);
- exit(1);
- }
- }
-
- return healthd_main();
-}
-
-#ifndef CHARGER_TEST
int main(int argc, char** argv) {
+#ifdef CHARGER_NO_UI
+ return healthd_charger_nops(argc, argv);
+#else
return healthd_charger_main(argc, argv);
-}
#endif
+}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 8f2f727..5fe58ac 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -54,6 +54,9 @@
using namespace android;
+// main healthd loop
+extern int healthd_main(void);
+
char* locale;
#ifndef max
@@ -711,3 +714,33 @@
healthd_config = config;
charger->boot_min_cap = config->boot_min_cap;
}
+
+static struct healthd_mode_ops charger_ops = {
+ .init = healthd_mode_charger_init,
+ .preparetowait = healthd_mode_charger_preparetowait,
+ .heartbeat = healthd_mode_charger_heartbeat,
+ .battery_update = healthd_mode_charger_battery_update,
+};
+
+int healthd_charger_main(int argc, char** argv) {
+ int ch;
+
+ healthd_mode_ops = &charger_ops;
+
+ while ((ch = getopt(argc, argv, "cr")) != -1) {
+ switch (ch) {
+ case 'c':
+ // -c is now a noop
+ break;
+ case 'r':
+ // -r is now a noop
+ break;
+ case '?':
+ default:
+ LOGE("Unrecognized charger option: %c\n", optopt);
+ exit(1);
+ }
+ }
+
+ return healthd_main();
+}
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
new file mode 100644
index 0000000..2f0c9f2
--- /dev/null
+++ b/healthd/healthd_mode_charger.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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
+
+int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
new file mode 100644
index 0000000..bcc04d5
--- /dev/null
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 "healthd_mode_charger_nops.h"
+
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+using namespace android;
+
+// main healthd loop
+extern int healthd_main(void);
+
+// NOPs for modes that need no special action
+
+static void healthd_mode_nop_init(struct healthd_config* config);
+static int healthd_mode_nop_preparetowait(void);
+static void healthd_mode_nop_heartbeat(void);
+static void healthd_mode_nop_battery_update(struct android::BatteryProperties* props);
+
+static struct healthd_mode_ops healthd_nops = {
+ .init = healthd_mode_nop_init,
+ .preparetowait = healthd_mode_nop_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_nop_battery_update,
+};
+
+static void healthd_mode_nop_init(struct healthd_config* config) {
+ using android::hardware::health::V2_0::implementation::Health;
+ Health::initInstance(config);
+}
+
+static int healthd_mode_nop_preparetowait(void) {
+ return -1;
+}
+
+static void healthd_mode_nop_heartbeat(void) {}
+
+static void healthd_mode_nop_battery_update(struct android::BatteryProperties* /*props*/) {}
+
+int healthd_charger_nops(int /* argc */, char** /* argv */) {
+ healthd_mode_ops = &healthd_nops;
+ return healthd_main();
+}
diff --git a/healthd/healthd_mode_charger_nops.h b/healthd/healthd_mode_charger_nops.h
new file mode 100644
index 0000000..a37b247
--- /dev/null
+++ b/healthd/healthd_mode_charger_nops.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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
+
+int healthd_charger_nops(int argc, char** argv);
diff --git a/init/Android.bp b/init/Android.bp
index 639d8d1..8292aa0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -91,7 +91,7 @@
cc_library_static {
name: "libinit",
recovery_available: true,
- defaults: ["init_defaults"],
+ defaults: ["init_defaults", "selinux_policy_version"],
srcs: [
"action.cpp",
"action_manager.cpp",
@@ -110,6 +110,7 @@
"init.cpp",
"keychords.cpp",
"modalias_handler.cpp",
+ "mount_handler.cpp",
"mount_namespace.cpp",
"parser.cpp",
"persistent_properties.cpp",
@@ -132,7 +133,7 @@
"ueventd_parser.cpp",
"util.cpp",
],
- whole_static_libs: ["libcap"],
+ whole_static_libs: ["libcap", "com.android.sysprop.apex"],
header_libs: ["bootimg_headers"],
proto: {
type: "lite",
diff --git a/init/Android.mk b/init/Android.mk
index 59d7f11..c63760c 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -2,6 +2,8 @@
LOCAL_PATH:= $(call my-dir)
+include system/sepolicy/policy_version.mk
+
# --
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
@@ -28,7 +30,8 @@
-DSHUTDOWN_ZERO_TIMEOUT=0
endif
-init_options += -DLOG_UEVENTS=0
+init_options += -DLOG_UEVENTS=0 \
+ -DSEPOLICY_VERSION=$(POLICYVERS)
init_cflags += \
$(init_options) \
@@ -63,8 +66,9 @@
LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
# Set up the same mount points on the ramdisk that system-as-root contains.
-LOCAL_POST_INSTALL_CMD := \
- mkdir -p $(TARGET_RAMDISK_OUT)/dev \
+LOCAL_POST_INSTALL_CMD := mkdir -p \
+ $(TARGET_RAMDISK_OUT)/apex \
+ $(TARGET_RAMDISK_OUT)/dev \
$(TARGET_RAMDISK_OUT)/mnt \
$(TARGET_RAMDISK_OUT)/proc \
$(TARGET_RAMDISK_OUT)/sys \
@@ -93,6 +97,7 @@
libselinux \
libcap \
libgsi \
+ libcom.android.sysprop.apex \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/README.md b/init/README.md
index f0e5d55..b2039b4 100644
--- a/init/README.md
+++ b/init/README.md
@@ -660,12 +660,19 @@
Properties
----------
-Init provides information about the services that it is responsible
-for via the below properties.
+Init provides state information with the following properties.
`init.svc.<name>`
> State of a named service ("stopped", "stopping", "running", "restarting")
+`dev.mnt.blk.<mount_point>`
+> Block device base name associated with a *mount_point*.
+ The *mount_point* has / replaced by . and if referencing the root mount point
+ "/", it will use "/root", specifically `dev.mnt.blk.root`.
+ Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
+ `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
+ characteristics in a device agnostic manner.
+
Boot timing
-----------
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 538ed00..8437e37 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1118,11 +1118,11 @@
}
}
-static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
+static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) {
if (SwitchToDefaultMountNamespace()) {
return Success();
} else {
- return Error() << "Failed to setup runtime bionic";
+ return Error() << "Failed to enter into default mount namespace";
}
}
@@ -1173,10 +1173,10 @@
{"rmdir", {1, 1, {true, do_rmdir}}},
{"setprop", {2, 2, {true, do_setprop}}},
{"setrlimit", {3, 3, {false, do_setrlimit}}},
- {"setup_runtime_bionic", {0, 0, {false, do_setup_runtime_bionic}}},
{"start", {1, 1, {false, do_start}}},
{"stop", {1, 1, {false, do_stop}}},
{"swapon_all", {1, 1, {false, do_swapon_all}}},
+ {"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},
{"trigger", {1, 1, {false, do_trigger}}},
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 4bca09e..94dd553 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -16,6 +16,7 @@
#include "epoll.h"
+#include <stdint.h>
#include <sys/epoll.h>
#include <chrono>
@@ -37,13 +38,16 @@
return Success();
}
-Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler) {
+Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
+ if (!events) {
+ return Error() << "Must specify events";
+ }
auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
if (!inserted) {
return Error() << "Cannot specify two epoll handlers for a given FD";
}
epoll_event ev;
- ev.events = EPOLLIN;
+ ev.events = events;
// std::map's iterators do not get invalidated until erased, so we use the
// pointer to the std::function in the map directly for epoll_ctl.
ev.data.ptr = reinterpret_cast<void*>(&it->second);
diff --git a/init/epoll.h b/init/epoll.h
index 85a791c..9789bef 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -17,6 +17,9 @@
#ifndef _INIT_EPOLL_H
#define _INIT_EPOLL_H
+#include <stdint.h>
+#include <sys/epoll.h>
+
#include <chrono>
#include <functional>
#include <map>
@@ -34,7 +37,8 @@
Epoll();
Result<Success> Open();
- Result<Success> RegisterHandler(int fd, std::function<void()> handler);
+ Result<Success> RegisterHandler(int fd, std::function<void()> handler,
+ uint32_t events = EPOLLIN);
Result<Success> UnregisterHandler(int fd);
Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index e11d897..c566676 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -155,6 +155,10 @@
// part of the product partition, e.g. because they are mounted read-write.
CHECKCALL(mkdir("/mnt/product", 0755));
+ // /apex is used to mount APEXes
+ CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+
#undef CHECKCALL
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
@@ -195,6 +199,12 @@
SwitchRoot("/first_stage_ramdisk");
}
+ // If this file 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) {
+ setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+ }
+
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 6e55c11a..4b0f05d 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -580,7 +580,14 @@
required_devices_partition_names_.emplace(basename(device.c_str()));
auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
uevent_listener_.RegenerateUevents(uevent_callback);
- uevent_listener_.Poll(uevent_callback, 10s);
+ if (!required_devices_partition_names_.empty()) {
+ uevent_listener_.Poll(uevent_callback, 10s);
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__
+ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ }
+ }
} else {
InitMappedDevice(device);
}
@@ -760,7 +767,7 @@
if (!InitAvbHandle()) return false;
hashtree_result =
avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
- } else if (!fstab_entry->avb_key.empty()) {
+ } else if (!fstab_entry->avb_keys.empty()) {
if (!InitAvbHandle()) return false;
// Checks if hashtree should be disabled from the top-level /vbmeta.
if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
diff --git a/init/init.cpp b/init/init.cpp
index bbef1a9..a5f4549 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -39,6 +39,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
+#include <fs_avb/fs_avb.h>
#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
#include <libavb/libavb.h>
@@ -56,6 +57,7 @@
#include "first_stage_mount.h"
#include "import_parser.h"
#include "keychords.h"
+#include "mount_handler.h"
#include "mount_namespace.h"
#include "property_service.h"
#include "reboot.h"
@@ -74,6 +76,7 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::fs_mgr::AvbHandle;
namespace android {
namespace init {
@@ -92,6 +95,7 @@
static bool shutting_down;
static std::string shutdown_command;
static bool do_shutdown = false;
+static bool load_debug_prop = false;
std::vector<std::string> late_import_paths;
@@ -655,10 +659,17 @@
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);
+ // See if need to load debug props to allow adb root, when the device is unlocked.
+ const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+ if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
+ load_debug_prop = "true"s == force_debuggable_env;
+ }
+
// Clean up our environment.
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
+ unsetenv("INIT_FORCE_DEBUGGABLE");
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
@@ -672,10 +683,11 @@
InstallSignalFdHandler(&epoll);
- property_load_boot_defaults();
+ property_load_boot_defaults(load_debug_prop);
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
StartPropertyService(&epoll);
+ MountHandler mount_handler(&epoll);
set_usb_controller();
const BuiltinFunctionMap function_map;
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
new file mode 100644
index 0000000..12dfc6d
--- /dev/null
+++ b/init/mount_handler.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 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 "mount_handler.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+
+#include "epoll.h"
+#include "property_service.h"
+
+namespace android {
+namespace init {
+
+namespace {
+
+MountHandlerEntry ParseMount(const std::string& line) {
+ auto fields = android::base::Split(line, " ");
+ while (fields.size() < 3) fields.emplace_back("");
+ if (fields[0] == "/dev/root") {
+ if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
+ if (auto entry = GetEntryForMountPoint(&fstab, "/")) {
+ fields[0] = entry->blk_device;
+ }
+ }
+ }
+ if (android::base::StartsWith(fields[0], "/dev/")) {
+ if (std::string link; android::base::Readlink(fields[0], &link)) {
+ fields[0] = link;
+ }
+ }
+ return MountHandlerEntry(fields[0], fields[1], fields[2]);
+}
+
+void SetMountProperty(const MountHandlerEntry& entry, bool add) {
+ static constexpr char devblock[] = "/dev/block/";
+ if (!android::base::StartsWith(entry.blk_device, devblock)) return;
+ std::string value;
+ if (add) {
+ value = entry.blk_device.substr(strlen(devblock));
+ if (android::base::StartsWith(value, "sd")) {
+ // All sd partitions inherit their queue characteristics
+ // from the whole device reference. Strip partition number.
+ auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
+ if (it != value.end()) value.erase(it, value.end());
+ }
+ auto queue = "/sys/block/" + value + "/queue";
+ struct stat sb;
+ if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+ if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+ // Skip the noise associated with APEX until there is a need
+ if (android::base::StartsWith(value, "loop")) value = "";
+ }
+ std::string property =
+ "dev.mnt.blk" + ((entry.mount_point == "/") ? "/root" : entry.mount_point);
+ std::replace(property.begin(), property.end(), '/', '.');
+ if (value.empty() && android::base::GetProperty(property, "").empty()) return;
+ property_set(property, value);
+}
+
+} // namespace
+
+MountHandlerEntry::MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+ const std::string& fs_type)
+ : blk_device(blk_device), mount_point(mount_point), fs_type(fs_type) {}
+
+bool MountHandlerEntry::operator<(const MountHandlerEntry& r) const {
+ if (blk_device < r.blk_device) return true;
+ if (blk_device > r.blk_device) return false;
+ if (mount_point < r.mount_point) return true;
+ if (mount_point > r.mount_point) return false;
+ return fs_type < r.fs_type;
+}
+
+MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) {
+ if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";
+ auto result = epoll->RegisterHandler(
+ fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);
+ if (!result) LOG(FATAL) << result.error();
+}
+
+MountHandler::~MountHandler() {
+ if (fp_) epoll_->UnregisterHandler(fileno(fp_.get())).IgnoreError();
+}
+
+void MountHandler::MountHandlerFunction() {
+ rewind(fp_.get());
+ char* buf = nullptr;
+ size_t len = 0;
+ auto untouched = mounts_;
+ while (getline(&buf, &len, fp_.get()) != -1) {
+ auto entry = ParseMount(std::string(buf, len));
+ auto match = untouched.find(entry);
+ if (match == untouched.end()) {
+ SetMountProperty(entry, true);
+ mounts_.emplace(std::move(entry));
+ } else {
+ untouched.erase(match);
+ }
+ }
+ free(buf);
+ for (auto entry : untouched) {
+ auto match = mounts_.find(entry);
+ if (match == mounts_.end()) continue;
+ mounts_.erase(match);
+ SetMountProperty(entry, false);
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/mount_handler.h b/init/mount_handler.h
new file mode 100644
index 0000000..e524a74
--- /dev/null
+++ b/init/mount_handler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 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 <stdio.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "epoll.h"
+
+namespace android {
+namespace init {
+
+struct MountHandlerEntry {
+ MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+ const std::string& fs_type);
+
+ bool operator<(const MountHandlerEntry& r) const;
+
+ const std::string blk_device;
+ const std::string mount_point;
+ const std::string fs_type;
+};
+
+class MountHandler {
+ public:
+ explicit MountHandler(Epoll* epoll);
+ MountHandler(const MountHandler&) = delete;
+ MountHandler(MountHandler&&) = delete;
+ MountHandler& operator=(const MountHandler&) = delete;
+ MountHandler& operator=(MountHandler&&) = delete;
+ ~MountHandler();
+
+ private:
+ void MountHandlerFunction();
+
+ Epoll* epoll_;
+ std::unique_ptr<FILE, decltype(&fclose)> fp_;
+ std::set<MountHandlerEntry> mounts_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 413fe8f..5305dc7 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -21,6 +21,7 @@
#include <string>
#include <vector>
+#include <ApexProperties.sysprop.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -32,37 +33,6 @@
namespace init {
namespace {
-static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker";
-static constexpr const char* kBootstrapLinkerPath = "/system/bin/bootstrap/linker";
-static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
-
-static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/";
-static constexpr const char* kBootstrapBionicLibsDir = "/system/lib/bootstrap/";
-static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
-
-static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64";
-static constexpr const char* kBootstrapLinkerPath64 = "/system/bin/bootstrap/linker64";
-static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
-
-static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/";
-static constexpr const char* kBootstrapBionicLibsDir64 = "/system/lib64/bootstrap/";
-static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
-
-static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
-
-static bool BindMount(const std::string& source, const std::string& mount_point,
- bool recursive = false) {
- unsigned long mountflags = MS_BIND;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point;
- return false;
- }
- return true;
-}
-
static bool MakeShared(const std::string& mount_point, bool recursive = false) {
unsigned long mountflags = MS_SHARED;
if (recursive) {
@@ -104,37 +74,9 @@
return ret;
}
-static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source,
- const std::string& linker_mount_point,
- const std::string& lib_mount_dir) {
- if (access(linker_source.c_str(), F_OK) != 0) {
- PLOG(INFO) << linker_source << " does not exist. skipping mounting bionic there.";
- // This can happen for 64-bit bionic in 32-bit only device.
- // It is okay to skip mounting the 64-bit bionic.
- return true;
- }
- if (!BindMount(linker_source, linker_mount_point)) {
- return false;
- }
- if (!MakePrivate(linker_mount_point)) {
- return false;
- }
- for (const auto& libname : kBionicLibFileNames) {
- std::string mount_point = lib_mount_dir + libname;
- std::string source = lib_dir_source + libname;
- if (!BindMount(source, mount_point)) {
- return false;
- }
- if (!MakePrivate(mount_point)) {
- return false;
- }
- }
- return true;
-}
-
-static bool IsBionicUpdatable() {
- static bool result = android::base::GetBoolProperty("ro.apex.IsBionicUpdatable", false);
- return result;
+static bool IsApexUpdatable() {
+ static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+ return updatable;
}
static android::base::unique_fd bootstrap_ns_fd;
@@ -153,36 +95,21 @@
// point to private.
if (!MakeShared("/", true /*recursive*/)) return false;
- // Since different files (bootstrap or runtime APEX) should be mounted to
- // the same mount point paths (e.g. /bionic/bin/linker, /bionic/lib/libc.so,
- // etc.) across the two mount namespaces, we create a private mount point at
- // /bionic so that a mount event for the bootstrap bionic in the mount
- // namespace for pre-apexd processes is not propagated to the other mount
- // namespace for post-apexd process, and vice versa.
- //
- // Other mount points other than /bionic, however, are all still shared.
- if (!BindMount("/bionic", "/bionic", true /*recursive*/)) return false;
- if (!MakePrivate("/bionic")) return false;
-
- // Bind-mount bootstrap bionic.
- if (!BindMountBionic(kBootstrapLinkerPath, kBootstrapBionicLibsDir, kLinkerMountPoint,
- kBionicLibsMountPointDir))
- return false;
- if (!BindMountBionic(kBootstrapLinkerPath64, kBootstrapBionicLibsDir64, kLinkerMountPoint64,
- kBionicLibsMountPointDir64))
- return false;
+ // /apex is a private mountpoint to give different sets of APEXes for
+ // the bootstrap and default mount namespaces. The processes running with
+ // the bootstrap namespace get APEXes from the read-only partition.
+ if (!(MakePrivate("/apex"))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
- // When bionic is updatable via the runtime APEX, we create separate mount
+ // When APEXes are updatable (e.g. not-flattened), we create separate mount
// namespaces for processes that are started before and after the APEX is
- // activated by apexd. In the namespace for pre-apexd processes, the bionic
- // from the /system partition (that we call bootstrap bionic) is
- // bind-mounted. In the namespace for post-apexd processes, the bionic from
- // the runtime APEX is bind-mounted.
+ // activated by apexd. In the namespace for pre-apexd processes, small
+ // 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 (IsBionicUpdatable() && !IsRecoveryMode()) {
+ if (IsApexUpdatable() && !IsRecoveryMode()) {
// Creating a new namespace by cloning, saving, and switching back to
// the original namespace.
if (unshare(CLONE_NEWNS) == -1) {
@@ -192,15 +119,6 @@
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
- // By this unmount, the bootstrap bionic are not mounted in the default
- // mount namespace.
- if (umount2("/bionic", MNT_DETACH) == -1) {
- PLOG(ERROR) << "Cannot unmount /bionic";
- // Don't return here. We have to switch back to the bootstrap
- // namespace.
- success = false;
- }
-
if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
return false;
@@ -227,17 +145,6 @@
}
}
- // Bind-mount bionic from the runtime APEX since it is now available. Note
- // that in case of IsBionicUpdatable() == false, these mounts are over the
- // existing existing bind mounts for the bootstrap bionic, which effectively
- // becomes hidden.
- if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint,
- kBionicLibsMountPointDir))
- return false;
- if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64,
- kBionicLibsMountPointDir64))
- return false;
-
LOG(INFO) << "Switched to default mount namespace";
return true;
}
@@ -248,7 +155,7 @@
return true;
}
if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
- IsBionicUpdatable()) {
+ IsApexUpdatable()) {
if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
return false;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 4bc857a..fc5538c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -866,7 +866,7 @@
}
}
-void property_load_boot_defaults() {
+void property_load_boot_defaults(bool load_debug_prop) {
// TODO(b/117892318): merge prop.default and build.prop files into one
// 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
@@ -888,6 +888,12 @@
load_properties_from_file("/product_services/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
+ if (load_debug_prop) {
+ constexpr static const char kAdbDebugProp[] = "/system/etc/adb_debug.prop";
+ LOG(INFO) << "Loading " << kAdbDebugProp;
+ load_properties_from_file(kAdbDebugProp, nullptr, &properties);
+ }
+
for (const auto& [name, value] : properties) {
std::string error;
if (PropertySet(name, value, &error) != PROP_SUCCESS) {
diff --git a/init/property_service.h b/init/property_service.h
index 9022f5a..85e7bc0 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -36,7 +36,7 @@
extern bool PropertyChildReap(pid_t pid);
void property_init(void);
-void property_load_boot_defaults(void);
+void property_load_boot_defaults(bool);
void load_persist_props(void);
void load_system_props(void);
void StartPropertyService(Epoll* epoll);
diff --git a/init/selinux.cpp b/init/selinux.cpp
index ee302c1..aa66baa 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -61,14 +61,18 @@
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
#include <cutils/android_reboot.h>
+#include <fs_avb/fs_avb.h>
#include <selinux/android.h>
#include "reboot_utils.h"
#include "util.h"
+using namespace std::string_literals;
+
using android::base::ParseInt;
using android::base::Timer;
using android::base::unique_fd;
+using android::fs_mgr::AvbHandle;
namespace android {
namespace init {
@@ -267,6 +271,8 @@
}
constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+constexpr const char userdebug_plat_policy_cil_file[] =
+ "/system/etc/selinux/userdebug_plat_sepolicy.cil";
bool IsSplitPolicyDevice() {
return access(plat_policy_cil_file, R_OK) != -1;
@@ -282,10 +288,21 @@
// 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(userdebug_plat_policy_cil_file, F_OK) == 0);
+ if (use_userdebug_policy) {
+ LOG(WARNING) << "Using userdebug system sepolicy";
+ }
+
// Load precompiled policy from vendor image, if a matching policy is found there. The policy
// must match the platform policy on the system image.
std::string precompiled_sepolicy_file;
- if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+ // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
+ // Thus it cannot use the precompiled policy from vendor image.
+ if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
if (fd != -1) {
if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
@@ -299,14 +316,6 @@
LOG(INFO) << "Compiling SELinux policy";
- // Determine the highest policy language version supported by the kernel
- set_selinuxmnt("/sys/fs/selinux");
- int max_policy_version = security_policyvers();
- if (max_policy_version == -1) {
- PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
- return false;
- }
-
// We store the output of the compilation on /dev because this is the most convenient tmpfs
// storage mount available this early in the boot sequence.
char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
@@ -353,14 +362,13 @@
if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
odm_policy_cil_file.clear();
}
- const std::string version_as_string = std::to_string(max_policy_version);
+ const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
// clang-format off
std::vector<const char*> compile_args {
"/system/bin/secilc",
- plat_policy_cil_file,
+ use_userdebug_policy ? userdebug_plat_policy_cil_file : plat_policy_cil_file,
"-m", "-M", "true", "-G", "-N",
- // Target the highest policy language version supported by the kernel
"-c", version_as_string.c_str(),
plat_mapping_file.c_str(),
"-o", compiled_sepolicy,
@@ -459,6 +467,8 @@
selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
selinux_android_restorecon("/dev/device-mapper", 0);
+
+ selinux_android_restorecon("/apex", 0);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index 2186a85..cba42c4 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1059,7 +1059,7 @@
std::string oom_str = std::to_string(oom_score_adjust_);
std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
if (!WriteStringToFile(oom_str, oom_file)) {
- PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+ PLOG(ERROR) << "couldn't write oom_score_adj";
}
}
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 0fd2a3a..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,7 +27,6 @@
name: "libasyncio",
defaults: ["libasyncio_defaults"],
vendor_available: true,
- double_loadable: true,
recovery_available: true,
host_supported: true,
srcs: [
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 9d15af2..4518891 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -55,7 +55,7 @@
}
// Iterate through the maps and fill in the backtrace_map_t structure.
- for (auto* map_info : *stack_maps_) {
+ for (const auto& map_info : *stack_maps_) {
backtrace_map_t map;
map.start = map_info->start;
map.end = map_info->end;
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index f1dcd50..a3df380 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -219,8 +219,6 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 10f5bc0..f6cae36 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -23,6 +23,9 @@
extern "C" {
#endif
+#define NATIVE_HANDLE_MAX_FDS 1024
+#define NATIVE_HANDLE_MAX_INTS 1024
+
/* Declare a char array for use with native_handle_init */
#define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
alignas(native_handle_t) char (name)[ \
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
index 66f7a3d..b409e5b 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -22,9 +22,6 @@
#include <string.h>
#include <unistd.h>
-static const int kMaxNativeFds = 1024;
-static const int kMaxNativeInts = 1024;
-
native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
if ((uintptr_t) storage % alignof(native_handle_t)) {
errno = EINVAL;
@@ -39,7 +36,8 @@
}
native_handle_t* native_handle_create(int numFds, int numInts) {
- if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+ if (numFds < 0 || numInts < 0 || numFds > NATIVE_HANDLE_MAX_FDS ||
+ numInts > NATIVE_HANDLE_MAX_INTS) {
errno = EINVAL;
return NULL;
}
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index e816926..dda491a 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -19,13 +19,14 @@
cc_binary {
name: "mini-keyctl",
- srcs: ["mini_keyctl.cpp"],
-
+ srcs: [
+ "mini_keyctl.cpp",
+ "mini_keyctl_utils.cpp"
+ ],
shared_libs: [
"libbase",
"libkeyutils",
"liblog",
],
-
- cflags: ["-Werror", "-Wall", "-Wextra"],
+ cflags: ["-Werror", "-Wall", "-Wextra", "-fexceptions"],
}
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
index abc8f82..e09c864 100644
--- a/libkeyutils/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl.cpp
@@ -18,159 +18,82 @@
* A tool loads keys to keyring.
*/
-#include <dirent.h>
-#include <errno.h>
-#include <sys/types.h>
+#include "mini_keyctl_utils.h"
+
+#include <error.h>
+#include <stdio.h>
#include <unistd.h>
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <keyutils.h>
-
-static constexpr int kMaxCertSize = 4096;
-
-// Add all the certs from directory path to keyring with keyring_id. Returns the number of keys
-// added.
-int AddKeys(const std::string& path, const key_serial_t keyring_id, const std::string& keyring_desc,
- int start_index) {
- std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(path.c_str()), closedir);
- if (!dir) {
- PLOG(WARNING) << "Failed to open directory " << path;
- return 0;
- }
- int keys_added = 0;
- struct dirent* dp;
- while ((dp = readdir(dir.get())) != NULL) {
- if (dp->d_type != DT_REG) {
- continue;
- }
- std::string cert_path = path + "/" + dp->d_name;
- std::string cert_buf;
- if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
- LOG(ERROR) << "Failed to read " << cert_path;
- continue;
- }
-
- if (cert_buf.size() > kMaxCertSize) {
- LOG(ERROR) << "Certficate size too large: " << cert_path;
- continue;
- }
-
- // Add key to keyring.
- int key_desc_index = keys_added + start_index;
- std::string key_desc = keyring_desc + "-key" + std::to_string(key_desc_index);
- key_serial_t key =
- add_key("asymmetric", key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
- if (key < 0) {
- PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
- continue;
- }
- keys_added++;
- }
- return keys_added;
-}
-
-std::vector<std::string> SplitBySpace(const std::string& s) {
- std::istringstream iss(s);
- return std::vector<std::string>{std::istream_iterator<std::string>{iss},
- std::istream_iterator<std::string>{}};
-}
-
-// Find the keyring id. Because request_key(2) syscall is not available or the key is
-// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
-// information in the descritption section depending on the key type, only the first word in the
-// keyring description is used for searching.
-bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
- if (!keyring_id) {
- LOG(ERROR) << "keyring_id is null";
- return false;
- }
-
- // Only keys allowed by SELinux rules will be shown here.
- std::ifstream proc_keys_file("/proc/keys");
- if (!proc_keys_file.is_open()) {
- PLOG(ERROR) << "Failed to open /proc/keys";
- return false;
- }
-
- std::string line;
- while (getline(proc_keys_file, line)) {
- std::vector<std::string> tokens = SplitBySpace(line);
- if (tokens.size() < 9) {
- continue;
- }
- std::string key_id = tokens[0];
- std::string key_type = tokens[7];
- // The key description may contain space.
- std::string key_desc_prefix = tokens[8];
- // The prefix has a ":" at the end
- std::string key_desc_pattern = keyring_desc + ":";
- if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
- continue;
- }
- *keyring_id = std::stoi(key_id, nullptr, 16);
- return true;
- }
- return false;
-}
+#include <android-base/parseint.h>
static void Usage(int exit_code) {
- fprintf(stderr, "usage: mini-keyctl -c PATHS -s DESCRIPTION\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "-c, --cert_dirs the certificate locations, separated by comma\n");
- fprintf(stderr, "-k, --keyring the keyring description\n");
+ fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
+ fprintf(stderr, " mini-keyctl add <type> <desc> <data> <keyring>\n");
+ fprintf(stderr, " mini-keyctl padd <type> <desc> <keyring>\n");
+ fprintf(stderr, " mini-keyctl dadd <type> <desc_prefix> <cert_dir> <keyring>\n");
+ fprintf(stderr, " mini-keyctl unlink <key> <keyring>\n");
+ fprintf(stderr, " mini-keyctl restrict_keyring <keyring>\n");
+ fprintf(stderr, " mini-keyctl security <key>\n");
_exit(exit_code);
}
-int main(int argc, char** argv) {
- if (argc < 5) Usage(1);
+static key_serial_t parseKeyOrDie(const char* str) {
+ key_serial_t key;
+ if (!android::base::ParseInt(str, &key)) {
+ error(1 /* exit code */, 0 /* errno */, "Unparsable key: '%s'\n", str);
+ }
+ return key;
+}
- std::string arg_cert_dirs;
- std::string arg_keyring_desc;
+int main(int argc, const char** argv) {
+ if (argc < 2) Usage(1);
+ const std::string action = argv[1];
- for (int i = 1; i < argc; i++) {
- std::string option = argv[i];
- if (option == "-c" || option == "--cert_dirs") {
- if (i + 1 < argc) arg_cert_dirs = argv[++i];
- } else if (option == "-k" || option == "--keyring") {
- if (i + 1 < argc) arg_keyring_desc = argv[++i];
+ if (action == "add") {
+ if (argc != 6) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string data = argv[4];
+ std::string keyring = argv[5];
+ return Add(type, desc, data, keyring);
+ } else if (action == "dadd") {
+ if (argc != 6) Usage(1);
+ std::string type = argv[2];
+ // The key description contains desc_prefix and an index.
+ std::string desc_prefix = argv[3];
+ std::string cert_dir = argv[4];
+ std::string keyring = argv[5];
+ return AddCertsFromDir(type, desc_prefix, cert_dir, keyring);
+ } else if (action == "padd") {
+ if (argc != 5) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string keyring = argv[4];
+ return Padd(type, desc, keyring);
+ } else if (action == "restrict_keyring") {
+ if (argc != 3) Usage(1);
+ std::string keyring = argv[2];
+ return RestrictKeyring(keyring);
+ } else if (action == "unlink") {
+ if (argc != 4) Usage(1);
+ key_serial_t key = parseKeyOrDie(argv[2]);
+ const std::string keyring = argv[3];
+ return Unlink(key, keyring);
+ } else if (action == "security") {
+ if (argc != 3) Usage(1);
+ const char* key_str = argv[2];
+ key_serial_t key = parseKeyOrDie(key_str);
+ std::string context = RetrieveSecurityContext(key);
+ if (context.empty()) {
+ perror(key_str);
+ return 1;
}
- }
-
- if (arg_cert_dirs.empty() || arg_keyring_desc.empty()) {
- LOG(ERROR) << "Missing cert_dirs or keyring desc";
+ fprintf(stderr, "%s\n", context.c_str());
+ return 0;
+ } else {
+ fprintf(stderr, "Unrecognized action: %s\n", action.c_str());
Usage(1);
}
- // Get the keyring id
- key_serial_t key_ring_id;
- if (!GetKeyringId(arg_keyring_desc, &key_ring_id)) {
- PLOG(ERROR) << "Can't find keyring with " << arg_keyring_desc;
- return 1;
- }
-
- std::vector<std::string> cert_dirs = android::base::Split(arg_cert_dirs, ",");
- int start_index = 0;
- for (const auto& cert_dir : cert_dirs) {
- int keys_added = AddKeys(cert_dir, key_ring_id, arg_keyring_desc, start_index);
- start_index += keys_added;
- }
-
- // Prevent new keys to be added.
- if (!android::base::GetBoolProperty("ro.debuggable", false) &&
- keyctl_restrict_keyring(key_ring_id, nullptr, nullptr) < 0) {
- PLOG(ERROR) << "Failed to restrict key ring " << arg_keyring_desc;
- return 1;
- }
-
return 0;
}
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..9fe2dfe
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2019 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 <mini_keyctl_utils.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#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 <keyutils.h>
+
+static constexpr int kMaxCertSize = 4096;
+
+static std::vector<std::string> SplitBySpace(const std::string& s) {
+ std::istringstream iss(s);
+ return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+ std::istream_iterator<std::string>{}};
+}
+
+// Find the keyring id. Because request_key(2) syscall is not available or the key is
+// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
+// information in the descritption section depending on the key type, only the first word in the
+// keyring description is used for searching.
+static bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
+ if (!keyring_id) {
+ LOG(ERROR) << "keyring_id is null";
+ return false;
+ }
+
+ // If the keyring id is already a hex number, directly convert it to keyring id
+ if (android::base::ParseInt(keyring_desc.c_str(), keyring_id)) {
+ return true;
+ }
+
+ // Only keys allowed by SELinux rules will be shown here.
+ std::ifstream proc_keys_file("/proc/keys");
+ if (!proc_keys_file.is_open()) {
+ PLOG(ERROR) << "Failed to open /proc/keys";
+ return false;
+ }
+
+ std::string line;
+ while (getline(proc_keys_file, line)) {
+ std::vector<std::string> tokens = SplitBySpace(line);
+ if (tokens.size() < 9) {
+ continue;
+ }
+ std::string key_id = tokens[0];
+ std::string key_type = tokens[7];
+ // The key description may contain space.
+ std::string key_desc_prefix = tokens[8];
+ // The prefix has a ":" at the end
+ std::string key_desc_pattern = keyring_desc + ":";
+ if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+ continue;
+ }
+ *keyring_id = std::stoi(key_id, nullptr, 16);
+ return true;
+ }
+ return false;
+}
+
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+ const std::string& cert_dir, const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can not find keyring id";
+ return 1;
+ }
+
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(cert_dir.c_str()), closedir);
+ if (!dir) {
+ PLOG(WARNING) << "Failed to open directory " << cert_dir;
+ return 1;
+ }
+ int keys_added = 0;
+ struct dirent* dp;
+ while ((dp = readdir(dir.get())) != NULL) {
+ if (dp->d_type != DT_REG) {
+ continue;
+ }
+ std::string cert_path = cert_dir + "/" + dp->d_name;
+ std::string cert_buf;
+ if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
+ LOG(ERROR) << "Failed to read " << cert_path;
+ continue;
+ }
+
+ if (cert_buf.size() > kMaxCertSize) {
+ LOG(ERROR) << "Certficate size too large: " << cert_path;
+ continue;
+ }
+
+ // Add key to keyring.
+ int key_desc_index = keys_added;
+ std::string key_desc = desc_prefix + std::to_string(key_desc_index);
+ key_serial_t key =
+ add_key(type.c_str(), key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
+ if (key < 0) {
+ PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
+ continue;
+ }
+ LOG(INFO) << "Key " << cert_path << " added to " << keyring << " with key id 0x" << std::hex
+ << key;
+ keys_added++;
+ }
+ return 0;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can't find keyring " << keyring;
+ return 1;
+ }
+
+ if (keyctl_unlink(key, keyring_id) < 0) {
+ PLOG(ERROR) << "Failed to unlink key 0x" << std::hex << key << " from keyring " << keyring_id;
+ return 1;
+ }
+ return 0;
+}
+
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+ const std::string& keyring) {
+ if (data.size() > kMaxCertSize) {
+ LOG(ERROR) << "Certificate too large";
+ return 1;
+ }
+
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can not find keyring id";
+ return 1;
+ }
+
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ PLOG(ERROR) << "Failed to add key";
+ return 1;
+ }
+
+ LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+ return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can not find keyring id";
+ return 1;
+ }
+
+ // read from stdin to get the certificates
+ std::istreambuf_iterator<char> begin(std::cin), end;
+ std::string data(begin, end);
+
+ if (data.size() > kMaxCertSize) {
+ LOG(ERROR) << "Certificate too large";
+ return 1;
+ }
+
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ PLOG(ERROR) << "Failed to add key";
+ return 1;
+ }
+
+ LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+ return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Cannot find keyring id";
+ return 1;
+ }
+
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ PLOG(ERROR) << "Cannot restrict keyring " << keyring;
+ return 1;
+ }
+ return 0;
+}
+
+std::string RetrieveSecurityContext(key_serial_t key) {
+ // Simply assume this size is enough in practice.
+ const int kMaxSupportedSize = 256;
+ std::string context;
+ context.resize(kMaxSupportedSize);
+ long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
+ if (retval < 0) {
+ PLOG(ERROR) << "Cannot get security context of key 0x" << std::hex << key;
+ return std::string();
+ }
+ if (retval > kMaxSupportedSize) {
+ LOG(ERROR) << "The key has unexpectedly long security context than " << kMaxSupportedSize;
+ return std::string();
+ }
+ context.resize(retval);
+ return context;
+}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
new file mode 100644
index 0000000..804a357
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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 "include/keyutils.h"
+
+#include <string>
+
+// Add all files in a directory as certificates to a keyring. |keyring| could be the keyring
+// description or keyring id in hex.
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+ const std::string& cert_dir, const std::string& keyring);
+
+// Add key to a keyring. Returns non-zero if error happens.
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+ const std::string& keyring);
+
+// Add key from stdin to a keyring. Returns non-zero if error happens.
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring);
+
+// Removes the link from a keyring to a key if exists. Return non-zero if error happens.
+int Unlink(key_serial_t key, const std::string& keyring);
+
+// Apply key-linking to a keyring. Return non-zero if error happens.
+int RestrictKeyring(const std::string& keyring);
+
+// Retrieves a key's security context. Return the context string, or empty string on error.
+std::string RetrieveSecurityContext(key_serial_t key);
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 908fe7f..7fa3f43 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -413,6 +413,8 @@
if (!tag) tag = "";
/* XXX: This needs to go! */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstring-plus-int"
if (bufID != LOG_ID_RADIO) {
switch (tag[0]) {
case 'H':
@@ -454,6 +456,7 @@
break;
}
}
+#pragma clang diagnostic pop
#if __BIONIC__
if (prio == ANDROID_LOG_FATAL) {
diff --git a/libmeminfo/tools/librank.cpp b/libmeminfo/tools/librank.cpp
index 2c2583d..e53c746 100644
--- a/libmeminfo/tools/librank.cpp
+++ b/libmeminfo/tools/librank.cpp
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <algorithm>
+#include <map>
#include <memory>
#include <vector>
@@ -122,30 +123,22 @@
const std::string& name() const { return name_; }
const MemUsage& usage() const { return usage_; }
- const std::vector<ProcessRecord>& processes() const { return procs_; }
+ const std::map<pid_t, ProcessRecord>& processes() const { return procs_; }
uint64_t pss() const { return usage_.pss; }
void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
- auto process = std::find_if(procs_.begin(), procs_.end(),
- [&](auto p) -> bool { return p.pid() == proc.pid(); });
- if (process == procs_.end()) {
- process = procs_.emplace(procs_.end(), proc.pid());
- }
- process->AddUsage(mem_usage);
+ auto [it, inserted] = procs_.insert(std::pair<pid_t, ProcessRecord>(proc.pid(), proc));
+ it->second.AddUsage(mem_usage);
add_mem_usage(&usage_, mem_usage);
}
- void Sort(std::function<bool(const ProcessRecord&, const ProcessRecord&)>& sorter) {
- std::sort(procs_.begin(), procs_.end(), sorter);
- }
-
private:
std::string name_;
MemUsage usage_;
- std::vector<ProcessRecord> procs_;
+ std::map<pid_t, ProcessRecord> procs_;
};
// List of every library / map
-static std::vector<LibRecord> g_libs;
+static std::map<std::string, LibRecord> g_libs;
// List of library/map names that we don't want to show by default
static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
@@ -204,13 +197,10 @@
continue;
}
- auto lib = std::find_if(g_libs.begin(), g_libs.end(),
- [&](auto l) -> bool { return map.name == l.name(); });
- if (lib == g_libs.end()) {
- lib = g_libs.emplace(g_libs.end(), map.name);
- }
+ auto [it, inserted] =
+ g_libs.insert(std::pair<std::string, LibRecord>(map.name, LibRecord(map.name)));
+ it->second.AddUsage(proc, map.usage);
- lib->AddUsage(proc, map.usage);
if (!g_has_swap && map.usage.swap) {
g_has_swap = true;
}
@@ -321,11 +311,16 @@
}
printf("Name/PID\n");
+ std::vector<LibRecord> v_libs;
+ v_libs.reserve(g_libs.size());
+ std::transform(g_libs.begin(), g_libs.end(), std::back_inserter(v_libs),
+ [] (std::pair<std::string, LibRecord> const& pair) { return pair.second; });
+
// sort the libraries by their pss
- std::sort(g_libs.begin(), g_libs.end(),
+ std::sort(v_libs.begin(), v_libs.end(),
[](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
- for (auto& lib : g_libs) {
+ for (auto& lib : v_libs) {
printf("%6" PRIu64 "K %7s %6s %6s %6s ", lib.pss() / 1024, "", "", "", "");
if (g_has_swap) {
printf(" %6s ", "");
@@ -333,9 +328,15 @@
printf("%s\n", lib.name().c_str());
// sort all mappings first
- lib.Sort(sort_func);
- for (auto& p : lib.processes()) {
+ std::vector<ProcessRecord> procs;
+ procs.reserve(lib.processes().size());
+ std::transform(lib.processes().begin(), lib.processes().end(), std::back_inserter(procs),
+ [] (std::pair<pid_t, ProcessRecord> const& pair) { return pair.second; });
+
+ std::sort(procs.begin(), procs.end(), sort_func);
+
+ for (auto& p : procs) {
const MemUsage& usage = p.usage();
printf(" %6s %7" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ", "",
usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 320b851..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -6,7 +6,6 @@
vndk: {
enabled: true,
},
- double_loadable: true,
srcs: ["memtrack.cpp"],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 260f655..51fb875 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -36,14 +36,9 @@
__attribute__((visibility("default")))
void InitializeNativeLoader();
-__attribute__((visibility("default")))
-jstring CreateClassLoaderNamespace(JNIEnv* env,
- int32_t target_sdk_version,
- jobject class_loader,
- bool is_shared,
- bool is_for_vendor,
- jstring library_path,
- jstring permitted_path);
+__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
+ JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
+ jstring library_path, jstring permitted_path);
__attribute__((visibility("default"))) void* OpenNativeLibrary(
JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 043f038..1c2581f 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -31,6 +31,7 @@
#include <list>
#include <memory>
#include <mutex>
+#include <regex>
#include <string>
#include <vector>
@@ -140,10 +141,24 @@
#if defined(__LP64__)
static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+static constexpr const char* kVendorLibPath = "/vendor/lib64";
+static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
#else
static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+static constexpr const char* kVendorLibPath = "/vendor/lib";
+static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
#endif
+static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+static const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+ APK_ORIGIN_DEFAULT = 0,
+ APK_ORIGIN_VENDOR = 1,
+ APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
static bool is_debuggable() {
bool debuggable = false;
#ifdef __BIONIC__
@@ -179,7 +194,7 @@
LibraryNamespaces() : initialized_(false) { }
NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
- bool is_shared, bool is_for_vendor, jstring java_library_path,
+ bool is_shared, jstring dex_path, jstring java_library_path,
jstring java_permitted_path, std::string* error_msg) {
std::string library_path; // empty string by default.
@@ -188,6 +203,8 @@
library_path = library_path_utf_chars.c_str();
}
+ ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
// (http://b/27588281) This is a workaround for apps using custom
// classloaders and calling System.load() with an absolute path which
// is outside of the classloader library search path.
@@ -234,31 +251,50 @@
std::string system_exposed_libraries = system_public_libraries_;
const char* namespace_name = kClassloaderNamespaceName;
android_namespace_t* vndk_ns = nullptr;
- if (is_for_vendor && !is_shared) {
- LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture");
+ if ((apk_origin == APK_ORIGIN_VENDOR ||
+ (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+ !is_shared) {
+ LOG_FATAL_IF(is_native_bridge,
+ "Unbundled vendor / product apk must not use translated architecture");
- // For vendor apks, give access to the vendor lib even though
+ // For vendor / product apks, give access to the vendor / product lib even though
// they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor partition.
-#if defined(__LP64__)
- std::string vendor_lib_path = "/vendor/lib64";
-#else
- std::string vendor_lib_path = "/vendor/lib";
-#endif
- library_path = library_path + ":" + vendor_lib_path.c_str();
- permitted_path = permitted_path + ":" + vendor_lib_path.c_str();
+ // together in the vendor / product partition.
+ const char* origin_partition;
+ const char* origin_lib_path;
+
+ switch (apk_origin) {
+ case APK_ORIGIN_VENDOR:
+ origin_partition = "vendor";
+ origin_lib_path = kVendorLibPath;
+ break;
+ case APK_ORIGIN_PRODUCT:
+ origin_partition = "product";
+ origin_lib_path = kProductLibPath;
+ break;
+ default:
+ origin_partition = "unknown";
+ origin_lib_path = "";
+ }
+
+ LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
+ origin_partition);
+
+ library_path = library_path + ":" + origin_lib_path;
+ permitted_path = permitted_path + ":" + origin_lib_path;
// Also give access to LLNDK libraries since they are available to vendors
system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
// Give access to VNDK-SP libraries from the 'vndk' namespace.
vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
- LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr,
- "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName);
+ LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr, "Cannot find \"%s\" namespace for %s apks",
+ kVndkNamespaceName, origin_partition);
// Different name is useful for debugging
namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
+ ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+ origin_partition, library_path.c_str());
} else {
// oem and product public libraries are NOT available to vendor apks, otherwise it
// would be system->vendor violation.
@@ -660,6 +696,28 @@
return nullptr;
}
+ ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+ ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+ if (dex_path != nullptr) {
+ ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+ apk_origin = APK_ORIGIN_VENDOR;
+ }
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+ LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+ "Dex path contains both vendor and product partition : %s",
+ dex_path_utf_chars.c_str());
+
+ apk_origin = APK_ORIGIN_PRODUCT;
+ }
+ }
+
+ return apk_origin;
+ }
+
bool initialized_;
std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
std::string system_public_libraries_;
@@ -690,31 +748,20 @@
#endif
}
-jstring CreateClassLoaderNamespace(JNIEnv* env,
- int32_t target_sdk_version,
- jobject class_loader,
- bool is_shared,
- bool is_for_vendor,
- jstring library_path,
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring library_path,
jstring permitted_path) {
#if defined(__ANDROID__)
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
std::string error_msg;
- bool success = g_namespaces->Create(env,
- target_sdk_version,
- class_loader,
- is_shared,
- is_for_vendor,
- library_path,
- permitted_path,
- &error_msg) != nullptr;
+ bool success = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
+ library_path, permitted_path, &error_msg) != nullptr;
if (!success) {
return env->NewStringUTF(error_msg.c_str());
}
#else
- UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
- library_path, permitted_path);
+ UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
#endif
return nullptr;
}
@@ -779,8 +826,7 @@
// In this case we create an isolated not-shared namespace for it.
std::string create_error_msg;
if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
- false /* is_for_vendor */, library_path, nullptr,
- &create_error_msg)) == nullptr) {
+ nullptr, library_path, nullptr, &create_error_msg)) == nullptr) {
*error_msg = strdup(create_error_msg.c_str());
return nullptr;
}
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
index 11ecc43..2eb1203 100644
--- a/libnativeloader/native_loader_lazy.cpp
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -50,10 +50,10 @@
}
jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
- bool is_shared, bool is_for_vendor, jstring library_path,
+ bool is_shared, jstring dex_path, jstring library_path,
jstring permitted_path) {
static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
- return f(env, target_sdk_version, class_loader, is_shared, is_for_vendor, library_path,
+ return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
permitted_path);
}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index d094811..9951621 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -45,6 +45,7 @@
using android::base::unique_fd;
static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
@@ -110,12 +111,13 @@
return true;
}
-static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+ std::map<std::string, CgroupDescriptor>* descriptors) {
std::vector<CgroupDescriptor> result;
std::string json_doc;
- if (!android::base::ReadFileToString(CGROUPS_DESC_FILE, &json_doc)) {
- LOG(ERROR) << "Failed to read task profiles from " << CGROUPS_DESC_FILE;
+ if (!android::base::ReadFileToString(file_name, &json_doc)) {
+ PLOG(ERROR) << "Failed to read task profiles from " << file_name;
return false;
}
@@ -130,21 +132,46 @@
const Json::Value& cgroups = root["Cgroups"];
for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
std::string name = cgroups[i]["Controller"].asString();
- descriptors->emplace(std::make_pair(
- name,
- CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
+ auto iter = descriptors->find(name);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(name, CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
- cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString())));
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+ } else {
+ iter->second = CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+ }
}
}
if (root.isMember("Cgroups2")) {
const Json::Value& cgroups2 = root["Cgroups2"];
- descriptors->emplace(std::make_pair(
- CGROUPV2_CONTROLLER_NAME,
- CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(CGROUPV2_CONTROLLER_NAME, CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
- cgroups2["UID"].asString(), cgroups2["GID"].asString())));
+ cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+ } else {
+ iter->second = CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString());
+ }
+ }
+
+ return true;
+}
+
+static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+ // load system cgroup descriptors
+ if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+ return false;
+ }
+
+ // load vendor cgroup descriptors if the file exists
+ if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
+ !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
+ return false;
}
return true;
@@ -158,7 +185,7 @@
// mkdir <path> [mode] [owner] [group]
if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
- PLOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+ LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
@@ -262,7 +289,7 @@
std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
std::string content;
if (!android::base::ReadFileToString(file_name, &content)) {
- LOG(ERROR) << "Failed to read " << file_name;
+ PLOG(ERROR) << "Failed to read " << file_name;
return false;
}
@@ -296,7 +323,7 @@
CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
if (!LoadRcFile()) {
- PLOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
+ LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
}
}
@@ -333,27 +360,42 @@
return false;
}
- cg_file_size_ = sb.st_size;
- if (cg_file_size_ < sizeof(CgroupFile)) {
- PLOG(ERROR) << "Invalid file format " << cgroup_rc_path;
+ size_t file_size = sb.st_size;
+ if (file_size < sizeof(CgroupFile)) {
+ LOG(ERROR) << "Invalid file format " << cgroup_rc_path;
return false;
}
- cg_file_data_ = (CgroupFile*)mmap(nullptr, cg_file_size_, PROT_READ, MAP_SHARED, fd, 0);
- if (cg_file_data_ == MAP_FAILED) {
+ CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (file_data == MAP_FAILED) {
PLOG(ERROR) << "Failed to mmap " << cgroup_rc_path;
return false;
}
- if (cg_file_data_->version_ != CgroupFile::FILE_CURR_VERSION) {
- PLOG(ERROR) << cgroup_rc_path << " file version mismatch";
+ if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+ LOG(ERROR) << cgroup_rc_path << " file version mismatch";
+ munmap(file_data, file_size);
return false;
}
+ if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
+ LOG(ERROR) << cgroup_rc_path << " file has invalid size";
+ munmap(file_data, file_size);
+ return false;
+ }
+
+ cg_file_data_ = file_data;
+ cg_file_size_ = file_size;
+
return true;
}
-void CgroupMap::Print() {
+void CgroupMap::Print() const {
+ if (!cg_file_data_) {
+ LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
+ return;
+ }
LOG(INFO) << "File version = " << cg_file_data_->version_;
LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
@@ -370,7 +412,7 @@
// load cgroups.json file
if (!ReadDescriptors(&descriptors)) {
- PLOG(ERROR) << "Failed to load cgroup description file";
+ LOG(ERROR) << "Failed to load cgroup description file";
return false;
}
@@ -385,7 +427,7 @@
// mkdir <CGROUPS_RC_DIR> 0711 system system
if (!Mkdir(CGROUPS_RC_DIR, 0711, "system", "system")) {
- PLOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
+ LOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
return false;
}
@@ -401,7 +443,7 @@
std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CGROUPS_RC_FILE);
// chmod 0644 <cgroup_rc_path>
if (fchmodat(AT_FDCWD, cgroup_rc_path.c_str(), 0644, AT_SYMLINK_NOFOLLOW) < 0) {
- LOG(ERROR) << "fchmodat() failed";
+ PLOG(ERROR) << "fchmodat() failed";
return false;
}
@@ -410,6 +452,8 @@
const CgroupController* CgroupMap::FindController(const std::string& name) const {
if (!cg_file_data_) {
+ LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
return nullptr;
}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index ba2caf7..1c355cd 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -92,5 +92,5 @@
~CgroupMap();
bool LoadRcFile();
- void Print();
+ void Print() const;
};
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8505e61..8884650 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -170,8 +170,9 @@
return ret;
}
-static void RemoveUidProcessGroups(const std::string& uid_path) {
+static bool RemoveUidProcessGroups(const std::string& uid_path) {
std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
+ bool empty = true;
if (uid != NULL) {
dirent* dir;
while ((dir = readdir(uid.get())) != nullptr) {
@@ -185,9 +186,15 @@
auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ if (rmdir(path.c_str()) == -1) {
+ if (errno != EBUSY) {
+ PLOG(WARNING) << "Failed to remove " << path;
+ }
+ empty = false;
+ }
}
}
+ return empty;
}
void removeAllProcessGroups() {
@@ -219,9 +226,14 @@
}
auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- RemoveUidProcessGroups(path);
+ if (!RemoveUidProcessGroups(path)) {
+ LOG(VERBOSE) << "Skip removing " << path;
+ continue;
+ }
LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ if (rmdir(path.c_str()) == -1 && errno != EBUSY) {
+ PLOG(WARNING) << "Failed to remove " << path;
+ }
}
}
}
@@ -249,6 +261,10 @@
auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
if (!fd) {
+ if (errno == ENOENT) {
+ // This happens when process is already dead
+ return 0;
+ }
PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
return -1;
}
@@ -293,7 +309,7 @@
LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
<< " as part of process cgroup " << initialPid;
- if (kill(-pgid, signal) == -1) {
+ if (kill(-pgid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
}
}
@@ -303,7 +319,7 @@
LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
<< initialPid;
- if (kill(pid, signal) == -1) {
+ if (kill(pid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 15d0172..e05a690 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -29,9 +29,21 @@
src: "task_profiles.json",
}
+cc_defaults {
+ name: "libprocessgroup_test_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+
+ // Needed for headers from libprotobuf.
+ "-Wno-unused-parameter",
+ ],
+}
+
cc_library_static {
name: "libprocessgroup_proto",
host_supported: true,
+ defaults: ["libprocessgroup_test_defaults"],
srcs: [
"cgroups.proto",
"task_profiles.proto",
@@ -40,15 +52,11 @@
type: "full",
export_proto_headers: true,
},
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- ],
}
cc_test_host {
name: "libprocessgroup_proto_test",
+ defaults: ["libprocessgroup_test_defaults"],
srcs: [
"test.cpp",
],
@@ -64,11 +72,6 @@
shared_libs: [
"libprotobuf-cpp-full",
],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- ],
data: [
"cgroups.json",
"cgroups.recovery.json",
@@ -78,3 +81,28 @@
"general-tests",
],
}
+
+cc_test {
+ name: "vts_processgroup_validate_test",
+ defaults: ["libprocessgroup_test_defaults"],
+ srcs: [
+ "test_vendor.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libjsonpbverify",
+ "libjsonpbparse",
+ "libprocessgroup_proto",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libjsoncpp",
+ "libprotobuf-cpp-full",
+ ],
+ target: {
+ android: {
+ test_config: "vts_processgroup_validate_test.xml",
+ },
+ },
+}
diff --git a/libprocessgroup/profiles/Android.mk b/libprocessgroup/profiles/Android.mk
new file mode 100644
index 0000000..eab96d4
--- /dev/null
+++ b/libprocessgroup/profiles/Android.mk
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2019 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := VtsProcessgroupValidateTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/libprocessgroup/profiles/cgroups_test.h b/libprocessgroup/profiles/cgroups_test.h
new file mode 100644
index 0000000..1309957
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_test.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups.pb.h"
+
+using ::testing::MatchesRegex;
+
+namespace android {
+namespace profiles {
+
+class CgroupsTest : public jsonpb::JsonSchemaTest {
+ public:
+ void SetUp() override {
+ JsonSchemaTest::SetUp();
+ cgroups_ = static_cast<Cgroups*>(message());
+ }
+ Cgroups* cgroups_;
+};
+
+TEST_P(CgroupsTest, CgroupRequiredFields) {
+ for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+ auto&& cgroup = cgroups_->cgroups(i);
+ EXPECT_FALSE(cgroup.controller().empty())
+ << "No controller name for cgroup #" << i << " in " << file_path_;
+ EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(CgroupsTest, Cgroup2RequiredFields) {
+ if (cgroups_->has_cgroups2()) {
+ EXPECT_FALSE(cgroups_->cgroups2().path().empty())
+ << "No path for cgroup2 in " << file_path_;
+ }
+}
+
+// "Mode" field must be in the format of "0xxx".
+static inline constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
+TEST_P(CgroupsTest, CgroupMode) {
+ for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+ EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
+ << "For cgroup controller #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(CgroupsTest, Cgroup2Mode) {
+ EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
+ << "For cgroups2 in " << file_path_;
+}
+
+} // namespace profiles
+} // namespace android
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 985720f..74a39cd 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -56,10 +56,10 @@
"Profiles": [
{
"Name": "HighEnergySaving",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "schedtune",
"Path": "background"
@@ -69,10 +69,10 @@
},
{
"Name": "NormalPerformance",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "schedtune",
"Path": ""
@@ -82,10 +82,10 @@
},
{
"Name": "HighPerformance",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "schedtune",
"Path": "foreground"
@@ -95,10 +95,10 @@
},
{
"Name": "MaxPerformance",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "schedtune",
"Path": "top-app"
@@ -108,10 +108,10 @@
},
{
"Name": "RealtimePerformance",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "schedtune",
"Path": "rt"
@@ -122,26 +122,26 @@
{
"Name": "CpuPolicySpread",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetAttribute",
- "Params" :
+ "Name": "SetAttribute",
+ "Params":
{
- "Name" : "STunePreferIdle",
- "Value" : "1"
+ "Name": "STunePreferIdle",
+ "Value": "1"
}
}
]
},
{
"Name": "CpuPolicyPack",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetAttribute",
- "Params" :
+ "Name": "SetAttribute",
+ "Params":
{
- "Name" : "STunePreferIdle",
- "Value" : "0"
+ "Name": "STunePreferIdle",
+ "Value": "0"
}
}
]
@@ -149,10 +149,10 @@
{
"Name": "VrKernelCapacity",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": ""
@@ -162,10 +162,10 @@
},
{
"Name": "VrServiceCapacityLow",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "system/background"
@@ -175,10 +175,10 @@
},
{
"Name": "VrServiceCapacityNormal",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "system"
@@ -188,10 +188,10 @@
},
{
"Name": "VrServiceCapacityHigh",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "system/performance"
@@ -201,10 +201,10 @@
},
{
"Name": "VrProcessCapacityLow",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "application/background"
@@ -214,10 +214,10 @@
},
{
"Name": "VrProcessCapacityNormal",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "application"
@@ -227,10 +227,10 @@
},
{
"Name": "VrProcessCapacityHigh",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "application/performance"
@@ -241,10 +241,10 @@
{
"Name": "ProcessCapacityLow",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "background"
@@ -254,10 +254,10 @@
},
{
"Name": "ProcessCapacityNormal",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": ""
@@ -267,10 +267,10 @@
},
{
"Name": "ProcessCapacityHigh",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "foreground"
@@ -280,10 +280,10 @@
},
{
"Name": "ProcessCapacityMax",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "top-app"
@@ -294,10 +294,10 @@
{
"Name": "ServiceCapacityLow",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "system-background"
@@ -307,10 +307,10 @@
},
{
"Name": "ServiceCapacityRestricted",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "restricted"
@@ -321,10 +321,10 @@
{
"Name": "CameraServiceCapacity",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "cpuset",
"Path": "camera-daemon"
@@ -335,10 +335,10 @@
{
"Name": "LowIoPriority",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "blkio",
"Path": "background"
@@ -348,10 +348,10 @@
},
{
"Name": "NormalIoPriority",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "blkio",
"Path": ""
@@ -361,10 +361,10 @@
},
{
"Name": "HighIoPriority",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "blkio",
"Path": ""
@@ -374,10 +374,10 @@
},
{
"Name": "MaxIoPriority",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "blkio",
"Path": ""
@@ -388,10 +388,10 @@
{
"Name": "TimerSlackHigh",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetTimerSlack",
- "Params" :
+ "Name": "SetTimerSlack",
+ "Params":
{
"Slack": "40000000"
}
@@ -400,10 +400,10 @@
},
{
"Name": "TimerSlackNormal",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetTimerSlack",
- "Params" :
+ "Name": "SetTimerSlack",
+ "Params":
{
"Slack": "50000"
}
@@ -413,26 +413,26 @@
{
"Name": "PerfBoost",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetClamps",
- "Params" :
+ "Name": "SetClamps",
+ "Params":
{
- "Boost" : "50%",
- "Clamp" : "0"
+ "Boost": "50%",
+ "Clamp": "0"
}
}
]
},
{
"Name": "PerfClamp",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetClamps",
- "Params" :
+ "Name": "SetClamps",
+ "Params":
{
- "Boost" : "0",
- "Clamp" : "30%"
+ "Boost": "0",
+ "Clamp": "30%"
}
}
]
@@ -440,21 +440,21 @@
{
"Name": "LowMemoryUsage",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetAttribute",
- "Params" :
+ "Name": "SetAttribute",
+ "Params":
{
- "Name" : "MemSoftLimit",
- "Value" : "16MB"
+ "Name": "MemSoftLimit",
+ "Value": "16MB"
}
},
{
- "Name" : "SetAttribute",
- "Params" :
+ "Name": "SetAttribute",
+ "Params":
{
- "Name" : "MemSwappiness",
- "Value" : "150"
+ "Name": "MemSwappiness",
+ "Value": "150"
}
}
@@ -462,31 +462,31 @@
},
{
"Name": "HighMemoryUsage",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetAttribute",
- "Params" :
+ "Name": "SetAttribute",
+ "Params":
{
- "Name" : "MemSoftLimit",
- "Value" : "512MB"
+ "Name": "MemSoftLimit",
+ "Value": "512MB"
}
},
{
- "Name" : "SetAttribute",
- "Params" :
+ "Name": "SetAttribute",
+ "Params":
{
- "Name" : "MemSwappiness",
- "Value" : "100"
+ "Name": "MemSwappiness",
+ "Value": "100"
}
}
]
},
{
"Name": "SystemMemoryProcess",
- "Actions" : [
+ "Actions": [
{
- "Name" : "JoinCgroup",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
{
"Controller": "memory",
"Path": "system"
diff --git a/libprocessgroup/profiles/task_profiles_test.h b/libprocessgroup/profiles/task_profiles_test.h
new file mode 100644
index 0000000..32f122d
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_test.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "task_profiles.pb.h"
+
+namespace android {
+namespace profiles {
+
+class TaskProfilesTest : public jsonpb::JsonSchemaTest {
+ public:
+ void SetUp() override {
+ JsonSchemaTest::SetUp();
+ task_profiles_ = static_cast<TaskProfiles*>(message());
+ }
+ TaskProfiles* task_profiles_;
+};
+
+TEST_P(TaskProfilesTest, AttributeRequiredFields) {
+ for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
+ auto&& attribute = task_profiles_->attributes(i);
+ EXPECT_FALSE(attribute.name().empty())
+ << "No name for attribute #" << i << " in " << file_path_;
+ EXPECT_FALSE(attribute.controller().empty())
+ << "No controller for attribute #" << i << " in " << file_path_;
+ EXPECT_FALSE(attribute.file().empty())
+ << "No file for attribute #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(TaskProfilesTest, ProfileRequiredFields) {
+ for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
+ auto&& profile = task_profiles_->profiles(profile_idx);
+ EXPECT_FALSE(profile.name().empty())
+ << "No name for profile #" << profile_idx << " in " << file_path_;
+ for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
+ auto&& action = profile.actions(action_idx);
+ EXPECT_FALSE(action.name().empty())
+ << "No name for profiles[" << profile_idx << "].actions[" << action_idx
+ << "] in " << file_path_;
+ }
+ }
+}
+
+} // namespace profiles
+} // namespace android
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
index 8ba14d6..bc9aade 100644
--- a/libprocessgroup/profiles/test.cpp
+++ b/libprocessgroup/profiles/test.cpp
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-#include <string>
-
#include <android-base/file.h>
-#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#include <jsonpb/json_schema_test.h>
-#include "cgroups.pb.h"
-#include "task_profiles.pb.h"
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
using namespace ::android::jsonpb;
using ::android::base::GetExecutableDirectory;
-using ::testing::MatchesRegex;
namespace android {
namespace profiles {
@@ -35,87 +32,7 @@
return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
}
-TEST(LibProcessgroupProto, EmptyMode) {
- EXPECT_EQ(0, strtoul("", nullptr, 8))
- << "Empty mode string cannot be silently converted to 0; this should not happen";
-}
-
-class CgroupsTest : public JsonSchemaTest {
- public:
- void SetUp() override {
- JsonSchemaTest::SetUp();
- cgroups_ = static_cast<Cgroups*>(message());
- }
- Cgroups* cgroups_;
-};
-
-TEST_P(CgroupsTest, CgroupRequiredFields) {
- for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
- auto&& cgroup = cgroups_->cgroups(i);
- EXPECT_FALSE(cgroup.controller().empty())
- << "No controller name for cgroup #" << i << " in " << file_path_;
- EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
- }
-}
-
-TEST_P(CgroupsTest, Cgroup2RequiredFields) {
- if (cgroups_->has_cgroups2()) {
- EXPECT_FALSE(cgroups_->cgroups2().path().empty())
- << "No path for cgroup2 in " << file_path_;
- }
-}
-
-// "Mode" field must be in the format of "0xxx".
-static constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
-TEST_P(CgroupsTest, CgroupMode) {
- for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
- EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
- << "For cgroup controller #" << i << " in " << file_path_;
- }
-}
-
-TEST_P(CgroupsTest, Cgroup2Mode) {
- EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
- << "For cgroups2 in " << file_path_;
-}
-
-class TaskProfilesTest : public JsonSchemaTest {
- public:
- void SetUp() override {
- JsonSchemaTest::SetUp();
- task_profiles_ = static_cast<TaskProfiles*>(message());
- }
- TaskProfiles* task_profiles_;
-};
-
-TEST_P(TaskProfilesTest, AttributeRequiredFields) {
- for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
- auto&& attribute = task_profiles_->attributes(i);
- EXPECT_FALSE(attribute.name().empty())
- << "No name for attribute #" << i << " in " << file_path_;
- EXPECT_FALSE(attribute.controller().empty())
- << "No controller for attribute #" << i << " in " << file_path_;
- EXPECT_FALSE(attribute.file().empty())
- << "No file for attribute #" << i << " in " << file_path_;
- }
-}
-
-TEST_P(TaskProfilesTest, ProfileRequiredFields) {
- for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
- auto&& profile = task_profiles_->profiles(profile_idx);
- EXPECT_FALSE(profile.name().empty())
- << "No name for profile #" << profile_idx << " in " << file_path_;
- for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
- auto&& action = profile.actions(action_idx);
- EXPECT_FALSE(action.name().empty())
- << "No name for profiles[" << profile_idx << "].actions[" << action_idx
- << "] in " << file_path_;
- }
- }
-}
-
// Test suite instantiations
-
INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
MakeTestParam<Cgroups>("/cgroups.recovery.json"),
diff --git a/libprocessgroup/profiles/test_vendor.cpp b/libprocessgroup/profiles/test_vendor.cpp
new file mode 100644
index 0000000..3ec7fcf
--- /dev/null
+++ b/libprocessgroup/profiles/test_vendor.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using ::android::base::GetExecutableDirectory;
+using namespace ::android::jsonpb;
+
+namespace android {
+namespace profiles {
+
+static constexpr const char* kVendorCgroups = "/vendor/etc/cgroups.json";
+static constexpr const char* kVendorTaskProfiles = "/vendor/etc/task_profiles.json";
+
+template <typename T>
+class TestConfig : public JsonSchemaTestConfig {
+ public:
+ TestConfig(const std::string& path) : file_path_(path){};
+ std::unique_ptr<google::protobuf::Message> CreateMessage() const override {
+ return std::make_unique<T>();
+ }
+ std::string file_path() const override { return file_path_; }
+ bool optional() const override {
+ // Ignore when vendor JSON files are missing.
+ return true;
+ }
+
+ private:
+ std::string file_path_;
+};
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+ return [path]() { return std::make_unique<TestConfig<T>>(path); };
+}
+
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, JsonSchemaTest,
+ ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, CgroupsTest,
+ ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, JsonSchemaTest,
+ ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, TaskProfilesTest,
+ ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+
+} // namespace profiles
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/vts_processgroup_validate_test.xml b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
new file mode 100644
index 0000000..21d29cd
--- /dev/null
+++ b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for VtsProcessgroupValidateTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsProcessgroupValidateTest"/>
+ <option name="binary-test-working-directory" value="_32bit::/data/nativetest/" />
+ <option name="binary-test-working-directory" value="_64bit::/data/nativetest64/" />
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="binary-test-disable-framework" value="false"/>
+ <option name="test-timeout" value="30s"/>
+ </test>
+</configuration>
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 026c011..1eefada 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -167,7 +167,7 @@
if (!controller) return -1;
if (!controller->GetTaskGroup(tid, &subgroup)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return -1;
}
return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 8ec29d3..f1fbcde 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -42,6 +42,7 @@
using android::base::WriteStringToFile;
#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
+#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
std::string subgroup;
@@ -118,7 +119,7 @@
std::string path;
if (!attribute_->GetPathForTask(tid, &path)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return false;
}
@@ -173,7 +174,7 @@
if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
// If the thread is in the process of exiting, don't flag an error
if (errno != ESRCH) {
- PLOG(ERROR) << "JoinGroup failed to write '" << value << "'; fd=" << fd;
+ PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
return false;
}
}
@@ -186,7 +187,7 @@
if (fd_ >= 0) {
// fd is cached, reuse it
if (!AddTidToCgroup(pid, fd_)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
return true;
@@ -201,11 +202,11 @@
std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
- PLOG(WARNING) << "Failed to open " << procs_path << ": " << strerror(errno);
+ PLOG(WARNING) << "Failed to open " << procs_path;
return false;
}
if (!AddTidToCgroup(pid, tmp_fd)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -218,7 +219,7 @@
return true;
}
if (!AddTidToCgroup(pid, tmp_fd)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -231,7 +232,7 @@
if (fd_ >= 0) {
// fd is cached, reuse it
if (!AddTidToCgroup(tid, fd_)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
return true;
@@ -243,7 +244,7 @@
}
// application-dependent path can't be used with tid
- PLOG(ERROR) << "Application profile can't be applied to a thread";
+ LOG(ERROR) << "Application profile can't be applied to a thread";
return false;
#else
std::string tasks_path = controller_->GetTasksFilePath(path_);
@@ -253,7 +254,7 @@
return true;
}
if (!AddTidToCgroup(tid, tmp_fd)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -288,16 +289,24 @@
}
TaskProfiles::TaskProfiles() {
- if (!Load(CgroupMap::GetInstance())) {
- LOG(ERROR) << "TaskProfiles::Load for [" << getpid() << "] failed";
+ // load system task profiles
+ if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
+ LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
+ }
+
+ // load vendor task profiles if the file exists
+ if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
+ !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
+ LOG(ERROR) << "Loading " << TASK_PROFILE_DB_VENDOR_FILE << " for [" << getpid()
+ << "] failed";
}
}
-bool TaskProfiles::Load(const CgroupMap& cg_map) {
+bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
std::string json_doc;
- if (!android::base::ReadFileToString(TASK_PROFILE_DB_FILE, &json_doc)) {
- LOG(ERROR) << "Failed to read task profiles from " << TASK_PROFILE_DB_FILE;
+ if (!android::base::ReadFileToString(file_name, &json_doc)) {
+ LOG(ERROR) << "Failed to read task profiles from " << file_name;
return false;
}
@@ -308,18 +317,18 @@
return false;
}
- Json::Value attr = root["Attributes"];
+ const Json::Value& attr = root["Attributes"];
for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {
std::string name = attr[i]["Name"].asString();
- std::string ctrlName = attr[i]["Controller"].asString();
- std::string file_name = attr[i]["File"].asString();
+ std::string controller_name = attr[i]["Controller"].asString();
+ std::string file_attr = attr[i]["File"].asString();
if (attributes_.find(name) == attributes_.end()) {
- const CgroupController* controller = cg_map.FindController(ctrlName);
+ const CgroupController* controller = cg_map.FindController(controller_name);
if (controller) {
- attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_name);
+ attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
} else {
- LOG(WARNING) << "Controller " << ctrlName << " is not found";
+ LOG(WARNING) << "Controller " << controller_name << " is not found";
}
} else {
LOG(WARNING) << "Attribute " << name << " is already defined";
@@ -328,72 +337,72 @@
std::map<std::string, std::string> params;
- Json::Value profilesVal = root["Profiles"];
- for (Json::Value::ArrayIndex i = 0; i < profilesVal.size(); ++i) {
- Json::Value profileVal = profilesVal[i];
+ const Json::Value& profiles_val = root["Profiles"];
+ for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
+ const Json::Value& profile_val = profiles_val[i];
- std::string profileName = profileVal["Name"].asString();
- Json::Value actions = profileVal["Actions"];
+ std::string profile_name = profile_val["Name"].asString();
+ const Json::Value& actions = profile_val["Actions"];
auto profile = std::make_unique<TaskProfile>();
- for (Json::Value::ArrayIndex actIdx = 0; actIdx < actions.size(); ++actIdx) {
- Json::Value actionVal = actions[actIdx];
- std::string actionName = actionVal["Name"].asString();
- Json::Value paramsVal = actionVal["Params"];
- if (actionName == "JoinCgroup") {
- std::string ctrlName = paramsVal["Controller"].asString();
- std::string path = paramsVal["Path"].asString();
+ for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
+ const Json::Value& action_val = actions[act_idx];
+ std::string action_name = action_val["Name"].asString();
+ const Json::Value& params_val = action_val["Params"];
+ if (action_name == "JoinCgroup") {
+ std::string controller_name = params_val["Controller"].asString();
+ std::string path = params_val["Path"].asString();
- const CgroupController* controller = cg_map.FindController(ctrlName);
+ const CgroupController* controller = cg_map.FindController(controller_name);
if (controller) {
profile->Add(std::make_unique<SetCgroupAction>(controller, path));
} else {
- LOG(WARNING) << "JoinCgroup: controller " << ctrlName << " is not found";
+ LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
}
- } else if (actionName == "SetTimerSlack") {
- std::string slackValue = paramsVal["Slack"].asString();
+ } else if (action_name == "SetTimerSlack") {
+ std::string slack_value = params_val["Slack"].asString();
char* end;
unsigned long slack;
- slack = strtoul(slackValue.c_str(), &end, 10);
- if (end > slackValue.c_str()) {
+ slack = strtoul(slack_value.c_str(), &end, 10);
+ if (end > slack_value.c_str()) {
profile->Add(std::make_unique<SetTimerSlackAction>(slack));
} else {
- LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slackValue;
+ LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slack_value;
}
- } else if (actionName == "SetAttribute") {
- std::string attrName = paramsVal["Name"].asString();
- std::string attrValue = paramsVal["Value"].asString();
+ } else if (action_name == "SetAttribute") {
+ std::string attr_name = params_val["Name"].asString();
+ std::string attr_value = params_val["Value"].asString();
- auto iter = attributes_.find(attrName);
+ auto iter = attributes_.find(attr_name);
if (iter != attributes_.end()) {
profile->Add(
- std::make_unique<SetAttributeAction>(iter->second.get(), attrValue));
+ std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
} else {
- LOG(WARNING) << "SetAttribute: unknown attribute: " << attrName;
+ LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
}
- } else if (actionName == "SetClamps") {
- std::string boostValue = paramsVal["Boost"].asString();
- std::string clampValue = paramsVal["Clamp"].asString();
+ } else if (action_name == "SetClamps") {
+ std::string boost_value = params_val["Boost"].asString();
+ std::string clamp_value = params_val["Clamp"].asString();
char* end;
unsigned long boost;
- boost = strtoul(boostValue.c_str(), &end, 10);
- if (end > boostValue.c_str()) {
- unsigned long clamp = strtoul(clampValue.c_str(), &end, 10);
- if (end > clampValue.c_str()) {
+ boost = strtoul(boost_value.c_str(), &end, 10);
+ if (end > boost_value.c_str()) {
+ unsigned long clamp = strtoul(clamp_value.c_str(), &end, 10);
+ if (end > clamp_value.c_str()) {
profile->Add(std::make_unique<SetClampsAction>(boost, clamp));
} else {
- LOG(WARNING) << "SetClamps: invalid parameter " << clampValue;
+ LOG(WARNING) << "SetClamps: invalid parameter " << clamp_value;
}
} else {
- LOG(WARNING) << "SetClamps: invalid parameter: " << boostValue;
+ LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
}
} else {
- LOG(WARNING) << "Unknown profile action: " << actionName;
+ LOG(WARNING) << "Unknown profile action: " << action_name;
}
}
- profiles_[profileName] = std::move(profile);
+ profiles_[profile_name] = std::move(profile);
}
return true;
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index b2e39f9..9ee81c1 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -152,5 +152,5 @@
TaskProfiles();
- bool Load(const CgroupMap& cg_map);
+ bool Load(const CgroupMap& cg_map, const std::string& file_name);
};
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 27cda93..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -31,7 +31,6 @@
vndk: {
enabled: true,
},
- double_loadable: true,
host_supported: true,
srcs: [
"process.cpp",
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a49fd9e..b7650a1 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -248,6 +248,7 @@
"tests/files/offline/offset_arm/*",
"tests/files/offline/shared_lib_in_apk_arm64/*",
"tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
+ "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 2f5eed9..4b93abb 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -93,9 +93,12 @@
valid_ = false;
}
-bool Elf::GetSoname(std::string* name) {
+std::string Elf::GetSoname() {
std::lock_guard<std::mutex> guard(lock_);
- return valid_ && interface_->GetSoname(name);
+ if (!valid_) {
+ return "";
+ }
+ return interface_->GetSoname();
}
uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index c1b98d9..12efb94 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -374,13 +374,12 @@
}
template <typename DynType>
-bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
+std::string ElfInterface::GetSonameWithTemplate() {
if (soname_type_ == SONAME_INVALID) {
- return false;
+ return "";
}
if (soname_type_ == SONAME_VALID) {
- *soname = soname_;
- return true;
+ return soname_;
}
soname_type_ = SONAME_INVALID;
@@ -397,7 +396,7 @@
if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
last_error_.code = ERROR_MEMORY_INVALID;
last_error_.address = offset;
- return false;
+ return "";
}
if (dyn.d_tag == DT_STRTAB) {
@@ -416,17 +415,16 @@
if (entry.first == strtab_addr) {
soname_offset = entry.second + soname_offset;
if (soname_offset >= entry.second + strtab_size) {
- return false;
+ return "";
}
if (!memory_->ReadString(soname_offset, &soname_)) {
- return false;
+ return "";
}
soname_type_ = SONAME_VALID;
- *soname = soname_;
- return true;
+ return soname_;
}
}
- return false;
+ return "";
}
template <typename SymType>
@@ -653,8 +651,8 @@
template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
-template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
-template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+template std::string ElfInterface::GetSonameWithTemplate<Elf32_Dyn>();
+template std::string ElfInterface::GetSonameWithTemplate<Elf64_Dyn>();
template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
uint64_t*);
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index fdfd705..a20be00 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -77,7 +77,7 @@
// f0000-f2000 0 r-- /system/lib/libc.so
// f2000-f3000 2000 rw- /system/lib/libc.so
MapInfo* map_start = nullptr;
- for (MapInfo* info : *maps) {
+ for (const auto& info : *maps) {
if (map_start != nullptr) {
if (map_start->name == info->name) {
if (info->offset != 0 &&
@@ -96,7 +96,7 @@
}
if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
!info->name.empty()) {
- map_start = info;
+ map_start = info.get();
}
}
}
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 89a6a79..28373b2 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -88,6 +88,7 @@
// Check if the start of this map is an embedded elf.
uint64_t max_size = 0;
if (Elf::GetInfo(memory.get(), &max_size)) {
+ elf_start_offset = offset;
if (max_size > map_size) {
if (memory->Init(name, offset, max_size)) {
return memory.release();
@@ -96,6 +97,7 @@
if (memory->Init(name, offset, map_size)) {
return memory.release();
}
+ elf_start_offset = 0;
return nullptr;
}
return memory.release();
@@ -186,44 +188,57 @@
}
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
- // Make sure no other thread is trying to add the elf to this map.
- std::lock_guard<std::mutex> guard(mutex_);
+ {
+ // Make sure no other thread is trying to add the elf to this map.
+ std::lock_guard<std::mutex> guard(mutex_);
- if (elf.get() != nullptr) {
- return elf.get();
- }
-
- bool locked = false;
- if (Elf::CachingEnabled() && !name.empty()) {
- Elf::CacheLock();
- locked = true;
- if (Elf::CacheGet(this)) {
- Elf::CacheUnlock();
+ if (elf.get() != nullptr) {
return elf.get();
}
+
+ bool locked = false;
+ if (Elf::CachingEnabled() && !name.empty()) {
+ Elf::CacheLock();
+ locked = true;
+ if (Elf::CacheGet(this)) {
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+
+ Memory* memory = CreateMemory(process_memory);
+ if (locked) {
+ if (Elf::CacheAfterCreateMemory(this)) {
+ delete memory;
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+ elf.reset(new Elf(memory));
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ elf->Init();
+ if (elf->valid() && expected_arch != elf->arch()) {
+ // Make the elf invalid, mismatch between arch and expected arch.
+ elf->Invalidate();
+ }
+
+ if (locked) {
+ Elf::CacheAdd(this);
+ Elf::CacheUnlock();
+ }
}
- Memory* memory = CreateMemory(process_memory);
- if (locked) {
- if (Elf::CacheAfterCreateMemory(this)) {
- delete memory;
- Elf::CacheUnlock();
- return elf.get();
+ // If there is a read-only map then a read-execute map that represents the
+ // same elf object, make sure the previous map is using the same elf
+ // object if it hasn't already been set.
+ if (prev_map != nullptr && elf_start_offset != offset && prev_map->offset == elf_start_offset &&
+ prev_map->name == name) {
+ std::lock_guard<std::mutex> guard(prev_map->mutex_);
+ if (prev_map->elf.get() == nullptr) {
+ prev_map->elf = elf;
}
}
- elf.reset(new Elf(memory));
- // If the init fails, keep the elf around as an invalid object so we
- // don't try to reinit the object.
- elf->Init();
- if (elf->valid() && expected_arch != elf->arch()) {
- // Make the elf invalid, mismatch between arch and expected arch.
- elf->Invalidate();
- }
-
- if (locked) {
- Elf::CacheAdd(this);
- Elf::CacheUnlock();
- }
return elf.get();
}
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 1e4f72e..5da73e4 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -47,9 +47,9 @@
size_t last = maps_.size();
while (first < last) {
size_t index = (first + last) / 2;
- MapInfo* cur = maps_[index];
+ const auto& cur = maps_[index];
if (pc >= cur->start && pc < cur->end) {
- return cur;
+ return cur.get();
} else if (pc < cur->start) {
last = index;
} else {
@@ -67,34 +67,31 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(
- new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
+ maps_.emplace_back(
+ new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+ flags, name));
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name, uint64_t load_bias) {
- MapInfo* map_info =
- new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, offset, flags, name);
+ auto map_info =
+ std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
+ flags, name);
map_info->load_bias = load_bias;
- maps_.push_back(map_info);
+ maps_.emplace_back(std::move(map_info));
}
void Maps::Sort() {
std::sort(maps_.begin(), maps_.end(),
- [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
+ [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+ return a->start < b->start; });
// Set the prev_map values on the info objects.
MapInfo* prev_map = nullptr;
- for (MapInfo* map_info : maps_) {
+ for (const auto& map_info : maps_) {
map_info->prev_map = prev_map;
- prev_map = map_info;
- }
-}
-
-Maps::~Maps() {
- for (auto& map : maps_) {
- delete map;
+ prev_map = map_info.get();
}
}
@@ -107,8 +104,9 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(
- new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
+ maps_.emplace_back(
+ new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+ flags, name));
});
}
@@ -124,10 +122,6 @@
// New maps will be added at the end without deleting the old ones.
size_t last_map_idx = maps_.size();
if (!Parse()) {
- // Delete any maps added by the Parse call.
- for (size_t i = last_map_idx; i < maps_.size(); i++) {
- delete maps_[i];
- }
maps_.resize(last_map_idx);
return false;
}
@@ -135,17 +129,16 @@
size_t total_entries = maps_.size();
size_t search_map_idx = 0;
for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
- MapInfo* new_map_info = maps_[new_map_idx];
+ auto& new_map_info = maps_[new_map_idx];
uint64_t start = new_map_info->start;
uint64_t end = new_map_info->end;
uint64_t flags = new_map_info->flags;
std::string* name = &new_map_info->name;
for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
- MapInfo* info = maps_[old_map_idx];
+ auto& info = maps_[old_map_idx];
if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
// No need to check
search_map_idx = old_map_idx + 1;
- delete new_map_info;
maps_[new_map_idx] = nullptr;
total_entries--;
break;
@@ -158,7 +151,7 @@
// Never delete these maps, they may be in use. The assumption is
// that there will only every be a handfull of these so waiting
// to destroy them is not too expensive.
- saved_maps_.push_back(info);
+ saved_maps_.emplace_back(std::move(info));
maps_[old_map_idx] = nullptr;
total_entries--;
}
@@ -169,14 +162,14 @@
// Now move out any of the maps that never were found.
for (size_t i = search_map_idx; i < last_map_idx; i++) {
- saved_maps_.push_back(maps_[i]);
+ saved_maps_.emplace_back(std::move(maps_[i]));
maps_[i] = nullptr;
total_entries--;
}
// Sort all of the values such that the nullptrs wind up at the end, then
// resize them away.
- std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+ std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
if (a == nullptr) {
return false;
} else if (b == nullptr) {
@@ -189,10 +182,4 @@
return true;
}
-LocalUpdatableMaps::~LocalUpdatableMaps() {
- for (auto map_info : saved_maps_) {
- delete map_info;
- }
-}
-
} // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2734cf8..a1c58dd 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -105,6 +105,12 @@
if (resolve_names_) {
frame->map_name = map_info->name;
+ if (embedded_soname_ && map_info->elf_start_offset != 0 && !frame->map_name.empty()) {
+ std::string soname = elf->GetSoname();
+ if (!soname.empty()) {
+ frame->map_name += '!' + soname;
+ }
+ }
}
frame->map_elf_start_offset = map_info->elf_start_offset;
frame->map_exact_offset = map_info->offset;
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index 8caecc7..de9137a 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -92,9 +92,9 @@
// Find the libc.so share library and use that for benchmark purposes.
*build_id_map_info = nullptr;
- for (unwindstack::MapInfo* map_info : maps) {
+ for (auto& map_info : maps) {
if (map_info->offset == 0 && map_info->GetBuildID() != "") {
- *build_id_map_info = map_info;
+ *build_id_map_info = map_info.get();
break;
}
}
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 00a249f..ac94f10 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -59,7 +59,7 @@
void Invalidate();
- bool GetSoname(std::string* name);
+ std::string GetSoname();
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index d41bb13..dbd917d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -56,7 +56,7 @@
virtual void InitHeaders(uint64_t load_bias) = 0;
- virtual bool GetSoname(std::string* name) = 0;
+ virtual std::string GetSoname() = 0;
virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
@@ -117,7 +117,7 @@
void ReadSectionHeaders(const EhdrType& ehdr);
template <typename DynType>
- bool GetSonameWithTemplate(std::string* soname);
+ std::string GetSonameWithTemplate();
template <typename SymType>
bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
@@ -183,9 +183,7 @@
ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
}
- bool GetSoname(std::string* soname) override {
- return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
- }
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
@@ -215,9 +213,7 @@
ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
}
- bool GetSoname(std::string* soname) override {
- return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
- }
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 67fbed2..1784394 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <string>
#include <vector>
@@ -37,8 +38,16 @@
class Maps {
public:
+ virtual ~Maps() = default;
+
Maps() = default;
- virtual ~Maps();
+
+ // Maps are not copyable but movable, because they own pointers to MapInfo
+ // objects.
+ Maps(const Maps&) = delete;
+ Maps& operator=(const Maps&) = delete;
+ Maps(Maps&&) = default;
+ Maps& operator=(Maps&&) = default;
MapInfo* Find(uint64_t pc);
@@ -51,11 +60,11 @@
void Sort();
- typedef std::vector<MapInfo*>::iterator iterator;
+ typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
- typedef std::vector<MapInfo*>::const_iterator const_iterator;
+ typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
const_iterator begin() const { return maps_.begin(); }
const_iterator end() const { return maps_.end(); }
@@ -63,11 +72,11 @@
MapInfo* Get(size_t index) {
if (index >= maps_.size()) return nullptr;
- return maps_[index];
+ return maps_[index].get();
}
protected:
- std::vector<MapInfo*> maps_;
+ std::vector<std::unique_ptr<MapInfo>> maps_;
};
class RemoteMaps : public Maps {
@@ -90,14 +99,14 @@
class LocalUpdatableMaps : public Maps {
public:
LocalUpdatableMaps() : Maps() {}
- virtual ~LocalUpdatableMaps();
+ virtual ~LocalUpdatableMaps() = default;
bool Reparse();
const std::string GetMapsFile() const override;
private:
- std::vector<MapInfo*> saved_maps_;
+ std::vector<std::unique_ptr<MapInfo>> saved_maps_;
};
class BufferMaps : public Maps {
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dba41d1..b3beb6e 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -101,7 +101,7 @@
size_t Size() { return size_; }
- void Clear();
+ void Clear() override;
protected:
size_t size_ = 0;
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 878ced3..1c2a81c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -18,6 +18,7 @@
#define _LIBUNWINDSTACK_REGS_H
#include <stdint.h>
+#include <unistd.h>
#include <functional>
#include <string>
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index ddda7fd..a0554e2 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -100,6 +100,11 @@
// set to an empty string and the function offset being set to zero.
void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+ // Enable/disable soname printing the soname for a map name if the elf is
+ // embedded in a file. This is enabled by default.
+ // NOTE: This does nothing unless resolving names is enabled.
+ void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
+
#if !defined(NO_LIBDEXFILE_SUPPORT)
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
#endif
@@ -124,6 +129,7 @@
DexFiles* dex_files_ = nullptr;
#endif
bool resolve_names_ = true;
+ bool embedded_soname_ = true;
ErrorData last_error_;
};
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 946bc3c..bd3083c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -68,7 +68,7 @@
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
- bool GetSoname(std::string*) override { return false; }
+ std::string GetSoname() override { return fake_soname_; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
bool GetGlobalVariable(const std::string&, uint64_t*) override;
@@ -83,6 +83,8 @@
void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
+ void FakeSetSoname(const char* soname) { fake_soname_ = soname; }
+
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
@@ -98,6 +100,7 @@
private:
std::unordered_map<std::string, uint64_t> globals_;
std::string fake_build_id_;
+ std::string fake_soname_;
static std::deque<FunctionData> functions_;
static std::deque<StepData> steps_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 7239749..d895863 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -555,9 +555,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_TRUE(elf->GetSoname(&name));
- ASSERT_STREQ("fake_soname.so", name.c_str());
+ ASSERT_EQ("fake_soname.so", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname) {
@@ -578,8 +576,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
@@ -600,8 +597,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_size) {
@@ -624,8 +620,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1ff2306..23c9cf8 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -126,9 +126,9 @@
ASSERT_FALSE(elf.valid());
ASSERT_TRUE(elf.interface() == nullptr);
- std::string name;
- ASSERT_FALSE(elf.GetSoname(&name));
+ ASSERT_EQ("", elf.GetSoname());
+ std::string name;
uint64_t func_offset;
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
@@ -309,7 +309,7 @@
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
- bool GetSoname(std::string*) override { return false; }
+ std::string GetSoname() override { return ""; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
std::string GetBuildID() override { return ""; }
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index a66685a..2ddadef 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -178,7 +178,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
- EXPECT_EQ(0U, info.elf_start_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
// Read the valid part of the file.
std::vector<uint8_t> buffer(0x100);
@@ -202,7 +202,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
- EXPECT_EQ(0U, info.elf_start_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -219,7 +219,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
- EXPECT_EQ(0U, info.elf_start_offset);
+ EXPECT_EQ(0x2000U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index d7b8485..d60b8b1 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -371,4 +371,35 @@
}
}
+// Verify that previous maps don't automatically get the same elf object.
+TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
+ MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+ MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
+ Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by a read-execute map will result
+// in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
+ MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index b4197f2..9e7a6ab 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -61,6 +61,26 @@
ASSERT_EQ(0U, info->load_bias.load());
}
+TEST(MapsTest, map_move) {
+ Maps maps;
+
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+ maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+ maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+ Maps maps2 = std::move(maps);
+
+ ASSERT_EQ(3U, maps2.Total());
+ MapInfo* info = maps2.Get(0);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("fake_map", info->name);
+ ASSERT_EQ(0U, info->elf_offset);
+ ASSERT_EQ(0U, info->load_bias.load());
+}
+
TEST(MapsTest, verify_parse_line) {
MapInfo info(nullptr, 0, 0, 0, 0, "");
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index b5feb38..655579e 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -300,7 +300,7 @@
EXPECT_EQ(
" #00 pc 00068fb8 libarttestd.so (art::CauseSegfault()+72)\n"
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
- " #02 pc 000021a8 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
+ " #02 pc 000021a8 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+136)\n"
" #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
@@ -601,7 +601,7 @@
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
- " #01 pc 0000212d 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
+ " #01 pc 0000212d 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
" #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
@@ -1312,7 +1312,8 @@
" #02 pc 00000000000008bc vdso.so\n"
" #03 pc 00000000000846f4 libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
- " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x4000) (ANGLEGetUtilityAPI+56)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
+ "(ANGLEGetUtilityAPI+56)\n"
" #06 pc 000000000007fe68 libc.so (__libc_init)\n",
frame_info);
@@ -1372,4 +1373,56 @@
// Ignore top frame since the test code was modified to end in __libc_init.
}
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000814bc libc.so (syscall+28)\n"
+ " #01 pc 00000000008cdf5c test.apk (offset 0x5000)\n"
+ " #02 pc 00000000008cde9c test.apk (offset 0x5000)\n"
+ " #03 pc 00000000008cdd70 test.apk (offset 0x5000)\n"
+ " #04 pc 00000000008ce408 test.apk (offset 0x5000)\n"
+ " #05 pc 00000000008ce8d8 test.apk (offset 0x5000)\n"
+ " #06 pc 00000000008ce814 test.apk (offset 0x5000)\n"
+ " #07 pc 00000000008bcf60 test.apk (offset 0x5000)\n"
+ " #08 pc 0000000000133024 test.apk (offset 0x5000)\n"
+ " #09 pc 0000000000134ad0 test.apk (offset 0x5000)\n"
+ " #10 pc 0000000000134b64 test.apk (offset 0x5000)\n"
+ " #11 pc 00000000000e406c libc.so (__pthread_start(void*)+36)\n"
+ " #12 pc 0000000000085e18 libc.so (__start_thread+64)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7be6715e9cULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7be4f07800ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7be6715d70ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7be4f07840ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7be6716408ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7be4f07860ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7be67168d8ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7be4f07880ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7be6716814ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7be4f078f0ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x7be6704f60ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7be4f07910ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x7be5f7b024ULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7be4f07950ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x7be5f7cad0ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7be4f07aa0ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x7be5f7cb64ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7be4f07ce0ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x7cbe11406cULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7be4f07d00ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x7cbe0b5e18ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0x7be4f07d20ULL, unwinder.frames()[12].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index d88531f..504b57a 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -49,7 +49,7 @@
std::string str_name(name);
maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
if (elf != nullptr) {
- MapInfo* map_info = *--maps_->end();
+ const auto& map_info = *--maps_->end();
map_info->elf.reset(elf);
}
}
@@ -79,13 +79,18 @@
AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
elf = new ElfFake(new MemoryFake);
- elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ interface->FakeSetSoname("lib_fake.so");
+ elf->FakeSetInterface(interface);
AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
+ MapInfo* map_info = maps_->Find(0x43000);
+ ASSERT_TRUE(map_info != nullptr);
+ map_info->elf_start_offset = 0x1d000;
AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
- MapInfo* info = *--maps_->end();
+ const auto& info = *--maps_->end();
info->load_bias = 0;
elf = new ElfFake(new MemoryFake);
@@ -98,8 +103,8 @@
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
elf);
- info = *--maps_->end();
- info->elf_offset = 0x8000;
+ const auto& info2 = *--maps_->end();
+ info2->elf_offset = 0x8000;
process_memory_.reset(new MemoryFake);
}
@@ -324,8 +329,38 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
+ EXPECT_EQ(0x43000U, frame->map_start);
+ EXPECT_EQ(0x44000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, disable_embedded_soname) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0x43000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.SetEmbeddedSoname(false);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x43000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
@@ -813,8 +848,8 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index 3cd9d40..4043122 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -1,5 +1,6 @@
ab0d3000-ab0d8000 r-xp 0 00:00 0 dalvikvm32
dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so
+e0445000-e0447000 r--p 0 00:00 0 137-cfi.odex
e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex
e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000
e648e000-e690f000 r-xp 0 00:00 0 libart.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index a8d215c..f255a44 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -1,5 +1,6 @@
56573000-56577000 r-xp 0 00:00 0 dalvikvm32
eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
+ec604000-ec606000 r--p 0 00:00 0 137-cfi.odex
ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
new file mode 100644
index 0000000..cac1dd9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
new file mode 100644
index 0000000..2c5ca62
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
@@ -0,0 +1,3 @@
+7be5e48000-7be6b2b000 r-xp 5000 00:00 0 test.apk
+7cbe030000-7cbe070000 r--p 0 00:00 0 libc.so
+7cbe070000-7cbe11a000 r-xp 40000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
new file mode 100644
index 0000000..090aeda
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7c326f6568
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 1
+x10: 1
+x11: 0
+x12: ffffffffc4653600
+x13: 17645696f
+x14: 2742ed97ca77a3
+x15: 3ab49084
+x16: 7be6b6bdb8
+x17: 7cbe0b14a0
+x18: 7c2b02a000
+x19: 0
+x20: 7c326f6568
+x21: 7be69c827c
+x22: 7be69c8272
+x23: 1
+x24: 7be74f7100
+x25: 881
+x26: 7be4f07a00
+x27: c479c000
+x28: 7be4f07998
+x29: 7be4f079b4
+sp: 7be4f077d0
+lr: 7be6715f60
+pc: 7cbe0b14bc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
new file mode 100644
index 0000000..27d5bf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
new file mode 100644
index 0000000..70a9c71
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 19982d8..92e5c0a 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -118,8 +118,8 @@
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n", soname.c_str());
}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 4b6f49a..b77a86b 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -185,8 +185,8 @@
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 9128430..b0a4dd0 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -71,8 +71,8 @@
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index 39bf3a5..fc6f305 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -20,7 +20,6 @@
vndk: {
enabled: true,
},
- double_loadable: true,
host_supported: true,
srcs: ["usbhost.c"],
cflags: ["-Werror"],
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 2e0cf6e..c9b4894 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -45,6 +45,44 @@
bool* mDeleted;
};
+// A version of Foo that ensures that all objects are allocated at the same
+// address. No more than one can be allocated at a time. Thread-hostile.
+class FooFixedAlloc : public RefBase {
+public:
+ static void* operator new(size_t size) {
+ if (mAllocCount != 0) {
+ abort();
+ }
+ mAllocCount = 1;
+ if (theMemory == nullptr) {
+ theMemory = malloc(size);
+ }
+ return theMemory;
+ }
+
+ static void operator delete(void *p) {
+ if (mAllocCount != 1 || p != theMemory) {
+ abort();
+ }
+ mAllocCount = 0;
+ }
+
+ FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) {
+ *mDeleted = false;
+ }
+
+ ~FooFixedAlloc() {
+ *mDeleted = true;
+ }
+private:
+ bool* mDeleted;
+ static int mAllocCount;
+ static void* theMemory;
+};
+
+int FooFixedAlloc::mAllocCount(0);
+void* FooFixedAlloc::theMemory(nullptr);
+
TEST(RefBase, StrongMoves) {
bool isDeleted;
Foo* foo = new Foo(&isDeleted);
@@ -90,6 +128,118 @@
ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
}
+TEST(RefBase, Comparisons) {
+ bool isDeleted, isDeleted2, isDeleted3;
+ Foo* foo = new Foo(&isDeleted);
+ Foo* foo2 = new Foo(&isDeleted2);
+ sp<Foo> sp1(foo);
+ sp<Foo> sp2(foo2);
+ wp<Foo> wp1(sp1);
+ wp<Foo> wp2(sp1);
+ wp<Foo> wp3(sp2);
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_TRUE(wp1 == sp1);
+ ASSERT_TRUE(wp3 == sp2);
+ ASSERT_TRUE(wp1 != sp2);
+ ASSERT_TRUE(wp1 <= wp2);
+ ASSERT_TRUE(wp1 >= wp2);
+ ASSERT_FALSE(wp1 != wp2);
+ ASSERT_FALSE(wp1 > wp2);
+ ASSERT_FALSE(wp1 < wp2);
+ ASSERT_FALSE(sp1 == sp2);
+ ASSERT_TRUE(sp1 != sp2);
+ bool sp1_smaller = sp1 < sp2;
+ wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3;
+ wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1;
+ ASSERT_TRUE(wp_smaller < wp_larger);
+ ASSERT_TRUE(wp_smaller != wp_larger);
+ ASSERT_TRUE(wp_smaller <= wp_larger);
+ ASSERT_FALSE(wp_smaller == wp_larger);
+ ASSERT_FALSE(wp_smaller > wp_larger);
+ ASSERT_FALSE(wp_smaller >= wp_larger);
+ sp2 = nullptr;
+ ASSERT_TRUE(isDeleted2);
+ ASSERT_FALSE(isDeleted);
+ ASSERT_FALSE(wp3 == sp2);
+ // Comparison results on weak pointers should not be affected.
+ ASSERT_TRUE(wp_smaller < wp_larger);
+ ASSERT_TRUE(wp_smaller != wp_larger);
+ ASSERT_TRUE(wp_smaller <= wp_larger);
+ ASSERT_FALSE(wp_smaller == wp_larger);
+ ASSERT_FALSE(wp_smaller > wp_larger);
+ ASSERT_FALSE(wp_smaller >= wp_larger);
+ wp2 = nullptr;
+ ASSERT_FALSE(wp1 == wp2);
+ ASSERT_TRUE(wp1 != wp2);
+ wp1.clear();
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_FALSE(wp1 != wp2);
+ wp3.clear();
+ ASSERT_TRUE(wp1 == wp3);
+ ASSERT_FALSE(wp1 != wp3);
+ ASSERT_FALSE(isDeleted);
+ sp1.clear();
+ ASSERT_TRUE(isDeleted);
+ ASSERT_TRUE(sp1 == sp2);
+ // Try to check that null pointers are properly initialized.
+ {
+ // Try once with non-null, to maximize chances of getting junk on the
+ // stack.
+ sp<Foo> sp3(new Foo(&isDeleted3));
+ wp<Foo> wp4(sp3);
+ wp<Foo> wp5;
+ ASSERT_FALSE(wp4 == wp5);
+ ASSERT_TRUE(wp4 != wp5);
+ ASSERT_FALSE(sp3 == wp5);
+ ASSERT_FALSE(wp5 == sp3);
+ ASSERT_TRUE(sp3 != wp5);
+ ASSERT_TRUE(wp5 != sp3);
+ ASSERT_TRUE(sp3 == wp4);
+ }
+ {
+ sp<Foo> sp3;
+ wp<Foo> wp4(sp3);
+ wp<Foo> wp5;
+ ASSERT_TRUE(wp4 == wp5);
+ ASSERT_FALSE(wp4 != wp5);
+ ASSERT_TRUE(sp3 == wp5);
+ ASSERT_TRUE(wp5 == sp3);
+ ASSERT_FALSE(sp3 != wp5);
+ ASSERT_FALSE(wp5 != sp3);
+ ASSERT_TRUE(sp3 == wp4);
+ }
+}
+
+// Check whether comparison against dead wp works, even if the object referenced
+// by the new wp happens to be at the same address.
+TEST(RefBase, ReplacedComparison) {
+ bool isDeleted, isDeleted2;
+ FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted);
+ sp<FooFixedAlloc> sp1(foo);
+ wp<FooFixedAlloc> wp1(sp1);
+ ASSERT_TRUE(wp1 == sp1);
+ sp1.clear(); // Deallocates the object.
+ ASSERT_TRUE(isDeleted);
+ FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2);
+ ASSERT_FALSE(isDeleted2);
+ ASSERT_EQ(foo, foo2); // Not technically a legal comparison, but ...
+ sp<FooFixedAlloc> sp2(foo2);
+ wp<FooFixedAlloc> wp2(sp2);
+ ASSERT_TRUE(sp2 == wp2);
+ ASSERT_FALSE(sp2 != wp2);
+ ASSERT_TRUE(sp2 != wp1);
+ ASSERT_FALSE(sp2 == wp1);
+ ASSERT_FALSE(sp2 == sp1); // sp1 is null.
+ ASSERT_FALSE(wp1 == wp2); // wp1 refers to old object.
+ ASSERT_TRUE(wp1 != wp2);
+ ASSERT_TRUE(wp1 > wp2 || wp1 < wp2);
+ ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2);
+ ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2);
+ ASSERT_FALSE(wp1 == nullptr);
+ wp1 = sp2;
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_FALSE(wp1 != wp2);
+}
// Set up a situation in which we race with visit2AndRremove() to delete
// 2 strong references. Bar destructor checks that there are no early
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 1780cf2..a105474 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -171,6 +171,8 @@
#define ANDROID_REF_BASE_H
#include <atomic>
+#include <functional>
+#include <type_traits> // for common_type.
#include <stdint.h>
#include <sys/types.h>
@@ -192,19 +194,26 @@
// ---------------------------------------------------------------------------
#define COMPARE_WEAK(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
-template<typename U> \
-inline bool operator _op_ (const sp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
template<typename U> \
inline bool operator _op_ (const U* o) const { \
return m_ptr _op_ o; \
+} \
+/* Needed to handle type inference for nullptr: */ \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+}
+
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _wp_compare_(T* a, U* b) {
+ return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_) \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return _wp_compare_<_compare_>(m_ptr, o); \
}
// ---------------------------------------------------------------------------
@@ -354,7 +363,7 @@
public:
typedef typename RefBase::weakref_type weakref_type;
- inline wp() : m_ptr(nullptr) { }
+ inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
wp(T* other); // NOLINT(implicit)
wp(const wp<T>& other);
@@ -395,39 +404,51 @@
COMPARE_WEAK(==)
COMPARE_WEAK(!=)
- COMPARE_WEAK(>)
- COMPARE_WEAK(<)
- COMPARE_WEAK(<=)
- COMPARE_WEAK(>=)
+ COMPARE_WEAK_FUNCTIONAL(>, std::greater)
+ COMPARE_WEAK_FUNCTIONAL(<, std::less)
+ COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
+ COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
- inline bool operator == (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
- }
template<typename U>
inline bool operator == (const wp<U>& o) const {
- return m_ptr == o.m_ptr;
+ return m_refs == o.m_refs; // Implies m_ptr == o.mptr; see invariants below.
}
- inline bool operator > (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ template<typename U>
+ inline bool operator == (const sp<U>& o) const {
+ // Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
+ // object at the same address.
+ if (o == nullptr) {
+ return m_ptr == nullptr;
+ } else {
+ return m_refs == o->getWeakRefs(); // Implies m_ptr == o.mptr.
+ }
}
+
+ template<typename U>
+ inline bool operator != (const sp<U>& o) const {
+ return !(*this == o);
+ }
+
template<typename U>
inline bool operator > (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ if (m_ptr == o.m_ptr) {
+ return _wp_compare_<std::greater>(m_refs, o.m_refs);
+ } else {
+ return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
+ }
}
- inline bool operator < (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
- }
template<typename U>
inline bool operator < (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ if (m_ptr == o.m_ptr) {
+ return _wp_compare_<std::less>(m_refs, o.m_refs);
+ } else {
+ return _wp_compare_<std::less>(m_ptr, o.m_ptr);
+ }
}
- inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
- inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
- inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
private:
@@ -446,11 +467,27 @@
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
+// Implementation invariants:
+// Either
+// 1) m_ptr and m_refs are both null, or
+// 2) m_refs == m_ptr->mRefs, or
+// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
+// to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
+//
+// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
+// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
+// null or point to the same object. If two wp's have identical m_ptr fields, they either both
+// point to the same live object and thus have the same m_ref fields, or at least one of the
+// objects is no longer live.
+//
+// Note that the above comparison operations go out of their way to provide an ordering consistent
+// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
+
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
- if (other) m_refs = other->createWeak(this);
+ m_refs = other ? m_refs = other->createWeak(this) : nullptr;
}
template<typename T>
@@ -464,16 +501,14 @@
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
+ m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
wp<T>::wp(U* other)
: m_ptr(other)
{
- if (other) m_refs = other->createWeak(this);
+ m_refs = other ? other->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
@@ -483,6 +518,8 @@
if (m_ptr) {
m_refs = other.m_refs;
m_refs->incWeak(this);
+ } else {
+ m_refs = nullptr;
}
}
@@ -490,9 +527,7 @@
wp<T>::wp(const sp<U>& other)
: m_ptr(other.m_ptr)
{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
+ m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T>
@@ -595,6 +630,7 @@
{
if (m_ptr) {
m_refs->decWeak(this);
+ m_refs = 0;
m_ptr = 0;
}
}
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 1571129..9cd7c75 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_STRONG_POINTER_H
#define ANDROID_STRONG_POINTER_H
+#include <functional>
+#include <type_traits> // for common_type.
+
// ---------------------------------------------------------------------------
namespace android {
@@ -24,13 +27,12 @@
// ---------------------------------------------------------------------------
-#define COMPARE(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
+// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
+// was created before the sp<>, and they point to different objects, they may
+// compare equal even if they are entirely unrelated. E.g. CameraService
+// currently performa such comparisons.
+
+#define COMPARE_STRONG(_op_) \
template<typename U> \
inline bool operator _op_ (const sp<U>& o) const { \
return m_ptr _op_ o.m_ptr; \
@@ -39,14 +41,27 @@
inline bool operator _op_ (const U* o) const { \
return m_ptr _op_ o; \
} \
-inline bool operator _op_ (const wp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const wp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
+/* Needed to handle type inference for nullptr: */ \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
}
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+ return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return _sp_compare_<_compare_>(m_ptr, o.m_ptr); \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return _sp_compare_<_compare_>(m_ptr, o); \
+}
// ---------------------------------------------------------------------------
template<typename T>
@@ -89,12 +104,23 @@
// Operators
- COMPARE(==)
- COMPARE(!=)
- COMPARE(>)
- COMPARE(<)
- COMPARE(<=)
- COMPARE(>=)
+ COMPARE_STRONG(==)
+ COMPARE_STRONG(!=)
+ COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+ COMPARE_STRONG_FUNCTIONAL(<, std::less)
+ COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+ COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
+
+ // Punt these to the wp<> implementation.
+ template<typename U>
+ inline bool operator == (const wp<U>& o) const {
+ return o == *this;
+ }
+
+ template<typename U>
+ inline bool operator != (const wp<U>& o) const {
+ return o != *this;
+ }
private:
template<typename Y> friend class sp;
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index 821940a..84c2132 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -20,6 +20,8 @@
#define LOG_TAG "vndksupport"
#include <log/log.h>
+#include <sys/types.h>
+#include <unistd.h>
__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
@@ -46,6 +48,11 @@
}
int android_is_in_vendor_process() {
+ // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
+ // loaded (sysprop service isn't up for init to know <ver>).
+ if (getpid() == 1) {
+ return 0;
+ }
if (android_get_exported_namespace == NULL) {
ALOGD("android_get_exported_namespace() not available. Assuming system process.");
return 0;
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 98b3aa1..2d3fbfc 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -110,7 +110,7 @@
*/
#define PSI_WINDOW_SIZE_MS 1000
/* Polling period after initial PSI signal */
-#define PSI_POLL_PERIOD_MS 200
+#define PSI_POLL_PERIOD_MS 40
/* Poll for the duration of one window after initial PSI signal */
#define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)
@@ -1329,7 +1329,7 @@
static int last_killed_pid = -1;
/* Kill one process specified by procp. Returns the size of the process killed */
-static int kill_one_process(struct proc* procp) {
+static int kill_one_process(struct proc* procp, int min_oom_score) {
int pid = procp->pid;
uid_t uid = procp->uid;
char *taskname;
@@ -1340,6 +1340,9 @@
#ifdef LMKD_LOG_STATS
struct memory_stat mem_st = {};
int memory_stat_parse_result = -1;
+#else
+ /* To prevent unused parameter warning */
+ (void)(min_oom_score);
#endif
taskname = proc_get_name(pid);
@@ -1385,10 +1388,12 @@
if (memory_stat_parse_result == 0) {
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
- mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns);
+ mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
+ min_oom_score);
} else if (enable_stats_log) {
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
- -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1);
+ -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
+ min_oom_score);
}
#endif
result = tasksize;
@@ -1425,7 +1430,7 @@
if (!procp)
break;
- killed_size = kill_one_process(procp);
+ killed_size = kill_one_process(procp, min_score_adj);
if (killed_size >= 0) {
#ifdef LMKD_LOG_STATS
if (enable_stats_log && !lmk_state_change_start) {
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index 689e8ae..0c230ae 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -65,7 +65,8 @@
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
char const* process_name, int32_t oom_score, int64_t pgfault,
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes, int64_t process_start_time_ns) {
+ int64_t swap_in_bytes, int64_t process_start_time_ns,
+ int32_t min_oom_score) {
assert(ctx != NULL);
int ret = -EINVAL;
if (!ctx) {
@@ -117,5 +118,9 @@
return ret;
}
+ if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+ return ret;
+ }
+
return write_to_logger(ctx, LOG_ID_STATS);
}
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index f3abe11..2edba7a 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -88,7 +88,8 @@
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
char const* process_name, int32_t oom_score, int64_t pgfault,
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes, int64_t process_start_time_ns);
+ int64_t swap_in_bytes, int64_t process_start_time_ns,
+ int32_t min_oom_score);
__END_DECLS
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
index 6b5d5d0..789bf5e 100755
--- a/mkbootimg/unpack_bootimg.py
+++ b/mkbootimg/unpack_bootimg.py
@@ -48,12 +48,12 @@
print('boot_magic: %s' % boot_magic)
kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
print('kernel_size: %s' % kernel_ramdisk_second_info[0])
- print('kernel load address: %s' % kernel_ramdisk_second_info[1])
+ print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
- print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
+ print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
- print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
- print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
+ print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
+ print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
print('page size: %s' % kernel_ramdisk_second_info[7])
print('boot image header version: %s' % kernel_ramdisk_second_info[8])
print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
@@ -77,7 +77,7 @@
recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
print('recovery dtbo size: %s' % recovery_dtbo_size)
recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
- print('recovery dtbo offset: %s' % recovery_dtbo_offset)
+ print('recovery dtbo offset: %#x' % recovery_dtbo_offset)
boot_header_size = unpack('I', args.boot_img.read(4))[0]
print('boot header size: %s' % boot_header_size)
else:
@@ -86,7 +86,7 @@
dtb_size = unpack('I', args.boot_img.read(4))[0]
print('dtb size: %s' % dtb_size)
dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
- print('dtb address: %s' % dtb_load_address)
+ print('dtb address: %#x' % dtb_load_address)
else:
dtb_size = 0
@@ -103,10 +103,11 @@
) # header + kernel
image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
- second_offset = page_size * (
- num_header_pages + num_kernel_pages + num_ramdisk_pages
- ) # header + kernel + ramdisk
- image_info_list.append((second_offset, second_size, 'second'))
+ if second_size > 0:
+ second_offset = page_size * (
+ num_header_pages + num_kernel_pages + num_ramdisk_pages
+ ) # header + kernel + ramdisk
+ image_info_list.append((second_offset, second_size, 'second'))
if recovery_dtbo_size > 0:
image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d2125d8..54b019e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -24,44 +24,6 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
-# Start of runtime APEX compatibility.
-#
-# Meta-comment:
-# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
-# entries need to be associated with something that goes into /system.
-# init-debug.rc qualifies but it could be anything else in /system until soong
-# supports creation of symlinks. http://b/123333111
-#
-# Keeping the appearance of files/dirs having old locations for apps that have
-# come to rely on them.
-
-# http://b/121248172 - create a link from /system/usr/icu to
-# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
-# A symlink can't overwrite a directory and the /system/usr/icu directory once
-# existed so the required structure must be created whatever we find.
-LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
-LOCAL_POST_INSTALL_CMD += ; ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
-
-# TODO(b/124106384): Clean up compat symlinks for ART binaries.
-ART_BINARIES= \
- dalvikvm \
- dalvikvm32 \
- dalvikvm64 \
- dex2oat \
- dexdiag \
- dexdump \
- dexlist \
- dexoptanalyzer \
- oatdump \
- profman \
-
-$(foreach b,$(ART_BINARIES), \
- $(eval LOCAL_POST_INSTALL_CMD += \
- ; ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
-)
-
-# End of runtime APEX compatibilty.
-
include $(BUILD_PREBUILT)
#######################################
@@ -242,6 +204,45 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+# Start of runtime APEX compatibility.
+#
+# Meta-comment:
+# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
+# entries need to be associated with something that goes into /system.
+# ld.config.txt qualifies but it could be anything else in /system until soong
+# supports creation of symlinks. http://b/123333111
+#
+# Keeping the appearance of files/dirs having old locations for apps that have
+# come to rely on them.
+
+# http://b/121248172 - create a link from /system/usr/icu to
+# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
+# A symlink can't overwrite a directory and the /system/usr/icu directory once
+# existed so the required structure must be created whatever we find.
+LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
+LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+
+# TODO(b/124106384): Clean up compat symlinks for ART binaries.
+ART_BINARIES := \
+ dalvikvm \
+ dalvikvm32 \
+ dalvikvm64 \
+ dex2oat \
+ dexdiag \
+ dexdump \
+ dexlist \
+ dexoptanalyzer \
+ oatdump \
+ profman \
+
+LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
+$(foreach b,$(ART_BINARIES), \
+ $(eval LOCAL_POST_INSTALL_CMD += \
+ && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+)
+
+# End of runtime APEX compatibilty.
+
ifeq ($(_enforce_vndk_at_runtime),true)
# for VNDK enforced devices
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 0cde3f2..fa46334 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -11,6 +11,11 @@
dir.legacy = /odm
dir.legacy = /sbin
+dir.legacy = /data/nativetest
+dir.legacy = /data/nativetest64
+dir.legacy = /data/benchmarktest
+dir.legacy = /data/benchmarktest64
+
# Except for /postinstall, where only /system and /product are searched
dir.postinstall = /postinstall
@@ -40,7 +45,7 @@
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
# shared library cannot be loaded from the runtime namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
@@ -94,11 +99,21 @@
namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
namespace.media.links = default
-namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
-namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs = libandroid.so
namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libdl.so
+namespace.media.link.default.shared_libs += liblog.so
namespace.media.link.default.shared_libs += libmediametrics.so
-namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.media.link.default.shared_libs += libmediandk.so
+namespace.media.link.default.shared_libs += libm.so
+namespace.media.link.default.shared_libs += libvndksupport.so
+
+namespace.media.link.default.shared_libs += libclang_rt.asan-aarch64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-arm-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-i686-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-x86_64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.hwasan-aarch64-android.so
###############################################################################
# "conscrypt" APEX namespace
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 28703d2..641a536 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -68,6 +68,9 @@
namespace.default.permitted.paths += /vendor/framework
namespace.default.permitted.paths += /vendor/app
namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /system/vendor/framework
+namespace.default.permitted.paths += /system/vendor/app
+namespace.default.permitted.paths += /system/vendor/priv-app
namespace.default.permitted.paths += /odm/framework
namespace.default.permitted.paths += /odm/app
namespace.default.permitted.paths += /odm/priv-app
@@ -80,7 +83,7 @@
namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
-namespace.default.permitted.paths += /bionic/${LIB}
+namespace.default.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
namespace.default.permitted.paths += /system/${LIB}/bootstrap
namespace.default.asan.search.paths = /data/asan/system/${LIB}
@@ -100,6 +103,9 @@
namespace.default.asan.permitted.paths += /vendor/framework
namespace.default.asan.permitted.paths += /vendor/app
namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /system/vendor/framework
+namespace.default.asan.permitted.paths += /system/vendor/app
+namespace.default.asan.permitted.paths += /system/vendor/priv-app
namespace.default.asan.permitted.paths += /odm/framework
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
@@ -113,13 +119,13 @@
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
-namespace.default.asan.permitted.paths += /bionic/${LIB}
+namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
# shared library cannot be loaded from the runtime namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
@@ -236,6 +242,7 @@
namespace.sphal.permitted.paths = /odm/${LIB}
namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
namespace.sphal.asan.search.paths += /odm/${LIB}
@@ -278,6 +285,7 @@
namespace.rs.permitted.paths = /odm/${LIB}
namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
namespace.rs.permitted.paths += /data
namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp
@@ -323,6 +331,8 @@
namespace.vndk.permitted.paths += /odm/${LIB}/egl
namespace.vndk.permitted.paths += /vendor/${LIB}/hw
namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
@@ -390,6 +400,7 @@
namespace.default.permitted.paths = /odm
namespace.default.permitted.paths += /vendor
+namespace.default.permitted.paths += /system/vendor
#VNDK27#namespace.default.search.paths += /vendor/${LIB}/hw
#VNDK27#namespace.default.search.paths += /vendor/${LIB}/egl
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index a5beb4e..beb6125 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -66,7 +66,7 @@
# ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
# shared library cannot be loaded from the runtime namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
@@ -184,6 +184,7 @@
namespace.sphal.permitted.paths = /odm/${LIB}
namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
namespace.sphal.asan.search.paths += /odm/${LIB}
@@ -226,6 +227,7 @@
namespace.rs.permitted.paths = /odm/${LIB}
namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
namespace.rs.permitted.paths += /data
namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp
@@ -271,6 +273,7 @@
namespace.vndk.permitted.paths += /odm/${LIB}/egl
namespace.vndk.permitted.paths += /vendor/${LIB}/hw
namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 5d6cd2d..455c9a8 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -6,6 +6,7 @@
export ANDROID_DATA /data
export ANDROID_STORAGE /storage
export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+ export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
export EXTERNAL_STORAGE /sdcard
export ASEC_MOUNTPOINT /mnt/asec
export BOOTCLASSPATH %BOOTCLASSPATH%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ce4b380..8e63a81 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -13,12 +13,6 @@
# Cgroups are mounted right before early-init using list from /etc/cgroups.json
on early-init
- # Mount shared so changes propagate into child namespaces
- # Do this before other processes are started from init. Otherwise,
- # processes launched while the propagation type of / is 'private'
- # won't get mount events from others.
- mount rootfs rootfs / shared rec
-
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
@@ -43,6 +37,11 @@
start ueventd
+ # Run apexd-bootstrap so that APEXes that provide critical libraries
+ # become available. Note that this is executed as exec_start to ensure that
+ # the libraries are available to the processes started after this statement.
+ exec_start apexd-bootstrap
+
on init
sysclktz 0
@@ -278,18 +277,9 @@
write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}
chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}
- # Setup APEX mount point and its security context
- mount tmpfs tmpfs /apex nodev noexec nosuid
- chmod 0755 /apex
- chown root root /apex
- restorecon /apex
-
# Start logd before any other services run to ensure we capture all of their logs.
start logd
- # Start apexd as soon as we can
- start apexd
-
# Start essential services.
start servicemanager
start hwservicemanager
@@ -397,7 +387,10 @@
restorecon_recursive /metadata
mkdir /metadata/vold
chmod 0700 /metadata/vold
+ mkdir /metadata/password_slots 0771 root system
+ mkdir /metadata/apex 0700 root system
+ mkdir /metadata/apex/sessions 0700 root system
on late-fs
# Ensure that tracefs has the correct permissions.
# This does not work correctly if it is called in post-fs.
@@ -425,8 +418,25 @@
mkdir /data/bootchart 0755 shell shell
bootchart start
- # /data/apex is now available. Let apexd to scan and activate APEXes.
- setprop apexd.data.status ready
+ # Load fsverity keys. This needs to happen before apexd, as post-install of
+ # APEXes may rely on keys.
+ exec -- /system/bin/mini-keyctl dadd asymmetric product_cert /product/etc/security/cacerts_fsverity .fs-verity
+ exec -- /system/bin/mini-keyctl dadd asymmetric vendor_cert /vendor/etc/security/cacerts_fsverity .fs-verity
+ # Prevent future key links to fsverity keyring
+ exec -- /system/bin/mini-keyctl restrict_keyring .fs-verity
+ # Enforce fsverity signature checking
+ write /proc/sys/fs/verity/require_signatures 1
+
+ # Make sure that apexd is started in the default namespace
+ enter_default_mount_ns
+
+ # /data/apex is now available. Start apexd to scan and activate APEXes.
+ mkdir /data/apex 0750 root system
+ mkdir /data/apex/active 0750 root system
+ mkdir /data/apex/backup 0700 root system
+ mkdir /data/apex/sessions 0700 root system
+ mkdir /data/app-staging 0750 system system
+ start apexd
# Avoid predictable entropy pool. Carry over entropy from previous boot.
copy /data/system/entropy.dat /dev/urandom
@@ -543,12 +553,6 @@
mkdir /data/anr 0775 system system
- mkdir /data/apex 0750 root system
- mkdir /data/apex/active 0750 root system
- mkdir /data/apex/backup 0700 root system
- mkdir /data/apex/sessions 0700 root system
- mkdir /data/pkg_staging 0750 system system
-
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc
mkdir /data/nfc/param 0770 nfc nfc
@@ -581,22 +585,15 @@
mkdir /data/cache/backup_stage 0700 system system
mkdir /data/cache/backup 0700 system system
+ # Wait for apexd to finish activating APEXes before starting more processes.
+ wait_for_prop apexd.status ready
+ parse_apex_configs
+
init_user0
# Set SELinux security contexts on upgrade or policy update.
restorecon --recursive --skip-ce /data
- # load fsverity keys
- exec -- /system/bin/mini-keyctl -c /product/etc/security/cacerts_fsverity,/vendor/etc/security/cacerts_fsverity -k .fs-verity
-
- # Wait for apexd to finish activating APEXes before starting more processes.
- # This certainly reduces the parallelism but is required to make as many processes
- # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
- # so the impact on the booting time is not significant.
- wait_for_prop apexd.status ready
- setup_runtime_bionic
- parse_apex_configs
-
# Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index e8c5d8e..f8e680d 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 9c7e807..0235370 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -22,6 +21,5 @@
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket blastula_pool_secondary stream 660 root system
- updatable
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 9908c99..3f3cc15 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 0b5edff..fae38c9 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -22,6 +21,5 @@
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket blastula_pool_secondary stream 660 root system
- updatable
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index 8752eef..432c434 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -70,32 +70,40 @@
return true; // Keep searching.
}
-static bool check_directory(const char* path, uid_t uid) {
+static void check_directory(const char* path, uid_t uid) {
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false;
+ if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) {
+ error(1, errno, "couldn't stat %s", path);
+ }
// /data/user/0 is a known safe symlink.
- if (strcmp("/data/user/0", path) == 0) return true;
+ if (strcmp("/data/user/0", path) == 0) return;
// Must be a real directory, not a symlink.
- if (!S_ISDIR(st.st_mode)) return false;
+ if (!S_ISDIR(st.st_mode)) {
+ error(1, 0, "%s not a directory: %o", path, st.st_mode);
+ }
// Must be owned by specific uid/gid.
- if (st.st_uid != uid || st.st_gid != uid) return false;
+ if (st.st_uid != uid || st.st_gid != uid) {
+ error(1, 0, "%s has wrong owner: %d/%d, not %d", path, st.st_uid, st.st_gid, uid);
+ }
// Must not be readable or writable by others.
- if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false;
-
- return true;
+ if ((st.st_mode & (S_IROTH | S_IWOTH)) != 0) {
+ error(1, 0, "%s readable or writable by others: %o", path, st.st_mode);
+ }
}
// This function is used to check the data directory path for safety.
// We check that every sub-directory is owned by the 'system' user
// and exists and is not a symlink. We also check that the full directory
// path is properly owned by the user ID.
-static bool check_data_path(const char* data_path, uid_t uid) {
+static void check_data_path(const char* package_name, const char* data_path, uid_t uid) {
// The path should be absolute.
- if (data_path[0] != '/') return false;
+ if (data_path[0] != '/') {
+ error(1, 0, "%s data path not absolute: %s", package_name, data_path);
+ }
// Look for all sub-paths, we do that by finding
// directory separators in the input path and
@@ -110,26 +118,28 @@
if (data_path[nn+1] == '\0') break;
/* found a separator, check that data_path is not too long. */
- if (nn >= (int)(sizeof subpath)) return false;
+ if (nn >= (int)(sizeof subpath)) {
+ error(1, 0, "%s data path too long: %s", package_name, data_path);
+ }
/* reject any '..' subpath */
if (nn >= 3 &&
data_path[nn-3] == '/' &&
data_path[nn-2] == '.' &&
data_path[nn-1] == '.') {
- return false;
+ error(1, 0, "%s contains '..': %s", package_name, data_path);
}
/* copy to 'subpath', then check ownership */
memcpy(subpath, data_path, nn);
subpath[nn] = '\0';
- if (!check_directory(subpath, AID_SYSTEM)) return false;
+ check_directory(subpath, AID_SYSTEM);
}
// All sub-paths were checked, now verify that the full data
// directory is owned by the application uid.
- return check_directory(data_path, uid);
+ check_directory(data_path, uid);
}
std::vector<gid_t> get_supplementary_gids(uid_t userAppId) {
@@ -222,9 +232,7 @@
}
// Check that the data directory path is valid.
- if (!check_data_path(info.data_dir, userAppId)) {
- error(1, 0, "package has corrupt installation: %s", pkgname);
- }
+ check_data_path(pkgname, info.data_dir, userAppId);
// Ensure that we change all real/effective/saved IDs at the
// same time to avoid nasty surprises.
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index e1de130..2b35819 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -99,14 +100,21 @@
static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
- mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
+ mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
+ bool use_esdfs) {
+ // Add new options at the end of the vector.
+ std::vector<std::string> new_opts_list;
+ if (multi_user) new_opts_list.push_back("multiuser,");
+ if (derive_gid) new_opts_list.push_back("derive_gid,");
+ if (default_normal) new_opts_list.push_back("default_normal,");
+ if (unshared_obb) new_opts_list.push_back("unshared_obb,");
// Try several attempts, each time with one less option, to gracefully
// handle older kernels that aren't updated yet.
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i <= new_opts_list.size(); ++i) {
std::string new_opts;
- if (multi_user && i < 3) new_opts += "multiuser,";
- if (derive_gid && i < 2) new_opts += "derive_gid,";
- if (default_normal && i < 1) new_opts += "default_normal,";
+ for (int j = 0; j < new_opts_list.size() - i; ++j) {
+ new_opts += new_opts_list[j];
+ }
auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
@@ -142,13 +150,14 @@
return true;
}
-static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
- const std::string& dest_path, uid_t fsuid, gid_t fsgid,
- bool multi_user, userid_t userid, gid_t gid, mode_t mask,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+static bool sdcardfs_setup_secondary(const std::string& default_path,
+ const std::string& source_path, const std::string& dest_path,
+ uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid,
+ gid_t gid, mode_t mask, bool derive_gid, bool default_normal,
+ bool unshared_obb, bool use_esdfs) {
if (use_esdfs) {
return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
- derive_gid, default_normal, use_esdfs);
+ derive_gid, default_normal, unshared_obb, use_esdfs);
} else {
return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
}
@@ -156,7 +165,7 @@
static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
gid_t gid, userid_t userid, bool multi_user, bool full_write,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+ bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
std::string dest_path_default = "/mnt/runtime/default/" + label;
std::string dest_path_read = "/mnt/runtime/read/" + label;
std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -167,16 +176,17 @@
// Multi-user storage is fully isolated per user, so "other"
// permissions are completely masked off.
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
- default_normal, use_esdfs) ||
+ default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
- default_normal, use_esdfs)) {
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
} else {
@@ -184,16 +194,17 @@
// the Android directories are masked off to a single user
// deep inside attr_from_stat().
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
- default_normal, use_esdfs)) {
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
}
@@ -216,7 +227,8 @@
<< " -U: specify user ID that owns device"
<< " -m: source_path is multi-user"
<< " -w: runtime write mount has full write access"
- << " -P preserve owners on the lower file system";
+ << " -P: preserve owners on the lower file system"
+ << " -o: obb dir doesn't need to be shared between users";
return 1;
}
@@ -230,6 +242,7 @@
bool full_write = false;
bool derive_gid = false;
bool default_normal = false;
+ bool unshared_obb = false;
int i;
struct rlimit rlim;
int fs_version;
@@ -238,7 +251,7 @@
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
int opt;
- while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:U:mwGio")) != -1) {
switch (opt) {
case 'u':
uid = strtoul(optarg, NULL, 10);
@@ -261,8 +274,12 @@
case 'i':
default_normal = true;
break;
+ case 'o':
+ unshared_obb = true;
+ break;
case '?':
default:
+ LOG(ERROR) << "Unknown option: '" << opt << "'";
return usage();
}
}
@@ -304,6 +321,6 @@
}
run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
- default_normal, !should_use_sdcardfs());
+ default_normal, unshared_obb, !should_use_sdcardfs());
return 1;
}
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f08cf93..7ad6f1c 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -59,6 +59,7 @@
name: "r",
defaults: ["toolbox_defaults"],
srcs: ["r.c"],
+ vendor_available: true,
}
// We build BSD grep separately (but see http://b/111849261).
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 41263e5..c61f7d0 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -17,8 +17,8 @@
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/stat.h>
@@ -34,28 +34,38 @@
#define REQ_BUFFER_SIZE 4096
static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
-static const char *ss_data_root;
-static const char *trusty_devname;
-static const char *rpmb_devname;
-static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+static const char* ss_data_root;
+static const char* trusty_devname;
+static const char* rpmb_devname;
+static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
-static const char *_sopts = "hp:d:r:";
-static const struct option _lopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"trusty_dev", required_argument, NULL, 'd'},
- {"data_path", required_argument, NULL, 'p'},
- {"rpmb_dev", required_argument, NULL, 'r'},
- {0, 0, 0, 0}
-};
+static enum dev_type dev_type = MMC_RPMB;
-static void show_usage_and_exit(int code)
-{
- ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+static enum dev_type parse_dev_type(const char* dev_type_name) {
+ if (!strcmp(dev_type_name, "mmc")) {
+ return MMC_RPMB;
+ } else if (!strcmp(dev_type_name, "virt")) {
+ return VIRT_RPMB;
+ } else {
+ return UNKNOWN_RPMB;
+ }
+}
+
+static const char* _sopts = "hp:d:r:t:";
+static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
+ {"trusty_dev", required_argument, NULL, 'd'},
+ {"data_path", required_argument, NULL, 'p'},
+ {"rpmb_dev", required_argument, NULL, 'r'},
+ {"dev_type", required_argument, NULL, 't'},
+ {0, 0, 0, 0}};
+
+static void show_usage_and_exit(int code) {
+ ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+ ALOGE("Available dev types: mmc, virt\n");
exit(code);
}
-static int drop_privs(void)
-{
+static int drop_privs(void) {
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];
@@ -95,12 +105,10 @@
return 0;
}
-static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
-{
+static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
int rc;
- if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
- (msg->cmd != STORAGE_RPMB_SEND)) {
+ if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
/*
* handling post commit messages on non rpmb commands are not
* implemented as there is no use case for this yet.
@@ -119,42 +127,42 @@
}
switch (msg->cmd) {
- case STORAGE_FILE_DELETE:
- rc = storage_file_delete(msg, req, req_len);
- break;
+ case STORAGE_FILE_DELETE:
+ rc = storage_file_delete(msg, req, req_len);
+ break;
- case STORAGE_FILE_OPEN:
- rc = storage_file_open(msg, req, req_len);
- break;
+ case STORAGE_FILE_OPEN:
+ rc = storage_file_open(msg, req, req_len);
+ break;
- case STORAGE_FILE_CLOSE:
- rc = storage_file_close(msg, req, req_len);
- break;
+ case STORAGE_FILE_CLOSE:
+ rc = storage_file_close(msg, req, req_len);
+ break;
- case STORAGE_FILE_WRITE:
- rc = storage_file_write(msg, req, req_len);
- break;
+ case STORAGE_FILE_WRITE:
+ rc = storage_file_write(msg, req, req_len);
+ break;
- case STORAGE_FILE_READ:
- rc = storage_file_read(msg, req, req_len);
- break;
+ case STORAGE_FILE_READ:
+ rc = storage_file_read(msg, req, req_len);
+ break;
- case STORAGE_FILE_GET_SIZE:
- rc = storage_file_get_size(msg, req, req_len);
- break;
+ case STORAGE_FILE_GET_SIZE:
+ rc = storage_file_get_size(msg, req, req_len);
+ break;
- case STORAGE_FILE_SET_SIZE:
- rc = storage_file_set_size(msg, req, req_len);
- break;
+ case STORAGE_FILE_SET_SIZE:
+ rc = storage_file_set_size(msg, req, req_len);
+ break;
- case STORAGE_RPMB_SEND:
- rc = rpmb_send(msg, req, req_len);
- break;
+ case STORAGE_RPMB_SEND:
+ rc = rpmb_send(msg, req, req_len);
+ break;
- default:
- ALOGE("unhandled command 0x%x\n", msg->cmd);
- msg->result = STORAGE_ERR_UNIMPLEMENTED;
- rc = 1;
+ default:
+ ALOGE("unhandled command 0x%x\n", msg->cmd);
+ msg->result = STORAGE_ERR_UNIMPLEMENTED;
+ rc = 1;
}
if (rc > 0) {
@@ -164,58 +172,58 @@
return rc;
}
-static int proxy_loop(void)
-{
+static int proxy_loop(void) {
ssize_t rc;
struct storage_msg msg;
/* enter main message handling loop */
while (true) {
-
/* get incoming message */
rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
- if (rc < 0)
- return rc;
+ if (rc < 0) return rc;
/* handle request */
req_buffer[rc] = 0; /* force zero termination */
rc = handle_req(&msg, req_buffer, rc);
- if (rc)
- return rc;
+ if (rc) return rc;
}
return 0;
}
-static void parse_args(int argc, char *argv[])
-{
+static void parse_args(int argc, char* argv[]) {
int opt;
int oidx = 0;
while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
switch (opt) {
+ case 'd':
+ trusty_devname = strdup(optarg);
+ break;
- case 'd':
- trusty_devname = strdup(optarg);
- break;
+ case 'p':
+ ss_data_root = strdup(optarg);
+ break;
- case 'p':
- ss_data_root = strdup(optarg);
- break;
+ case 'r':
+ rpmb_devname = strdup(optarg);
+ break;
- case 'r':
- rpmb_devname = strdup(optarg);
- break;
+ case 't':
+ dev_type = parse_dev_type(optarg);
+ if (dev_type == UNKNOWN_RPMB) {
+ ALOGE("Unrecognized dev type: %s\n", optarg);
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+ break;
- default:
- ALOGE("unrecognized option (%c):\n", opt);
- show_usage_and_exit(EXIT_FAILURE);
+ default:
+ ALOGE("unrecognized option (%c):\n", opt);
+ show_usage_and_exit(EXIT_FAILURE);
}
}
- if (ss_data_root == NULL ||
- trusty_devname == NULL ||
- rpmb_devname == NULL) {
+ if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {
ALOGE("missing required argument(s)\n");
show_usage_and_exit(EXIT_FAILURE);
}
@@ -226,31 +234,26 @@
ALOGI("rpmb dev: %s\n", rpmb_devname);
}
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
int rc;
/* drop privileges */
- if (drop_privs() < 0)
- return EXIT_FAILURE;
+ if (drop_privs() < 0) return EXIT_FAILURE;
/* parse arguments */
parse_args(argc, argv);
/* initialize secure storage directory */
rc = storage_init(ss_data_root);
- if (rc < 0)
- return EXIT_FAILURE;
+ if (rc < 0) return EXIT_FAILURE;
/* open rpmb device */
- rc = rpmb_open(rpmb_devname);
- if (rc < 0)
- return EXIT_FAILURE;
+ rc = rpmb_open(rpmb_devname, dev_type);
+ if (rc < 0) return EXIT_FAILURE;
/* connect to Trusty secure storage server */
rc = ipc_connect(trusty_devname, ss_srv_name);
- if (rc < 0)
- return EXIT_FAILURE;
+ if (rc < 0) return EXIT_FAILURE;
/* enter main loop */
rc = proxy_loop();
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 9c79105..29827e2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -51,17 +51,16 @@
static int rpmb_fd = -1;
static uint8_t read_buf[4096];
+static enum dev_type dev_type = UNKNOWN_RPMB;
#ifdef RPMB_DEBUG
-static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
-{
+static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
size_t i;
printf("%s @%p [%zu]", prefix, buf, size);
for (i = 0; i < size; i++) {
- if (i && i % 32 == 0)
- printf("\n%*s", (int) strlen(prefix), "");
+ if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
printf(" %02x", buf[i]);
}
printf("\n");
@@ -70,41 +69,16 @@
#endif
-
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
-{
- int rc;
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
struct mmc_ioc_cmd cmd_buf[3];
} mmc = {};
- struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
- const struct storage_rpmb_send_req *req = r;
+ struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
+ int rc;
- if (req_len < sizeof(*req)) {
- ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
- req_len, sizeof(*req));
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
- size_t expected_len =
- sizeof(*req) + req->reliable_write_size + req->write_size;
- if (req_len != expected_len) {
- ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
- req_len, expected_len);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
- const uint8_t *write_buf = req->payload;
+ const uint8_t* write_buf = req->payload;
if (req->reliable_write_size) {
- if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_RELW;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -121,12 +95,6 @@
}
if (req->write_size) {
- if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid write size %u\n", req->write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_W;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -143,17 +111,9 @@
}
if (req->read_size) {
- if (req->read_size % MMC_BLOCK_SIZE != 0 ||
- req->read_size > sizeof(read_buf)) {
- ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_R;
cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
- cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
- cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;
cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
mmc_ioc_cmd_set_data((*cmd), read_buf);
#ifdef RPMB_DEBUG
@@ -163,15 +123,97 @@
cmd++;
}
- rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+ rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
if (rc < 0) {
ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ }
+ return rc;
+}
+
+static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
+ size_t payload_size) {
+ int rc;
+ uint16_t res_count = read_size / MMC_BLOCK_SIZE;
+ uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;
+ rc = write(rpmb_fd, &res_count, sizeof(res_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, payload, payload_size);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = read(rpmb_fd, read_buf, read_size);
+ return rc;
+}
+
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+ int rc;
+ const struct storage_rpmb_send_req* req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
+ if (req_len != expected_len) {
+ ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid write size %u\n", req->write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
+ ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (dev_type == MMC_RPMB) {
+ rc = send_mmc_rpmb_req(rpmb_fd, req);
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else if (dev_type == VIRT_RPMB) {
+ size_t payload_size = req->reliable_write_size + req->write_size;
+ rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
+ if (rc < 0) {
+ ALOGE("send_virt_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ if (rc != req->read_size) {
+ ALOGE("send_virt_rpmb_req got incomplete response: "
+ "(size %d, expected %d)\n",
+ rc, req->read_size);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else {
+ ALOGE("Unsupported dev_type\n");
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
#ifdef RPMB_DEBUG
- if (req->read_size)
- print_buf("response: ", read_buf, req->read_size);
+ if (req->read_size) print_buf("response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
@@ -188,24 +230,20 @@
return ipc_respond(msg, NULL, 0);
}
-
-int rpmb_open(const char *rpmb_devname)
-{
+int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
int rc;
+ dev_type = open_dev_type;
rc = open(rpmb_devname, O_RDWR, 0);
if (rc < 0) {
- ALOGE("unable (%d) to open rpmb device '%s': %s\n",
- errno, rpmb_devname, strerror(errno));
+ ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
return rc;
}
rpmb_fd = rc;
return 0;
}
-void rpmb_close(void)
-{
+void rpmb_close(void) {
close(rpmb_fd);
rpmb_fd = -1;
}
-
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 85cff44..4c330c9 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,8 @@
#include <stdint.h>
#include <trusty/interface/storage.h>
-int rpmb_open(const char *rpmb_devname);
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+
+int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
void rpmb_close(void);