Snap for 5475808 from 220241f2668c7e052dcc34fd66e352a78c06e791 to sdk-release
Change-Id: Ib5d310764f17082aa6bee9839dc7424ff7517305
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/TEST_MAPPING b/TEST_MAPPING
index cc85408..716378b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,9 @@
"name": "fs_mgr_unit_test"
},
{
+ "name": "fs_mgr_vendor_overlay_test"
+ },
+ {
"name": "init_tests"
},
{
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/adb.cpp b/adb/adb.cpp
index a5b2f7b..e417f05 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1018,9 +1018,10 @@
return 0;
}
-bool handle_host_request(const char* service, TransportType type, const char* serial,
- TransportId transport_id, int reply_fd, asocket* s) {
- if (strcmp(service, "kill") == 0) {
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+ const char* serial, TransportId transport_id, int reply_fd,
+ asocket* s) {
+ if (service == "kill") {
fprintf(stderr, "adb server killed by remote request\n");
fflush(stdout);
@@ -1032,29 +1033,49 @@
exit(0);
}
- // "transport:" is used for switching transport with a specified serial number
- // "transport-usb:" is used for switching transport to the only USB transport
- // "transport-local:" is used for switching transport to the only local transport
- // "transport-any:" is used for switching transport to the only transport
- if (!strncmp(service, "transport", strlen("transport"))) {
+ LOG(DEBUG) << "handle_host_request(" << service << ")";
+
+ // Transport selection:
+ if (service.starts_with("transport") || service.starts_with("tport:")) {
TransportType type = kTransportAny;
- if (!strncmp(service, "transport-id:", strlen("transport-id:"))) {
- service += strlen("transport-id:");
- transport_id = strtoll(service, const_cast<char**>(&service), 10);
- if (*service != '\0') {
- SendFail(reply_fd, "invalid transport id");
- return true;
+ std::string serial_storage;
+ bool legacy = true;
+
+ // New transport selection protocol:
+ // This is essentially identical to the previous version, except it returns the selected
+ // transport id to the caller as well.
+ if (ConsumePrefix(&service, "tport:")) {
+ legacy = false;
+ if (ConsumePrefix(&service, "serial:")) {
+ serial_storage = service;
+ serial = serial_storage.c_str();
+ } else if (service == "usb") {
+ type = kTransportUsb;
+ } else if (service == "local") {
+ type = kTransportLocal;
+ } else if (service == "any") {
+ type = kTransportAny;
}
- } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
- type = kTransportUsb;
- } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
- type = kTransportLocal;
- } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
- type = kTransportAny;
- } else if (!strncmp(service, "transport:", strlen("transport:"))) {
- service += strlen("transport:");
- serial = service;
+
+ // Selection by id is unimplemented, since you obviously already know the transport id
+ // you're connecting to.
+ } else {
+ if (ConsumePrefix(&service, "transport-id:")) {
+ if (!ParseUint(&transport_id, service)) {
+ SendFail(reply_fd, "invalid transport id");
+ return HostRequestResult::Handled;
+ }
+ } else if (service == "transport-usb") {
+ type = kTransportUsb;
+ } else if (service == "transport-local") {
+ type = kTransportLocal;
+ } else if (service == "transport-any") {
+ type = kTransportAny;
+ } else if (ConsumePrefix(&service, "transport:")) {
+ serial_storage = service;
+ serial = serial_storage.c_str();
+ }
}
std::string error;
@@ -1063,27 +1084,29 @@
s->transport = t;
SendOkay(reply_fd);
- // We succesfully handled the device selection, but there's another request coming.
- return false;
+ if (!legacy) {
+ // Nothing we can do if this fails.
+ WriteFdExactly(reply_fd, &t->id, sizeof(t->id));
+ }
+
+ return HostRequestResult::SwitchedTransport;
} else {
SendFail(reply_fd, error);
- return true;
+ return HostRequestResult::Handled;
}
}
// return a list of all connected devices
- if (!strncmp(service, "devices", 7)) {
- bool long_listing = (strcmp(service+7, "-l") == 0);
- if (long_listing || service[7] == 0) {
- D("Getting device list...");
- std::string device_list = list_transports(long_listing);
- D("Sending device list...");
- SendOkay(reply_fd, device_list);
- }
- return true;
+ if (service == "devices" || service == "devices-l") {
+ bool long_listing = service == "devices-l";
+ D("Getting device list...");
+ std::string device_list = list_transports(long_listing);
+ D("Sending device list...");
+ SendOkay(reply_fd, device_list);
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "reconnect-offline")) {
+ if (service == "reconnect-offline") {
std::string response;
close_usb_devices([&response](const atransport* transport) {
if (!ConnectionStateIsOnline(transport->GetConnectionState())) {
@@ -1091,15 +1114,15 @@
return true;
}
return false;
- });
+ }, true);
if (!response.empty()) {
response.resize(response.size() - 1);
}
SendOkay(reply_fd, response);
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "features")) {
+ if (service == "features") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t != nullptr) {
@@ -1107,10 +1130,10 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "host-features")) {
+ if (service == "host-features") {
FeatureSet features = supported_features();
// Abuse features to report libusb status.
if (should_use_libusb()) {
@@ -1118,16 +1141,16 @@
}
features.insert(kFeaturePushSync);
SendOkay(reply_fd, FeatureSetToString(features));
- return true;
+ return HostRequestResult::Handled;
}
// remove TCP transport
- if (!strncmp(service, "disconnect:", 11)) {
- const std::string address(service + 11);
+ if (service.starts_with("disconnect:")) {
+ std::string address(service.substr(11));
if (address.empty()) {
kick_all_tcp_devices();
SendOkay(reply_fd, "disconnected everything");
- return true;
+ return HostRequestResult::Handled;
}
std::string serial;
@@ -1139,26 +1162,26 @@
} else if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
address.c_str(), error.c_str()));
- return true;
+ return HostRequestResult::Handled;
}
atransport* t = find_transport(serial.c_str());
if (t == nullptr) {
SendFail(reply_fd, android::base::StringPrintf("no such device '%s'", serial.c_str()));
- return true;
+ return HostRequestResult::Handled;
}
kick_transport(t);
SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
- return true;
+ return HostRequestResult::Handled;
}
// Returns our value for ADB_SERVER_VERSION.
- if (!strcmp(service, "version")) {
+ if (service == "version") {
SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
- return true;
+ return HostRequestResult::Handled;
}
// These always report "unknown" rather than the actual error, for scripts.
- if (!strcmp(service, "get-serialno")) {
+ if (service == "get-serialno") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
@@ -1166,9 +1189,9 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "get-devpath")) {
+ if (service == "get-devpath") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
@@ -1176,9 +1199,9 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "get-state")) {
+ if (service == "get-state") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
@@ -1186,39 +1209,46 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
// Indicates a new emulator instance has started.
- if (!strncmp(service, "emulator:", 9)) {
- int port = atoi(service+9);
- local_connect(port);
+ if (ConsumePrefix(&service, "emulator:")) {
+ unsigned int port;
+ if (!ParseUint(&port, service)) {
+ LOG(ERROR) << "received invalid port for emulator: " << service;
+ } else {
+ local_connect(port);
+ }
+
/* we don't even need to send a reply */
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "reconnect")) {
+ if (service == "reconnect") {
std::string response;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
if (t != nullptr) {
- kick_transport(t);
+ kick_transport(t, true);
response =
- "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
+ "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
}
SendOkay(reply_fd, response);
- return true;
+ return HostRequestResult::Handled;
}
- if (handle_forward_request(service,
- [=](std::string* error) {
- return acquire_one_transport(type, serial, transport_id, nullptr,
- error);
- },
- reply_fd)) {
- return true;
+ // TODO: Switch handle_forward_request to string_view.
+ std::string service_str(service);
+ if (handle_forward_request(
+ service_str.c_str(),
+ [=](std::string* error) {
+ return acquire_one_transport(type, serial, transport_id, nullptr, error);
+ },
+ reply_fd)) {
+ return HostRequestResult::Handled;
}
- return false;
+ return HostRequestResult::Unhandled;
}
static auto& init_mutex = *new std::mutex();
diff --git a/adb/adb.h b/adb/adb.h
index 9209997..c60dcbc 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -59,7 +59,7 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 40
+#define ADB_SERVER_VERSION 41
using TransportId = uint64_t;
class atransport;
@@ -145,7 +145,8 @@
#endif
#if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+ TransportId transport_id);
#endif
#if !ADB_HOST
@@ -153,7 +154,7 @@
#endif
#if !ADB_HOST
-unique_fd execute_binder_command(std::string_view command);
+unique_fd execute_abb_command(std::string_view command);
#endif
#if !ADB_HOST
@@ -218,8 +219,15 @@
#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
#endif
-bool handle_host_request(const char* service, TransportType type, const char* serial,
- TransportId transport_id, int reply_fd, asocket* s);
+enum class HostRequestResult {
+ Handled,
+ SwitchedTransport,
+ Unhandled,
+};
+
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+ const char* serial, TransportId transport_id, int reply_fd,
+ asocket* s);
void handle_online(atransport* t);
void handle_offline(atransport* t);
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 605d27d..f5cdcb5 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -34,7 +34,7 @@
#include "adb_utils.h"
#include "sysdeps.h"
-bool SendProtocolString(int fd, const std::string& s) {
+bool SendProtocolString(int fd, std::string_view s) {
unsigned int length = s.size();
if (length > MAX_PAYLOAD - 4) {
errno = EMSGSIZE;
@@ -69,7 +69,7 @@
return WriteFdExactly(fd, "OKAY", 4);
}
-bool SendFail(int fd, const std::string& reason) {
+bool SendFail(int fd, std::string_view reason) {
return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
}
@@ -187,79 +187,3 @@
return false;
}
}
-
-#if defined(__linux__)
-bool SendFileDescriptor(int socket_fd, int fd) {
- struct msghdr msg;
- struct iovec iov;
- char dummy = '!';
- union {
- cmsghdr cm;
- char buffer[CMSG_SPACE(sizeof(int))];
- } cm_un;
-
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = cm_un.buffer;
- msg.msg_controllen = sizeof(cm_un.buffer);
-
- cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)CMSG_DATA(cmsg))[0] = fd;
-
- int ret = TEMP_FAILURE_RETRY(sendmsg(socket_fd, &msg, 0));
- if (ret < 0) {
- D("sending file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
- return false;
- }
-
- D("sent file descriptor %d to via socket %d", fd, socket_fd);
- return true;
-}
-
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error) {
- char dummy = '!';
- union {
- cmsghdr cm;
- char buffer[CMSG_SPACE(sizeof(int))];
- } cm_un;
-
- iovec iov;
- iov.iov_base = &dummy;
- iov.iov_len = 1;
-
- msghdr msg;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = cm_un.buffer;
- msg.msg_controllen = sizeof(cm_un.buffer);
-
- cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)(CMSG_DATA(cmsg)))[0] = -1;
-
- int rc = TEMP_FAILURE_RETRY(recvmsg(socket_fd, &msg, 0));
- if (rc <= 0) {
- *error = perror_str("receiving file descriptor via socket failed");
- D("receiving file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
- return false;
- }
-
- fd->reset(((int*)(CMSG_DATA(cmsg)))[0]);
- D("received file descriptor %d to via socket %d", fd->get(), socket_fd);
-
- return true;
-}
-#endif
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 2ccaa32..d6e65d8 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <string>
+#include <string_view>
#include "adb_unique_fd.h"
@@ -27,10 +28,10 @@
bool SendOkay(int fd);
// Sends the protocol "FAIL" message, with the given failure reason.
-bool SendFail(int fd, const std::string& reason);
+bool SendFail(int fd, std::string_view reason);
// Writes a protocol-format string; a four hex digit length followed by the string data.
-bool SendProtocolString(int fd, const std::string& s);
+bool SendProtocolString(int fd, std::string_view s);
// Reads a protocol-format string; a four hex digit length followed by the string data.
bool ReadProtocolString(int fd, std::string* s, std::string* error);
@@ -74,13 +75,4 @@
// Same as above, but formats the string to send.
bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
-
-#if !ADB_HOST
-// Sends an FD via Unix domain socket.
-bool SendFileDescriptor(int socket_fd, int fd);
-
-// Receives an FD via Unix domain socket.
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error);
-#endif
-
#endif /* ADB_IO_H */
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 6960345..9791769 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -312,7 +312,7 @@
std::string android_dir = user_dir + OS_PATH_SEPARATOR + ".android";
struct stat buf;
if (stat(android_dir.c_str(), &buf) == -1) {
- if (adb_mkdir(android_dir.c_str(), 0750) == -1) {
+ if (adb_mkdir(android_dir, 0750) == -1) {
PLOG(FATAL) << "Cannot mkdir '" << android_dir << "'";
}
}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index a85ca8c..5800a62 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -110,7 +110,7 @@
// Base-10 stroll on a string_view.
template <typename T>
-inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining) {
+inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
if (str.empty() || !isdigit(str[0])) {
return false;
}
@@ -135,6 +135,17 @@
*result = value;
if (remaining) {
*remaining = str.substr(it - str.begin());
+ } else {
+ return it == str.end();
}
+
return true;
}
+
+inline bool ConsumePrefix(std::string_view* str, std::string_view prefix) {
+ if (str->starts_with(prefix)) {
+ str->remove_prefix(prefix.size());
+ return true;
+ }
+ return false;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 8518e17..bd676c2 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -206,6 +206,14 @@
EXPECT_EQ(remaining, "foo");
}
}
+
+ // With trailing text, without remaining.
+ {
+ std::string text = std::string(string) + "foo";
+ uint32_t value;
+ bool success = ParseUint(&value, text, nullptr);
+ EXPECT_EQ(success, false);
+ }
}
TEST(adb_utils, ParseUint) {
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/bugreport_test.cpp b/adb/bugreport_test.cpp
index 72ca59a..a6be203 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -136,7 +136,7 @@
void ExpectBugreportzVersion(const std::string& version) {
EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
- .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())),
+ .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version)),
WithArg<2>(ReturnCallbackDone(0))));
}
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 0a09d1e..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>
@@ -70,46 +72,60 @@
__adb_server_socket_spec = socket_spec;
}
-static int switch_socket_transport(int fd, std::string* error) {
+static std::optional<TransportId> switch_socket_transport(int fd, std::string* error) {
+ TransportId result;
+ bool read_transport = true;
+
std::string service;
if (__adb_transport_id) {
+ read_transport = false;
service += "host:transport-id:";
service += std::to_string(__adb_transport_id);
+ result = __adb_transport_id;
} else if (__adb_serial) {
- service += "host:transport:";
+ service += "host:tport:serial:";
service += __adb_serial;
} else {
const char* transport_type = "???";
switch (__adb_transport) {
case kTransportUsb:
- transport_type = "transport-usb";
- break;
+ transport_type = "usb";
+ break;
case kTransportLocal:
- transport_type = "transport-local";
- break;
+ transport_type = "local";
+ break;
case kTransportAny:
- transport_type = "transport-any";
- break;
+ transport_type = "any";
+ break;
case kTransportHost:
// no switch necessary
return 0;
}
- service += "host:";
+ service += "host:tport:";
service += transport_type;
}
if (!SendProtocolString(fd, service)) {
*error = perror_str("write failure during connection");
- return -1;
+ return std::nullopt;
}
- D("Switch transport in progress");
+
+ LOG(DEBUG) << "Switch transport in progress: " << service;
if (!adb_status(fd, error)) {
D("Switch transport failed: %s", error->c_str());
- return -1;
+ return std::nullopt;
}
+
+ if (read_transport) {
+ if (!ReadFdExactly(fd, &result, sizeof(result))) {
+ *error = "failed to read transport id from server";
+ return std::nullopt;
+ }
+ }
+
D("Switch transport success");
- return 0;
+ return result;
}
bool adb_status(int fd, std::string* error) {
@@ -133,11 +149,10 @@
return false;
}
-static int _adb_connect(const std::string& service, std::string* error) {
- D("_adb_connect: %s", service.c_str());
+static int _adb_connect(std::string_view service, TransportId* transport, std::string* error) {
+ LOG(DEBUG) << "_adb_connect: " << service;
if (service.empty() || service.size() > MAX_PAYLOAD) {
- *error = android::base::StringPrintf("bad service name length (%zd)",
- service.size());
+ *error = android::base::StringPrintf("bad service name length (%zd)", service.size());
return -1;
}
@@ -149,8 +164,15 @@
return -2;
}
- if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd.get(), error)) {
- return -1;
+ if (!service.starts_with("host")) {
+ std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
+ if (!transport_result) {
+ return -1;
+ }
+
+ if (transport) {
+ *transport = *transport_result;
+ }
}
if (!SendProtocolString(fd.get(), service)) {
@@ -190,15 +212,30 @@
return true;
}
-int adb_connect(const std::string& service, std::string* error) {
- // first query the adb server's version
- unique_fd fd(_adb_connect("host:version", error));
+int adb_connect(std::string_view service, std::string* error) {
+ return adb_connect(nullptr, service, error);
+}
- D("adb_connect: service %s", service.c_str());
- if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
+#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));
+
+ 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:
@@ -208,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");
}
@@ -216,13 +253,13 @@
// Fall through to _adb_connect.
} else {
// If a server is already running, check its version matches.
- int version = ADB_SERVER_VERSION - 1;
+ int version = 0;
// If we have a file descriptor, then parse version result.
if (fd >= 0) {
std::string version_string;
if (!ReadProtocolString(fd, &version_string, error)) {
- return -1;
+ return false;
}
ReadOrderlyShutdown(fd);
@@ -230,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();
@@ -249,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, error));
+ unique_fd fd(_adb_connect(service, transport, error));
if (fd == -1) {
D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
@@ -265,7 +347,6 @@
return fd.release();
}
-
bool adb_command(const std::string& service) {
std::string error;
unique_fd fd(adb_connect(service, &error));
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index d467539..8d32c93 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -20,11 +20,20 @@
#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(const std::string& service, std::string* _Nonnull error);
+int adb_connect(std::string_view service, std::string* _Nonnull error);
+
+// Same as above, except returning the TransportId for the service that we've connected to.
+int adb_connect(TransportId* _Nullable id, std::string_view service, std::string* _Nonnull error);
// Kill the currently running adb server, if it exists.
bool adb_kill_server();
@@ -62,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/auth.cpp b/adb/client/auth.cpp
index bcb829b..3eee426 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -416,7 +416,7 @@
#endif
for (const std::string& path : key_paths) {
- load_keys(path.c_str());
+ load_keys(path);
}
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index f70b480..43a3e5e 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -190,8 +190,8 @@
"scripting:\n"
" wait-for[-TRANSPORT]-STATE\n"
" wait for device to be in the given state\n"
- " State: device, recovery, sideload, or bootloader\n"
- " Transport: usb, local, or any [default=any]\n"
+ " STATE: device, recovery, sideload, bootloader, or disconnect\n"
+ " TRANSPORT: usb, local, or any [default=any]\n"
" get-state print offline | bootloader | device\n"
" get-serialno print <serial-number>\n"
" get-devpath print <device-path>\n"
@@ -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);
@@ -1031,22 +1037,31 @@
}
if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
- components[3] != "recovery" && components[3] != "sideload") {
+ components[3] != "recovery" && components[3] != "sideload" &&
+ components[3] != "disconnect") {
fprintf(stderr,
"adb: unknown state %s; "
- "expected 'any', 'bootloader', 'device', 'recovery', or 'sideload'\n",
+ "expected 'any', 'bootloader', 'device', 'recovery', 'sideload', or 'disconnect'\n",
components[3].c_str());
return false;
}
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);
}
static bool adb_root(const char* command) {
std::string error;
- unique_fd fd(adb_connect(android::base::StringPrintf("%s:", command), &error));
+ TransportId transport_id;
+ unique_fd fd(adb_connect(&transport_id, android::base::StringPrintf("%s:", command), &error));
if (fd < 0) {
fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
return false;
@@ -1079,9 +1094,22 @@
return true;
}
- // Give adbd some time to kill itself and come back up.
- // We can't use wait-for-device because devices (e.g. adb over network) might not come back.
- std::this_thread::sleep_for(3s);
+ // 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;
}
@@ -1278,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;
@@ -1369,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 */
@@ -1408,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') {
@@ -1544,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/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index b8827ef..5d10238 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -681,9 +681,7 @@
if (sync) {
struct stat st;
if (sync_lstat(sc, rpath, &st)) {
- // For links, we cannot update the atime/mtime.
- if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
- (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+ if (st.st_mtime == static_cast<time_t>(mtime)) {
sc.RecordFilesSkipped(1);
return true;
}
@@ -921,12 +919,8 @@
for (copyinfo& ci : file_list) {
struct stat st;
if (sc.FinishStat(&st)) {
- if (st.st_size == static_cast<off_t>(ci.size)) {
- // For links, we cannot update the atime/mtime.
- if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
- (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
- ci.skip = true;
- }
+ if (st.st_size == static_cast<off_t>(ci.size) && st.st_mtime == ci.time) {
+ ci.skip = true;
}
}
}
@@ -1018,7 +1012,7 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
+ success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1134,7 +1128,7 @@
// Recursively build the list of files to copy.
sc.Printf("pull: building file list...");
std::vector<copyinfo> file_list;
- if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
+ if (!remote_build_list(sc, &file_list, rpath, lpath)) {
return false;
}
@@ -1246,7 +1240,7 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
+ success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs);
continue;
} else if (!should_pull_file(src_st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
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/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index ce57731..f55ae90 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -52,6 +52,11 @@
: native::usb_close(reinterpret_cast<native::usb_handle*>(h));
}
+void usb_reset(usb_handle* h) {
+ should_use_libusb() ? libusb::usb_reset(reinterpret_cast<libusb::usb_handle*>(h))
+ : native::usb_reset(reinterpret_cast<native::usb_handle*>(h));
+}
+
void usb_kick(usb_handle* h) {
should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
: native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index f2ca63b..53f01a0 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -622,6 +622,11 @@
return 0;
}
+void usb_reset(usb_handle* h) {
+ libusb_reset_device(h->device_handle);
+ usb_kick(h);
+}
+
void usb_kick(usb_handle* h) {
h->Close();
}
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 1168958..81b8306 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -458,6 +458,11 @@
return orig_len - len;
}
+void usb_reset(usb_handle* h) {
+ ioctl(h->fd, USBDEVFS_RESET);
+ usb_kick(h);
+}
+
void usb_kick(usb_handle* h) {
std::lock_guard<std::mutex> lock(h->mutex);
D("[ kicking %p (fd = %d) ]", h, h->fd);
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index e380c84..5c0da47 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -556,6 +556,11 @@
return 0;
}
+void usb_reset(usb_handle* handle) {
+ // Unimplemented on OS X.
+ usb_kick(handle);
+}
+
static void usb_kick_locked(usb_handle *handle)
{
LOG(INFO) << "Kicking handle";
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index cfa5cf4..f23c3a5 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -448,6 +448,11 @@
}
}
+void usb_reset(usb_handle* handle) {
+ // Unimplemented on Windows.
+ usb_kick(handle);
+}
+
static void usb_kick_locked(usb_handle* handle) {
// The reason the lock must be acquired before calling this function is in
// case multiple threads are trying to kick the same device at the same time.
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index f69babe..eeac41a 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -14,14 +14,16 @@
* limitations under the License.
*/
+#include <sys/wait.h>
+
+#include <android-base/cmsg.h>
+#include <cmd.h>
+
#include "adb.h"
#include "adb_io.h"
+#include "adb_utils.h"
#include "shell_service.h"
-#include "cmd.h"
-
-#include <sys/wait.h>
-
namespace {
class AdbFdTextOutput : public android::TextOutput {
@@ -83,8 +85,18 @@
break;
}
- auto result = StartCommandInProcess(std::move(data), &execCmd);
- if (!SendFileDescriptor(fd, result)) {
+ std::string_view name = data;
+ auto protocol = SubprocessProtocol::kShell;
+ if (ConsumePrefix(&name, "abb:")) {
+ protocol = SubprocessProtocol::kShell;
+ } else if (ConsumePrefix(&name, "abb_exec:")) {
+ protocol = SubprocessProtocol::kNone;
+ } else {
+ LOG(FATAL) << "Unknown command prefix for abb: " << data;
+ }
+
+ unique_fd result = StartCommandInProcess(std::string(name), &execCmd, protocol);
+ if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
break;
}
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
index 817aea1..a435279 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -20,6 +20,8 @@
#include "adb_utils.h"
#include "shell_service.h"
+#include <android-base/cmsg.h>
+
namespace {
struct AbbProcess;
@@ -59,8 +61,9 @@
unique_fd fd;
std::string error;
- if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
- LOG(ERROR) << "failed to receive FD from abb: " << error;
+ char buf;
+ if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
+ PLOG(ERROR) << "failed to receive FD from abb";
socket_fd_.reset();
continue;
}
@@ -83,6 +86,6 @@
} // namespace
-unique_fd execute_binder_command(std::string_view command) {
+unique_fd execute_abb_command(std::string_view command) {
return abbp->sendCommand(command);
}
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 9e1760d..e82a51f 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -25,8 +25,8 @@
#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>
#include <unistd.h>
#include <utime.h>
@@ -210,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
@@ -242,10 +226,10 @@
return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
}
-static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
- mode_t mode, std::vector<char>& buffer, bool do_unlink) {
+static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
+ uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
+ bool do_unlink) {
syncmsg msg;
- unsigned int timestamp = 0;
__android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
@@ -291,7 +275,7 @@
if (msg.data.id != ID_DATA) {
if (msg.data.id == ID_DONE) {
- timestamp = msg.data.size;
+ *timestamp = msg.data.size;
break;
}
SendSyncFail(s, "invalid data message");
@@ -316,11 +300,6 @@
goto fail;
}
- utimbuf u;
- u.actime = timestamp;
- u.modtime = timestamp;
- utime(path, &u);
-
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
return WriteFdExactly(s, &msg.status, sizeof(msg.status));
@@ -360,9 +339,12 @@
}
#if defined(_WIN32)
-extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
+extern bool handle_send_link(int s, const std::string& path,
+ uint32_t* timestamp, std::vector<char>& buffer)
+ __attribute__((error("no symlinks on Windows")));
#else
-static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
+static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp,
+ std::vector<char>& buffer) {
syncmsg msg;
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -399,6 +381,7 @@
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
if (msg.data.id == ID_DONE) {
+ *timestamp = msg.data.size;
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
@@ -432,40 +415,44 @@
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());
}
+ bool result;
+ uint32_t timestamp;
if (S_ISLNK(mode)) {
- return handle_send_link(s, path.c_str(), buffer);
+ result = handle_send_link(s, path, ×tamp, buffer);
+ } else {
+ // Copy user permission bits to "group" and "other" permissions.
+ mode &= 0777;
+ mode |= ((mode >> 3) & 0070);
+ mode |= ((mode >> 3) & 0007);
+
+ uid_t uid = -1;
+ gid_t gid = -1;
+ uint64_t capabilities = 0;
+ if (should_use_fs_config(path)) {
+ unsigned int broken_api_hack = mode;
+ fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
+ mode = broken_api_hack;
+ }
+
+ result = handle_send_file(s, path.c_str(), ×tamp, uid, gid, capabilities, mode, buffer,
+ do_unlink);
}
- // Copy user permission bits to "group" and "other" permissions.
- mode &= 0777;
- mode |= ((mode >> 3) & 0070);
- mode |= ((mode >> 3) & 0007);
-
- uid_t uid = -1;
- gid_t gid = -1;
- uint64_t capabilities = 0;
- if (should_use_fs_config(path)) {
- unsigned int broken_api_hack = mode;
- fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
- mode = broken_api_hack;
+ if (!result) {
+ return false;
}
- return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
+
+ struct timeval tv[2];
+ tv[0].tv_sec = timestamp;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = timestamp;
+ tv[1].tv_usec = 0;
+ lutimes(path.c_str(), tv);
+ return true;
}
static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
@@ -575,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/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 032ee42..66bfc0d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -32,6 +32,8 @@
#include <memory>
#include <vector>
+#include <android-base/cmsg.h>
+
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
@@ -237,7 +239,7 @@
CHECK(!proc->out_fds.empty());
int fd = proc->out_fds.back().get();
- if (!SendFileDescriptor(socket, fd)) {
+ if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
goto CloseProcess;
}
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/restart_service.cpp b/adb/daemon/restart_service.cpp
index 6803d93..16d2627 100644
--- a/adb/daemon/restart_service.cpp
+++ b/adb/daemon/restart_service.cpp
@@ -20,6 +20,7 @@
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <log/log_properties.h>
@@ -37,6 +38,7 @@
return;
}
+ LOG(INFO) << "adbd restarting as root";
android::base::SetProperty("service.adb.root", "1");
WriteFdExactly(fd.get(), "restarting adbd as root\n");
}
@@ -46,6 +48,8 @@
WriteFdExactly(fd.get(), "adbd not running as root\n");
return;
}
+
+ LOG(INFO) << "adbd restarting as nonroot";
android::base::SetProperty("service.adb.root", "0");
WriteFdExactly(fd.get(), "restarting adbd as non root\n");
}
@@ -56,11 +60,13 @@
return;
}
+ LOG(INFO) << "adbd restarting in TCP mode (port = " << port << ")";
android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
}
void restart_usb_service(unique_fd fd) {
+ LOG(INFO) << "adbd restarting in USB mode";
android::base::SetProperty("service.adb.tcp.port", "0");
WriteFdExactly(fd.get(), "restarting in USB mode\n");
}
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index d1f0345..b0cc450 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -223,17 +223,15 @@
return create_jdwp_service_socket();
} else if (name == "track-jdwp") {
return create_jdwp_tracker_service_socket();
- } else if (name.starts_with("sink:")) {
- name.remove_prefix(strlen("sink:"));
+ } else if (ConsumePrefix(&name, "sink:")) {
uint64_t byte_count = 0;
- if (!android::base::ParseUint(name.data(), &byte_count)) {
+ if (!ParseUint(&byte_count, name)) {
return nullptr;
}
return new SinkSocket(byte_count);
- } else if (name.starts_with("source:")) {
- name.remove_prefix(strlen("source:"));
+ } else if (ConsumePrefix(&name, "source:")) {
uint64_t byte_count = 0;
- if (!android::base::ParseUint(name.data(), &byte_count)) {
+ if (!ParseUint(&byte_count, name)) {
return nullptr;
}
return new SourceSocket(byte_count);
@@ -244,29 +242,27 @@
unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
- if (name.starts_with("abb:")) {
- name.remove_prefix(strlen("abb:"));
- return execute_binder_command(name);
+ if (name.starts_with("abb:") || name.starts_with("abb_exec:")) {
+ return execute_abb_command(name);
}
#endif
#if defined(__ANDROID__)
if (name.starts_with("framebuffer:")) {
return create_service_thread("fb", framebuffer_service);
- } else if (name.starts_with("remount:")) {
- std::string arg(name.begin() + strlen("remount:"), name.end());
+ } else if (ConsumePrefix(&name, "remount:")) {
+ std::string arg(name);
return create_service_thread("remount",
std::bind(remount_service, std::placeholders::_1, arg));
- } else if (name.starts_with("reboot:")) {
- std::string arg(name.begin() + strlen("reboot:"), name.end());
+ } else if (ConsumePrefix(&name, "reboot:")) {
+ std::string arg(name);
return create_service_thread("reboot",
std::bind(reboot_service, std::placeholders::_1, arg));
} else if (name.starts_with("root:")) {
return create_service_thread("root", restart_root_service);
} else if (name.starts_with("unroot:")) {
return create_service_thread("unroot", restart_unroot_service);
- } else if (name.starts_with("backup:")) {
- name.remove_prefix(strlen("backup:"));
+ } else if (ConsumePrefix(&name, "backup:")) {
std::string cmd = "/system/bin/bu backup ";
cmd += name;
return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
@@ -279,8 +275,7 @@
} else if (name.starts_with("enable-verity:")) {
return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
std::placeholders::_1, true));
- } else if (name.starts_with("tcpip:")) {
- name.remove_prefix(strlen("tcpip:"));
+ } else if (ConsumePrefix(&name, "tcpip:")) {
std::string str(name);
int port;
@@ -294,24 +289,22 @@
}
#endif
- if (name.starts_with("dev:")) {
- name.remove_prefix(strlen("dev:"));
+ if (ConsumePrefix(&name, "dev:")) {
return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
- } else if (name.starts_with("jdwp:")) {
- name.remove_prefix(strlen("jdwp:"));
- std::string str(name);
- return create_jdwp_connection_fd(atoi(str.c_str()));
- } else if (name.starts_with("shell")) {
- name.remove_prefix(strlen("shell"));
+ } else if (ConsumePrefix(&name, "jdwp:")) {
+ pid_t pid;
+ if (!ParseUint(&pid, name)) {
+ return unique_fd{};
+ }
+ return create_jdwp_connection_fd(pid);
+ } else if (ConsumePrefix(&name, "shell")) {
return ShellService(name, transport);
- } else if (name.starts_with("exec:")) {
- name.remove_prefix(strlen("exec:"));
+ } else if (ConsumePrefix(&name, "exec:")) {
return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
} else if (name.starts_with("sync:")) {
return create_service_thread("sync", file_sync_service);
- } else if (name.starts_with("reverse:")) {
- name.remove_prefix(strlen("reverse:"));
+ } else if (ConsumePrefix(&name, "reverse:")) {
return reverse_service(name, transport);
} else if (name == "reconnect") {
return create_service_thread(
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/shell_service.cpp b/adb/daemon/shell_service.cpp
index 0794bcd..3c8f393 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -170,6 +170,8 @@
// Opens the file at |pts_name|.
int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
+ bool ConnectProtocolEndpoints(std::string* _Nonnull error);
+
static void ThreadHandler(void* userdata);
void PassDataStreams();
void WaitForExit();
@@ -383,16 +385,65 @@
}
D("subprocess parent: exec completed");
+ if (!ConnectProtocolEndpoints(error)) {
+ kill(pid_, SIGKILL);
+ return false;
+ }
+
+ D("subprocess parent: completed");
+ return true;
+}
+
+bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) {
+ unique_fd child_stdinout_sfd, child_stderr_sfd;
+
+ CHECK(type_ == SubprocessType::kRaw);
+
+ __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+
+ if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
+ strerror(errno));
+ return false;
+ }
+ if (protocol_ == SubprocessProtocol::kShell) {
+ // Shell protocol allows for splitting stderr.
+ if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
+ strerror(errno));
+ return false;
+ }
+ } else {
+ // Raw protocol doesn't support multiple output streams, so combine stdout and stderr.
+ child_stderr_sfd.reset(dup(child_stdinout_sfd));
+ }
+
+ D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
+ stderr_sfd_.get());
+
+ if (!ConnectProtocolEndpoints(error)) {
+ return false;
+ }
+
+ std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
+ command = std::move(command),
+ args = command_]() { command(args, inout_sfd, inout_sfd, err_sfd); })
+ .detach();
+
+ D("execinprocess: completed");
+ return true;
+}
+
+bool Subprocess::ConnectProtocolEndpoints(std::string* _Nonnull error) {
if (protocol_ == SubprocessProtocol::kNone) {
// No protocol: all streams pass through the stdinout FD and hook
// directly into the local socket for raw data transfer.
local_socket_sfd_.reset(stdinout_sfd_.release());
} else {
- // Shell protocol: create another socketpair to intercept data.
+ // Required for shell protocol: create another socketpair to intercept data.
if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
*error = android::base::StringPrintf(
- "failed to create socketpair to intercept data: %s", strerror(errno));
- kill(pid_, SIGKILL);
+ "failed to create socketpair to intercept data: %s", strerror(errno));
return false;
}
D("protocol FD = %d", protocol_sfd_.get());
@@ -401,7 +452,6 @@
output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
if (!input_ || !output_) {
*error = "failed to allocate shell protocol objects";
- kill(pid_, SIGKILL);
return false;
}
@@ -413,76 +463,13 @@
if (fd >= 0) {
if (!set_file_block_mode(fd, false)) {
*error = android::base::StringPrintf(
- "failed to set non-blocking mode for fd %d", fd);
- kill(pid_, SIGKILL);
+ "failed to set non-blocking mode for fd %d", fd);
return false;
}
}
}
}
- D("subprocess parent: completed");
- return true;
-}
-
-bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) {
- unique_fd child_stdinout_sfd, child_stderr_sfd;
-
- CHECK(type_ == SubprocessType::kRaw);
- CHECK(protocol_ == SubprocessProtocol::kShell);
-
- __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
-
- if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
- *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
- strerror(errno));
- return false;
- }
- // Raw subprocess + shell protocol allows for splitting stderr.
- if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
- *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
- strerror(errno));
- return false;
- }
-
- D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
- stderr_sfd_.get());
-
- // Required for shell protocol: create another socketpair to intercept data.
- if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
- *error = android::base::StringPrintf("failed to create socketpair to intercept data: %s",
- strerror(errno));
- return false;
- }
- D("protocol FD = %d", protocol_sfd_.get());
-
- input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
- output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
- if (!input_ || !output_) {
- *error = "failed to allocate shell protocol objects";
- return false;
- }
-
- // Don't let reads/writes to the subprocess block our thread. This isn't
- // likely but could happen under unusual circumstances, such as if we
- // write a ton of data to stdin but the subprocess never reads it and
- // the pipe fills up.
- for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
- if (fd >= 0) {
- if (!set_file_block_mode(fd, false)) {
- *error = android::base::StringPrintf("failed to set non-blocking mode for fd %d",
- fd);
- return false;
- }
- }
- }
-
- std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
- command = std::move(command),
- args = command_]() { command(args, inout_sfd, inout_sfd, err_sfd); })
- .detach();
-
- D("execinprocess: completed");
return true;
}
@@ -863,12 +850,11 @@
return local_socket;
}
-unique_fd StartCommandInProcess(std::string name, Command command) {
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol) {
LOG(INFO) << "StartCommandInProcess(" << dump_hex(name.data(), name.size()) << ")";
constexpr auto terminal_type = "";
constexpr auto type = SubprocessType::kRaw;
- constexpr auto protocol = SubprocessProtocol::kShell;
constexpr auto make_pty_raw = false;
auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index fc66377..3abd958 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -49,7 +49,7 @@
//
// Returns an open FD connected to the thread or -1 on failure.
using Command = int(std::string_view args, int in, int out, int err);
-unique_fd StartCommandInProcess(std::string name, Command command);
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
// Create a pipe containing the error.
unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
index 323bcec..dc79d12 100644
--- a/adb/daemon/shell_service_test.cpp
+++ b/adb/daemon/shell_service_test.cpp
@@ -35,7 +35,6 @@
static void SetUpTestCase() {
// This is normally done in main.cpp.
saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
-
}
static void TearDownTestCase() {
@@ -49,26 +48,32 @@
SubprocessProtocol protocol);
void CleanupTestSubprocess();
- virtual void TearDown() override {
- void CleanupTestSubprocess();
- }
+ void StartTestCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
+
+ virtual void TearDown() override { CleanupTestSubprocess(); }
static sighandler_t saved_sigpipe_handler_;
- unique_fd subprocess_fd_;
+ unique_fd command_fd_;
};
sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
void ShellServiceTest::StartTestSubprocess(
const char* command, SubprocessType type, SubprocessProtocol protocol) {
- subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
- ASSERT_TRUE(subprocess_fd_ >= 0);
+ command_fd_ = StartSubprocess(command, nullptr, type, protocol);
+ ASSERT_TRUE(command_fd_ >= 0);
}
void ShellServiceTest::CleanupTestSubprocess() {
}
+void ShellServiceTest::StartTestCommandInProcess(std::string name, Command command,
+ SubprocessProtocol protocol) {
+ command_fd_ = StartCommandInProcess(std::move(name), std::move(command), protocol);
+ ASSERT_TRUE(command_fd_ >= 0);
+}
+
namespace {
// Reads raw data from |fd| until it closes or errors.
@@ -93,7 +98,7 @@
stdout->clear();
stderr->clear();
- ShellProtocol* protocol = new ShellProtocol(fd);
+ auto protocol = std::make_unique<ShellProtocol>(fd);
while (protocol->Read()) {
switch (protocol->id()) {
case ShellProtocol::kIdStdout:
@@ -111,7 +116,6 @@
ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
}
}
- delete protocol;
return exit_code;
}
@@ -154,7 +158,7 @@
// [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without
// the shell protocol we should always force a PTY to ensure proper cleanup.
- ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+ ExpectLinesEqual(ReadRaw(command_fd_), {"foo", "bar", "0"});
}
// Tests a PTY subprocess with no protocol.
@@ -165,7 +169,7 @@
SubprocessType::kPty, SubprocessProtocol::kNone));
// [ -t 0 ] == 0 means we have a terminal (PTY).
- ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+ ExpectLinesEqual(ReadRaw(command_fd_), {"foo", "bar", "0"});
}
// Tests a raw subprocess with the shell protocol.
@@ -175,7 +179,7 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string stdout, stderr;
- EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(24, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo", "baz"});
ExpectLinesEqual(stderr, {"bar"});
}
@@ -189,7 +193,7 @@
// PTY always combines stdout and stderr but the shell protocol should
// still give us an exit code.
std::string stdout, stderr;
- EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(50, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
ExpectLinesEqual(stderr, {});
}
@@ -204,7 +208,7 @@
"echo --${TEST_STR}--",
"exit"};
- ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ ShellProtocol* protocol = new ShellProtocol(command_fd_);
for (std::string command : commands) {
// Interactive shell requires a newline to complete each command.
command.push_back('\n');
@@ -214,7 +218,7 @@
delete protocol;
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
// An unpredictable command prompt makes parsing exact output difficult but
// it should at least contain echoed input and the expected output.
for (const char* command : commands) {
@@ -230,14 +234,14 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string input = "foo\nbar";
- ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ ShellProtocol* protocol = new ShellProtocol(command_fd_);
memcpy(protocol->data(), input.data(), input.length());
ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
delete protocol;
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
ExpectLinesEqual(stderr, {});
}
@@ -249,7 +253,7 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {});
ExpectLinesEqual(stderr, {"bar"});
}
@@ -261,7 +265,56 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo"});
ExpectLinesEqual(stderr, {});
}
+
+// Tests an inprocess command with no protocol.
+TEST_F(ShellServiceTest, RawNoProtocolInprocess) {
+ ASSERT_NO_FATAL_FAILURE(
+ StartTestCommandInProcess("123",
+ [](auto args, auto in, auto out, auto err) -> int {
+ EXPECT_EQ("123", args);
+ char input[10];
+ EXPECT_TRUE(ReadFdExactly(in, input, 2));
+ input[2] = 0;
+ EXPECT_STREQ("in", input);
+ WriteFdExactly(out, "out\n");
+ WriteFdExactly(err, "err\n");
+ return 0;
+ },
+ SubprocessProtocol::kNone));
+
+ WriteFdExactly(command_fd_, "in");
+ ExpectLinesEqual(ReadRaw(command_fd_), {"out", "err"});
+}
+
+// Tests an inprocess command with the shell protocol.
+TEST_F(ShellServiceTest, RawShellProtocolInprocess) {
+ ASSERT_NO_FATAL_FAILURE(
+ StartTestCommandInProcess("321",
+ [](auto args, auto in, auto out, auto err) -> int {
+ EXPECT_EQ("321", args);
+ char input[10];
+ EXPECT_TRUE(ReadFdExactly(in, input, 2));
+ input[2] = 0;
+ EXPECT_STREQ("in", input);
+ WriteFdExactly(out, "out\n");
+ WriteFdExactly(err, "err\n");
+ return 0;
+ },
+ SubprocessProtocol::kShell));
+
+ {
+ auto write_protocol = std::make_unique<ShellProtocol>(command_fd_);
+ memcpy(write_protocol->data(), "in", 2);
+ write_protocol->Write(ShellProtocol::kIdStdin, 2);
+ }
+
+ std::string stdout, stderr;
+ // For in-process commands the exit code is always the default (1).
+ EXPECT_EQ(1, ReadShellProtocol(command_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"out"});
+ ExpectLinesEqual(stderr, {"err"});
+}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f603d13..8c33ca5 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>
@@ -53,10 +54,14 @@
using android::base::StringPrintf;
-static constexpr size_t kUsbReadQueueDepth = 16;
-static constexpr size_t kUsbReadSize = 16384;
+// 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 kUsbWriteQueueDepth = 16;
+static constexpr size_t kUsbReadQueueDepth = 32;
+static constexpr size_t kUsbReadSize = 8 * PAGE_SIZE;
+
+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) {
@@ -110,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); }
};
@@ -163,14 +168,20 @@
struct UsbFfsConnection : public Connection {
UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
std::promise<void> destruction_notifier)
- : stopped_(false),
+ : worker_started_(false),
+ stopped_(false),
destruction_notifier_(std::move(destruction_notifier)),
control_fd_(std::move(control)),
read_fd_(std::move(read)),
write_fd_(std::move(write)) {
LOG(INFO) << "UsbFfsConnection constructed";
- event_fd_.reset(eventfd(0, EFD_CLOEXEC));
- if (event_fd_ == -1) {
+ worker_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (worker_event_fd_ == -1) {
+ PLOG(FATAL) << "failed to create eventfd";
+ }
+
+ monitor_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (monitor_event_fd_ == -1) {
PLOG(FATAL) << "failed to create eventfd";
}
@@ -181,6 +192,14 @@
LOG(INFO) << "UsbFfsConnection being destroyed";
Stop();
monitor_thread_.join();
+
+ // We need to explicitly close our file descriptors before we notify our destruction,
+ // because the thread listening on the future will immediately try to reopen the endpoint.
+ aio_context_.reset();
+ control_fd_.reset();
+ read_fd_.reset();
+ write_fd_.reset();
+
destruction_notifier_.set_value();
}
@@ -192,8 +211,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;
@@ -207,11 +238,18 @@
}
stopped_ = true;
uint64_t notify = 1;
- ssize_t rc = adb_write(event_fd_.get(), ¬ify, sizeof(notify));
+ ssize_t rc = adb_write(worker_event_fd_.get(), ¬ify, sizeof(notify));
if (rc < 0) {
- PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+ PLOG(FATAL) << "failed to notify worker eventfd to stop UsbFfsConnection";
}
CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+
+ rc = adb_write(monitor_event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify monitor eventfd to stop UsbFfsConnection";
+ }
+
+ CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
}
private:
@@ -224,7 +262,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) {}); });
@@ -232,25 +269,28 @@
adb_thread_setname("UsbFfs-monitor");
bool bound = false;
- bool started = false;
+ bool enabled = false;
bool running = true;
while (running) {
- if (!bound || !started) {
- adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
- int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
- 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;
- }
+ adb_pollfd pfd[2] = {
+ { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
+ { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
+ };
+
+ // If we don't see our first bind within a second, try again.
+ int timeout_ms = bound ? -1 : 1000;
+
+ int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout_ms));
+ if (rc == -1) {
+ PLOG(FATAL) << "poll on USB control fd failed";
+ } else if (rc == 0) {
+ LOG(WARNING) << "timed out while waiting for FUNCTIONFS_BIND, trying again";
+ break;
+ }
+
+ if (pfd[1].revents) {
+ // We were told to die.
+ break;
}
struct usb_functionfs_event event;
@@ -264,78 +304,127 @@
switch (event.type) {
case FUNCTIONFS_BIND:
- CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+ if (bound) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
+ running = false;
+ }
+
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
+ running = false;
+ }
+
bound = true;
break;
case FUNCTIONFS_ENABLE:
- CHECK(!started) << "received FUNCTIONFS_ENABLE while already running?";
- started = true;
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
+ running = false;
+ }
+
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
+ running = false;
+ }
+
+ enabled = true;
StartWorker();
break;
case FUNCTIONFS_DISABLE:
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_DISABLE while not bound?";
+ }
+
+ if (!enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_DISABLE while not enabled?";
+ }
+
+ enabled = false;
+ running = false;
+ break;
+
+ case FUNCTIONFS_UNBIND:
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_UNBIND while still enabled?";
+ }
+
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_UNBIND when not bound?";
+ }
+
+ bound = false;
running = false;
break;
}
}
- 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();
-
- aio_context_.reset();
- read_fd_.reset();
- write_fd_.reset();
+ StopWorker();
+ HandleError("monitor thread finished");
});
}
void StartWorker() {
+ CHECK(!worker_started_);
+ worker_started_ = true;
worker_thread_ = std::thread([this]() {
adb_thread_setname("UsbFfs-worker");
for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
read_requests_[i] = CreateReadBlock(next_read_id_++);
- SubmitRead(&read_requests_[i]);
+ if (!SubmitRead(&read_requests_[i])) {
+ return;
+ }
}
while (!stopped_) {
uint64_t dummy;
- ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+ ssize_t rc = adb_read(worker_event_fd_.get(), &dummy, sizeof(dummy));
if (rc == -1) {
PLOG(FATAL) << "failed to read from eventfd";
} else if (rc == 0) {
LOG(FATAL) << "hit EOF on eventfd";
}
- WaitForEvents();
+ ReadEvents();
}
});
}
+ void StopWorker() {
+ if (!worker_started_) {
+ return;
+ }
+
+ 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) {
@@ -347,11 +436,11 @@
block.control.aio_fildes = read_fd_.get();
block.control.aio_offset = 0;
block.control.aio_flags = IOCB_FLAG_RESFD;
- block.control.aio_resfd = event_fd_.get();
+ block.control.aio_resfd = worker_event_fd_.get();
return block;
}
- void WaitForEvents() {
+ void ReadEvents() {
static constexpr size_t kMaxEvents = kUsbReadQueueDepth + kUsbWriteQueueDepth;
struct io_event events[kMaxEvents];
struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
@@ -386,7 +475,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_) {
@@ -407,16 +496,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)));
}
@@ -438,13 +527,22 @@
SubmitRead(block);
}
- void SubmitRead(IoBlock* block) {
+ bool SubmitRead(IoBlock* block) {
block->pending = true;
struct iocb* iocb = &block->control;
if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+ if (errno == EINVAL && !gFfsAioSupported.has_value()) {
+ HandleError("failed to submit first read, AIO on FFS not supported");
+ gFfsAioSupported = false;
+ return false;
+ }
+
HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
- return;
+ return false;
}
+
+ gFfsAioSupported = true;
+ return true;
}
void HandleWrite(TransferId id) {
@@ -462,7 +560,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));
@@ -470,14 +569,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 = event_fd_.get();
+ 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;
@@ -498,6 +603,8 @@
LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
}
+ writes_submitted_ += writes_to_submit;
+
int rc = io_submit(aio_context_.get(), writes_to_submit, iocbs);
if (rc == -1) {
HandleError(StringPrintf("failed to submit write requests: %s", strerror(errno)));
@@ -506,8 +613,6 @@
LOG(FATAL) << "failed to submit all writes: wanted to submit " << writes_to_submit
<< ", actually submitted " << rc;
}
-
- writes_submitted_ += rc;
}
void HandleError(const std::string& error) {
@@ -520,13 +625,16 @@
}
std::thread monitor_thread_;
+
+ bool worker_started_;
std::thread worker_thread_;
std::atomic<bool> stopped_;
std::promise<void> destruction_notifier_;
std::once_flag error_flag_;
- unique_fd event_fd_;
+ unique_fd worker_event_fd_;
+ unique_fd monitor_event_fd_;
ScopedAioContext aio_context_;
unique_fd control_fd_;
@@ -549,12 +657,21 @@
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();
+
static void usb_ffs_open_thread() {
adb_thread_setname("usb ffs open");
while (true) {
+ if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
+ LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
+ return usb_init_legacy();
+ }
+
unique_fd control;
unique_fd bulk_out;
unique_fd bulk_in;
@@ -575,11 +692,14 @@
}
}
-void usb_init_legacy();
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/daemon/usb_dummy.cpp b/adb/daemon/usb_dummy.cpp
index 984bc25..c9bf797 100644
--- a/adb/daemon/usb_dummy.cpp
+++ b/adb/daemon/usb_dummy.cpp
@@ -33,6 +33,10 @@
return -1;
}
+void usb_reset(usb_handle*) {
+ LOG(FATAL) << "unimplemented";
+}
+
void usb_kick(usb_handle*) {
LOG(FATAL) << "unimplemented";
}
diff --git a/adb/daemon/usb_legacy.cpp b/adb/daemon/usb_legacy.cpp
index 7ace59d..b65727a 100644
--- a/adb/daemon/usb_legacy.cpp
+++ b/adb/daemon/usb_legacy.cpp
@@ -307,6 +307,10 @@
return 0;
}
+void usb_reset(usb_handle* h) {
+ usb_close(h);
+}
+
void usb_kick(usb_handle* h) {
h->kick(h);
}
diff --git a/adb/services.cpp b/adb/services.cpp
index 0061f0e..cf346ba 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -25,6 +25,7 @@
#include <string.h>
#include <thread>
+
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/sockets.h>
@@ -63,11 +64,11 @@
adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
adb_setsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
}
-#endif // !ADB_HOST
+#endif // !ADB_HOST
std::thread(service_bootstrap_func, service_name, func, unique_fd(s[1])).detach();
- D("service thread started, %d:%d",s[0], s[1]);
+ D("service thread started, %d:%d", s[0], s[1]);
return unique_fd(s[0]);
}
@@ -108,12 +109,21 @@
const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : nullptr;
atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
&is_ambiguous, &error);
- if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
+ if (sinfo->state == kCsOffline) {
+ // wait-for-disconnect uses kCsOffline, we don't actually want to wait for 'offline'.
+ if (t == nullptr) {
+ SendOkay(fd);
+ break;
+ }
+ } else if (t != nullptr &&
+ (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
SendOkay(fd);
break;
- } else if (!is_ambiguous) {
- adb_pollfd pfd = {.fd = fd, .events = POLLIN };
- int rc = adb_poll(&pfd, 1, 1000);
+ }
+
+ if (!is_ambiguous) {
+ adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+ int rc = adb_poll(&pfd, 1, 100);
if (rc < 0) {
SendFail(fd, error);
break;
@@ -178,7 +188,7 @@
if (!strncmp(host.c_str(), "emu:", 4)) {
connect_emulator(host.c_str() + 4, &response);
} else {
- connect_device(host.c_str(), &response);
+ connect_device(host, &response);
}
// Send response for emulator and device
@@ -187,46 +197,44 @@
#endif
#if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
- if (!strcmp(name,"track-devices")) {
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+ TransportId transport_id) {
+ if (name == "track-devices") {
return create_device_tracker(false);
- } else if (!strcmp(name, "track-devices-l")) {
+ } else if (name == "track-devices-l") {
return create_device_tracker(true);
- } else if (android::base::StartsWith(name, "wait-for-")) {
- name += strlen("wait-for-");
-
+ } else if (ConsumePrefix(&name, "wait-for-")) {
std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
if (sinfo == nullptr) {
fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
return nullptr;
}
- if (serial) sinfo->serial = serial;
+ sinfo->serial = serial;
sinfo->transport_id = transport_id;
- if (android::base::StartsWith(name, "local")) {
- name += strlen("local");
+ if (ConsumePrefix(&name, "local")) {
sinfo->transport_type = kTransportLocal;
- } else if (android::base::StartsWith(name, "usb")) {
- name += strlen("usb");
+ } else if (ConsumePrefix(&name, "usb")) {
sinfo->transport_type = kTransportUsb;
- } else if (android::base::StartsWith(name, "any")) {
- name += strlen("any");
+ } else if (ConsumePrefix(&name, "any")) {
sinfo->transport_type = kTransportAny;
} else {
return nullptr;
}
- if (!strcmp(name, "-device")) {
+ if (name == "-device") {
sinfo->state = kCsDevice;
- } else if (!strcmp(name, "-recovery")) {
+ } else if (name == "-recovery") {
sinfo->state = kCsRecovery;
- } else if (!strcmp(name, "-sideload")) {
+ } else if (name == "-sideload") {
sinfo->state = kCsSideload;
- } else if (!strcmp(name, "-bootloader")) {
+ } else if (name == "-bootloader") {
sinfo->state = kCsBootloader;
- } else if (!strcmp(name, "-any")) {
+ } else if (name == "-any") {
sinfo->state = kCsAny;
+ } else if (name == "-disconnect") {
+ sinfo->state = kCsOffline;
} else {
return nullptr;
}
@@ -235,8 +243,8 @@
wait_for_state(fd, sinfo.get());
});
return create_local_socket(std::move(fd));
- } else if (!strncmp(name, "connect:", 8)) {
- std::string host(name + strlen("connect:"));
+ } else if (ConsumePrefix(&name, "connect:")) {
+ std::string host(name);
unique_fd fd = create_service_thread(
"connect", std::bind(connect_service, std::placeholders::_1, host));
return create_local_socket(std::move(fd));
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/sockets.cpp b/adb/sockets.cpp
index 420a6d5..8a2bf9a 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -426,22 +426,6 @@
return s;
}
-#if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial,
- TransportId transport_id) {
- asocket* s;
-
- s = host_service_to_socket(name, serial, transport_id);
-
- if (s != nullptr) {
- D("LS(%d) bound to '%s'", s->id, name);
- return s;
- }
-
- return s;
-}
-#endif /* ADB_HOST */
-
static int remote_socket_enqueue(asocket* s, apacket::payload_type data) {
D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d", s->id, s->fd, s->peer->fd);
apacket* p = get_apacket();
@@ -771,34 +755,27 @@
#if ADB_HOST
service = std::string_view(s->smart_socket_data).substr(4);
- if (service.starts_with("host-serial:")) {
- service.remove_prefix(strlen("host-serial:"));
-
+ if (ConsumePrefix(&service, "host-serial:")) {
// serial number should follow "host:" and could be a host:port string.
if (!internal::parse_host_service(&serial, &service, service)) {
LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
goto fail;
}
- } else if (service.starts_with("host-transport-id:")) {
- service.remove_prefix(strlen("host-transport-id:"));
+ } else if (ConsumePrefix(&service, "host-transport-id:")) {
if (!ParseUint(&transport_id, service, &service)) {
LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
return -1;
}
- if (!service.starts_with(":")) {
+ if (!ConsumePrefix(&service, ":")) {
LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
return -1;
}
- service.remove_prefix(1);
- } else if (service.starts_with("host-usb:")) {
+ } else if (ConsumePrefix(&service, "host-usb:")) {
type = kTransportUsb;
- service.remove_prefix(strlen("host-usb:"));
- } else if (service.starts_with("host-local:")) {
+ } else if (ConsumePrefix(&service, "host-local:")) {
type = kTransportLocal;
- service.remove_prefix(strlen("host-local:"));
- } else if (service.starts_with("host:")) {
+ } else if (ConsumePrefix(&service, "host:")) {
type = kTransportAny;
- service.remove_prefix(strlen("host:"));
} else {
service = std::string_view{};
}
@@ -808,17 +785,22 @@
// Some requests are handled immediately -- in that case the handle_host_request() routine
// has sent the OKAY or FAIL message and all we have to do is clean up.
- // TODO: Convert to string_view.
- if (handle_host_request(std::string(service).c_str(), type,
- serial.empty() ? nullptr : std::string(serial).c_str(),
- transport_id, s->peer->fd, s)) {
- LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
- goto fail;
- }
- if (service.starts_with("transport")) {
- D("SS(%d): okay transport", s->id);
- s->smart_socket_data.clear();
- return 0;
+ auto host_request_result = handle_host_request(
+ service, type, serial.empty() ? nullptr : std::string(serial).c_str(), transport_id,
+ s->peer->fd, s);
+
+ switch (host_request_result) {
+ case HostRequestResult::Handled:
+ LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
+ goto fail;
+
+ case HostRequestResult::SwitchedTransport:
+ D("SS(%d): okay transport", s->id);
+ s->smart_socket_data.clear();
+ return 0;
+
+ case HostRequestResult::Unhandled:
+ break;
}
/* try to find a local service with this name.
@@ -826,8 +808,7 @@
** and tear down here.
*/
// TODO: Convert to string_view.
- s2 = create_host_service_socket(std::string(service).c_str(), std::string(serial).c_str(),
- transport_id);
+ s2 = host_service_to_socket(service, serial, transport_id);
if (s2 == nullptr) {
LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'";
SendFail(s->peer->fd, "unknown host service");
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 14e5071..8272722 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -416,6 +416,7 @@
output.strip(),
"already connected to {}".format(serial).encode("utf8"))
+ @unittest.skip("Currently failing b/123247844")
def test_reconnect(self):
"""Ensure that a disconnected device reconnects."""
diff --git a/adb/transport.cpp b/adb/transport.cpp
index ae53597..15c3a9a 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -52,6 +52,8 @@
#include "fdevent.h"
#include "sysdeps/chrono.h"
+using android::base::ScopedLockAssertion;
+
static void remove_transport(atransport* transport);
static void transport_unref(atransport* transport);
@@ -69,20 +71,11 @@
const char* const kFeatureApex = "apex";
const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
const char* const kFeatureAbb = "abb";
+const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
+const char* const kFeatureAbbExec = "abb_exec";
namespace {
-// A class that helps the Clang Thread Safety Analysis deal with
-// std::unique_lock. Given that std::unique_lock is movable, and the analysis
-// can not currently perform alias analysis, it is not annotated. In order to
-// assert that the mutex is held, a ScopedAssumeLocked can be created just after
-// the std::unique_lock.
-class SCOPED_CAPABILITY ScopedAssumeLocked {
- public:
- ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
- ~ScopedAssumeLocked() RELEASE() {}
-};
-
#if ADB_HOST
// Tracks and handles atransport*s that are attempting reconnection.
class ReconnectHandler {
@@ -180,7 +173,7 @@
ReconnectAttempt attempt;
{
std::unique_lock<std::mutex> lock(reconnect_mutex_);
- ScopedAssumeLocked assume_lock(reconnect_mutex_);
+ ScopedLockAssertion assume_lock(reconnect_mutex_);
if (!reconnect_queue_.empty()) {
// FIXME: libstdc++ (used on Windows) implements condition_variable with
@@ -264,6 +257,11 @@
return next++;
}
+void Connection::Reset() {
+ LOG(INFO) << "Connection::Reset(): stopping";
+ Stop();
+}
+
BlockingConnectionAdapter::BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection)
: underlying_(std::move(connection)) {}
@@ -296,7 +294,7 @@
LOG(INFO) << this->transport_name_ << ": write thread spawning";
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
- ScopedAssumeLocked assume_locked(mutex_);
+ ScopedLockAssertion assume_locked(mutex_);
cv_.wait(lock, [this]() REQUIRES(mutex_) {
return this->stopped_ || !this->write_queue_.empty();
});
@@ -319,6 +317,26 @@
started_ = true;
}
+void BlockingConnectionAdapter::Reset() {
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (!started_) {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): not started";
+ return;
+ }
+
+ if (stopped_) {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_
+ << "): already stopped";
+ return;
+ }
+ }
+
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): resetting";
+ this->underlying_->Reset();
+ Stop();
+}
+
void BlockingConnectionAdapter::Stop() {
{
std::lock_guard<std::mutex> lock(mutex_);
@@ -431,14 +449,18 @@
}
}
-void kick_transport(atransport* t) {
+void kick_transport(atransport* t, bool reset) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
// As kick_transport() can be called from threads without guarantee that t is valid,
// check if the transport is in transport_list first.
//
// TODO(jmgao): WTF? Is this actually true?
if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
- t->Kick();
+ if (reset) {
+ t->Reset();
+ } else {
+ t->Kick();
+ }
}
#if ADB_HOST
@@ -923,7 +945,7 @@
bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mutex_);
- ScopedAssumeLocked assume_locked(mutex_);
+ ScopedLockAssertion assume_locked(mutex_);
return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
return connection_established_ready_;
}) && connection_established_;
@@ -949,9 +971,16 @@
return this->connection()->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
}
+void atransport::Reset() {
+ if (!kicked_.exchange(true)) {
+ LOG(INFO) << "resetting transport " << this << " " << this->serial;
+ this->connection()->Reset();
+ }
+}
+
void atransport::Kick() {
if (!kicked_.exchange(true)) {
- D("kicking transport %p %s", this, this->serial.c_str());
+ LOG(INFO) << "kicking transport " << this << " " << this->serial;
this->connection()->Stop();
}
}
@@ -1014,8 +1043,14 @@
const FeatureSet& supported_features() {
// Local static allocation to avoid global non-POD variables.
static const FeatureSet* features = new FeatureSet{
- kFeatureShell2, kFeatureCmd, kFeatureStat2,
- kFeatureFixedPushMkdir, kFeatureApex, kFeatureAbb,
+ kFeatureShell2,
+ kFeatureCmd,
+ kFeatureStat2,
+ kFeatureFixedPushMkdir,
+ kFeatureApex,
+ kFeatureAbb,
+ kFeatureFixedPushSymlinkTimestamp,
+ kFeatureAbbExec,
// Increment ADB_SERVER_VERSION when adding a feature that adbd needs
// to know about. Otherwise, the client can be stuck running an old
// version of the server even after upgrading their copy of adb.
@@ -1174,18 +1209,22 @@
return result;
}
-void close_usb_devices(std::function<bool(const atransport*)> predicate) {
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (predicate(t)) {
- t->Kick();
+ if (reset) {
+ t->Reset();
+ } else {
+ t->Kick();
+ }
}
}
}
/* hack for osx */
-void close_usb_devices() {
- close_usb_devices([](const atransport*) { return true; });
+void close_usb_devices(bool reset) {
+ close_usb_devices([](const atransport*) { return true; }, reset);
}
#endif // ADB_HOST
diff --git a/adb/transport.h b/adb/transport.h
index 065c81f..f4490ed 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -67,6 +67,8 @@
extern const char* const kFeatureFixedPushMkdir;
// adbd supports android binder bridge (abb).
extern const char* const kFeatureAbb;
+// adbd properly updates symlink timestamps on push.
+extern const char* const kFeatureFixedPushSymlinkTimestamp;
TransportId NextTransportId();
@@ -97,6 +99,9 @@
virtual void Start() = 0;
virtual void Stop() = 0;
+ // Stop, and reset the device if it's a USB connection.
+ virtual void Reset();
+
std::string transport_name_;
ReadCallback read_callback_;
ErrorCallback error_callback_;
@@ -122,6 +127,9 @@
// This method must be thread-safe, and must cause concurrent Reads/Writes to terminate.
// Formerly known as 'Kick' in atransport.
virtual void Close() = 0;
+
+ // Terminate a connection, and reset it.
+ virtual void Reset() = 0;
};
struct BlockingConnectionAdapter : public Connection {
@@ -134,6 +142,8 @@
virtual void Start() override final;
virtual void Stop() override final;
+ virtual void Reset() override final;
+
bool started_ GUARDED_BY(mutex_) = false;
bool stopped_ GUARDED_BY(mutex_) = false;
@@ -155,6 +165,7 @@
bool Write(apacket* packet) override final;
void Close() override;
+ virtual void Reset() override final { Close(); }
private:
unique_fd fd_;
@@ -168,6 +179,7 @@
bool Write(apacket* packet) override final;
void Close() override final;
+ virtual void Reset() override final;
usb_handle* handle_;
};
@@ -233,6 +245,7 @@
virtual ~atransport();
int Write(apacket* p);
+ void Reset();
void Kick();
bool kicked() const { return kicked_; }
@@ -362,7 +375,7 @@
atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
bool* is_ambiguous, std::string* error_out,
bool accept_any_state = false);
-void kick_transport(atransport* t);
+void kick_transport(atransport* t, bool reset = false);
void update_transports(void);
// Iterates across all of the current and pending transports.
@@ -393,8 +406,8 @@
bool check_header(apacket* p, atransport* t);
-void close_usb_devices();
-void close_usb_devices(std::function<bool(const atransport*)> predicate);
+void close_usb_devices(bool reset = false);
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset = false);
void send_packet(apacket* p, atransport* t);
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/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 2e5918a..3e87522 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -171,6 +171,11 @@
return true;
}
+void UsbConnection::Reset() {
+ usb_reset(handle_);
+ usb_kick(handle_);
+}
+
void UsbConnection::Close() {
usb_kick(handle_);
}
diff --git a/adb/types.h b/adb/types.h
index 0090c98..cd1366d 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -216,7 +216,10 @@
// Add a nonempty block to the chain.
// The end of the chain must be a complete block (i.e. end_offset_ == 0).
void append(std::unique_ptr<const block_type> block) {
- CHECK_NE(0ULL, block->size());
+ if (block->size() == 0) {
+ return;
+ }
+
CHECK_EQ(0ULL, end_offset_);
chain_length_ += block->size();
chain_.emplace_back(std::move(block));
diff --git a/adb/usb.h b/adb/usb.h
index cd83c42..eb8ca6c 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -26,6 +26,7 @@
int usb_write(handle_ref_type h, const void* data, int len); \
int usb_read(handle_ref_type h, void* data, int len); \
int usb_close(handle_ref_type h); \
+ void usb_reset(handle_ref_type h); \
void usb_kick(handle_ref_type h); \
size_t usb_get_max_packet_size(handle_ref_type)
diff --git a/base/Android.bp b/base/Android.bp
index b0181aa..38f301a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -46,6 +46,7 @@
defaults: ["libbase_cflags_defaults"],
srcs: [
"chrono_utils.cpp",
+ "cmsg.cpp",
"file.cpp",
"logging.cpp",
"mapped_file.cpp",
@@ -85,6 +86,9 @@
"errors_windows.cpp",
"utf8.cpp",
],
+ exclude_srcs: [
+ "cmsg.cpp",
+ ],
enabled: true,
},
},
@@ -121,6 +125,7 @@
defaults: ["libbase_cflags_defaults"],
host_supported: true,
srcs: [
+ "cmsg_test.cpp",
"endian_test.cpp",
"errors_test.cpp",
"file_test.cpp",
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 0000000..42866f8
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+ const std::vector<int>& fds) {
+ size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
+ size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+ if (cmsg_space >= PAGE_SIZE) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+ iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+ msghdr msg = {
+ .msg_name = nullptr,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ // We can't cast to the actual type of the field, because it's different across platforms.
+ .msg_controllen = static_cast<unsigned int>(cmsg_space),
+ .msg_flags = 0,
+ };
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = cmsg_len;
+
+ int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ for (size_t i = 0; i < fds.size(); ++i) {
+ cmsg_fds[i] = fds[i];
+ }
+
+#if defined(__linux__)
+ int flags = MSG_NOSIGNAL;
+#else
+ int flags = 0;
+#endif
+
+ return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, flags));
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+ std::vector<unique_fd>* fds) {
+ fds->clear();
+
+ size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
+ if (cmsg_space >= PAGE_SIZE) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+ iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+ msghdr msg = {
+ .msg_name = nullptr,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ // We can't cast to the actual type of the field, because it's different across platforms.
+ .msg_controllen = static_cast<unsigned int>(cmsg_space),
+ .msg_flags = 0,
+ };
+
+ int flags = MSG_TRUNC | MSG_CTRUNC;
+#if defined(__linux__)
+ flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
+#endif
+
+ ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, flags));
+
+ if (rc == -1) {
+ return -1;
+ }
+
+ int error = 0;
+ if ((msg.msg_flags & MSG_TRUNC)) {
+ LOG(ERROR) << "message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ } else if ((msg.msg_flags & MSG_CTRUNC)) {
+ LOG(ERROR) << "control message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ }
+
+ std::vector<unique_fd> received_fds;
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
+ << "]";
+ error = EBADMSG;
+ continue;
+ }
+
+ // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
+ // some asserts to ensure that CMSG_LEN behaves as we expect.
+#if defined(__linux__)
+#define CMSG_ASSERT static_assert
+#else
+// CMSG_LEN is somehow not constexpr on darwin.
+#define CMSG_ASSERT CHECK
+#endif
+ CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
+
+ if (cmsg->cmsg_len % sizeof(int) != 0) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+ } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+ }
+
+ int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ for (size_t i = 0; i < cmsg_fdcount; ++i) {
+#if !defined(__linux__)
+ // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
+ fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
+#endif
+ received_fds.emplace_back(cmsg_fds[i]);
+ }
+ }
+
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ if (received_fds.size() > max_fds) {
+ LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+ << received_fds.size();
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ *fds = std::move(received_fds);
+ return rc;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 0000000..9ee5c82
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ PLOG(FATAL) << "fstat failed";
+ }
+
+ return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+ bool Seqpacket() { return GetParam(); }
+
+ void SetUp() override {
+ ASSERT_TRUE(
+ android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+ int dup1 = dup(tmp1.fd);
+ ASSERT_NE(-1, dup1);
+ int dup2 = dup(tmp2.fd);
+ ASSERT_NE(-1, dup2);
+
+ fd1.reset(dup1);
+ fd2.reset(dup2);
+
+ ino1 = GetInode(dup1);
+ ino2 = GetInode(dup2);
+ }
+
+ unique_fd send;
+ unique_fd recv;
+
+ TemporaryFile tmp1;
+ TemporaryFile tmp2;
+
+ unique_fd fd1;
+ unique_fd fd2;
+
+ ino_t ino1;
+ ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+ ASSERT_EQ('x', buf[0]);
+ ASSERT_NE(-1, received.get());
+
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+
+ ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+ if (Seqpacket()) {
+ ASSERT_EQ(-1, rc);
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received1.get());
+ ASSERT_EQ(-1, received2.get());
+ } else {
+ ASSERT_EQ(1, rc);
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+ ASSERT_EQ(1, read(recv.get(), buf, 2));
+ }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+ char buf[2];
+ ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+ ASSERT_EQ('a', buf[0]);
+
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+ if (Seqpacket()) {
+ return;
+ }
+
+ // fds are associated with the first byte of the write.
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+ char buf[2];
+ ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+ ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+ std::vector<unique_fd> received1;
+ ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_TRUE(received1.empty());
+
+ unique_fd received2;
+ rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('d', buf[0]);
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+ unique_fd sent1(dup(tmp1.fd));
+ unique_fd sent2(dup(tmp2.fd));
+
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+ char buf[2];
+ std::vector<unique_fd> received;
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/file.cpp b/base/file.cpp
index 2f4a517..adc8984 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -385,7 +385,12 @@
bool Realpath(const std::string& path, std::string* result) {
result->clear();
- char* realpath_buf = realpath(path.c_str(), nullptr);
+ // realpath may exit with EINTR. Retry if so.
+ char* realpath_buf = nullptr;
+ do {
+ realpath_buf = realpath(path.c_str(), nullptr);
+ } while (realpath_buf == nullptr && errno == EINTR);
+
if (realpath_buf == nullptr) {
return false;
}
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 0000000..7f93ddc
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// Helpers for sending and receiving file descriptors across Unix domain sockets.
+//
+// The cmsg(3) API is very hard to get right, with multiple landmines that can
+// lead to death. Almost all of the uses of cmsg in Android make at least one of
+// the following mistakes:
+//
+// - not aligning the cmsg buffer
+// - leaking fds if more fds are received than expected
+// - blindly dereferencing CMSG_DATA without checking the header
+// - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
+// - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
+// - using a length specified in number of fds instead of bytes
+//
+// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
+
+// Send file descriptors across a Unix domain socket.
+//
+// Note that the write can return short if the socket type is SOCK_STREAM. When
+// this happens, file descriptors are still sent to the other end, but with
+// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
+ssize_t SendFileDescriptorVector(int sock, const void* data, size_t len,
+ const std::vector<int>& fds);
+
+// Receive file descriptors from a Unix domain socket.
+//
+// If more FDs (or bytes, for datagram sockets) are received than expected,
+// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
+ssize_t ReceiveFileDescriptorVector(int sock, void* data, size_t len, size_t max_fds,
+ std::vector<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+// SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(int sock, const void* data, size_t len, Args&&... sent_fds) {
+ // Do not allow implicit conversion to int: people might try to do something along the lines of:
+ // SendFileDescriptors(..., std::move(a_unique_fd))
+ // and be surprised when the unique_fd isn't closed afterwards.
+ AssertType<int>(std::forward<Args>(sent_fds)...);
+ std::vector<int> fds;
+ Append(fds, std::forward<Args>(sent_fds)...);
+ return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(int sock, void* data, size_t len, Args&&... received_fds) {
+ std::vector<unique_fd*> fds;
+ Append(fds, std::forward<Args>(received_fds)...);
+
+ std::vector<unique_fd> result;
+ ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+ if (rc == -1 || result.size() != fds.size()) {
+ int err = rc == -1 ? errno : ENOMSG;
+ for (unique_fd* fd : fds) {
+ fd->reset();
+ }
+ errno = err;
+ return -1;
+ }
+
+ for (size_t i = 0; i < fds.size(); ++i) {
+ *fds[i] = std::move(result[i]);
+ }
+ return rc;
+}
+
+#endif
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 0000000..be0683a
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -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.
+ */
+
+#pragma once
+
+#include <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+// template <typename... Args>
+// std::vector<int> CreateVector(Args&&... args) {
+// std::vector<int> result;
+// Append(result, std::forward<Args>(args)...);
+// return result;
+// }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+ collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+ collection.push_back(std::forward<T>(arg));
+ return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+ AssertType<T>(std::forward<Args>(args)...);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index fc5c1ce..8e9716f 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -56,23 +56,17 @@
extern template std::string Join(const std::vector<const char*>&, const std::string&);
// Tests whether 's' starts with 'prefix'.
-// TODO: string_view
-bool StartsWith(const std::string& s, const char* prefix);
-bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
-bool StartsWith(const std::string& s, const std::string& prefix);
-bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
-bool StartsWith(const std::string& s, char prefix);
+bool StartsWith(std::string_view s, std::string_view prefix);
+bool StartsWith(std::string_view s, char prefix);
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
// Tests whether 's' ends with 'suffix'.
-// TODO: string_view
-bool EndsWith(const std::string& s, const char* suffix);
-bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
-bool EndsWith(const std::string& s, const std::string& suffix);
-bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
-bool EndsWith(const std::string& s, char suffix);
+bool EndsWith(std::string_view s, std::string_view suffix);
+bool EndsWith(std::string_view s, char suffix);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
// Tests whether 'lhs' equals 'rhs', ignoring case.
-bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
} // namespace base
} // namespace android
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 5c55e63..53fe6da 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -16,6 +16,8 @@
#pragma once
+#include <mutex>
+
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#define CAPABILITY(x) \
@@ -104,3 +106,39 @@
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+// std::mutex mutex;
+// std::condition_variable cv;
+// std::vector<int> vec GUARDED_BY(mutex);
+//
+// int pop() {
+// std::unique_lock lock(mutex);
+// ScopedLockAssertion lock_assertion(mutex);
+// cv.wait(lock, []() {
+// ScopedLockAssertion lock_assertion(mutex);
+// return !vec.empty();
+// });
+//
+// int result = vec.back();
+// vec.pop_back();
+// return result;
+// }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+ ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+ ~ScopedLockAssertion() RELEASE() {}
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 83213e9..3fa3bea 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -105,6 +105,9 @@
int get() const { return fd_; }
operator int() const { return get(); } // NOLINT
+ // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
+ bool operator!() const = delete;
+
int release() __attribute__((warn_unused_result)) {
tag(fd_, this, nullptr);
int ret = fd_;
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/base/strings.cpp b/base/strings.cpp
index 2d6eef0..bb3167e 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -87,58 +87,33 @@
template std::string Join(const std::vector<std::string>&, const std::string&);
template std::string Join(const std::vector<const char*>&, const std::string&);
-bool StartsWith(const std::string& s, const char* prefix) {
- return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool StartsWith(std::string_view s, std::string_view prefix) {
+ return s.substr(0, prefix.size()) == prefix;
}
-bool StartsWith(const std::string& s, const std::string& prefix) {
- return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool StartsWith(std::string_view s, char prefix) {
+ return !s.empty() && s.front() == prefix;
}
-bool StartsWith(const std::string& s, char prefix) {
- return *s.c_str() == prefix; // Use c_str() to guarantee there is at least a '\0'.
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
+ return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
}
-bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
- return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool EndsWith(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
}
-bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix) {
- return strncasecmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool EndsWith(std::string_view s, char suffix) {
+ return !s.empty() && s.back() == suffix;
}
-static bool EndsWith(const std::string& s, const char* suffix, size_t suffix_length,
- bool case_sensitive) {
- size_t string_length = s.size();
- if (suffix_length > string_length) {
- return false;
- }
- size_t offset = string_length - suffix_length;
- return (case_sensitive ? strncmp : strncasecmp)(s.c_str() + offset, suffix, suffix_length) == 0;
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() &&
+ strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
}
-bool EndsWith(const std::string& s, const char* suffix) {
- return EndsWith(s, suffix, strlen(suffix), true);
-}
-
-bool EndsWith(const std::string& s, const std::string& suffix) {
- return EndsWith(s, suffix.c_str(), suffix.size(), true);
-}
-
-bool EndsWith(const std::string& s, char suffix) {
- return EndsWith(s, &suffix, 1, true);
-}
-
-bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
- return EndsWith(s, suffix, strlen(suffix), false);
-}
-
-bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix) {
- return EndsWith(s, suffix.c_str(), suffix.size(), false);
-}
-
-bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
- return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
+ return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
} // namespace base
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..1ce0ec4 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -293,6 +293,12 @@
{"kernel_panic,dsps", 166},
{"kernel_panic,wcnss", 167},
{"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
+ {"recovery,quiescent", 169},
+ {"reboot,quiescent", 170},
+ {"reboot,rtc", 171},
+ {"reboot,dm-verity_device_corrupted", 172},
+ {"reboot,dm-verity_enforcing", 173},
+ {"reboot,keys_clear", 174},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 4a53a33..0cf3378 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -115,19 +115,14 @@
"libbase",
"libdebuggerd",
"libunwindstack",
- "libdexfile", // libunwindstack dependency
- "libdexfile_external", // libunwindstack dependency
- "libdexfile_support", // libunwindstack dependency
+ "libdexfile_support_static", // libunwindstack dependency
"liblzma",
"libcutils",
],
target: {
recovery: {
exclude_static_libs: [
- "libartbase",
- "libdexfile",
- "libdexfile_external",
- "libdexfile_support",
+ "libdexfile_support_static",
],
},
},
@@ -174,8 +169,7 @@
include_dirs: ["bionic/libc"],
static_libs: [
- "libdexfile_external", // libunwindstack dependency
- "libdexfile_support", // libunwindstack dependency
+ "libdexfile_support_static", // libunwindstack dependency
"libunwindstack",
"liblzma",
"libbase",
@@ -185,8 +179,7 @@
target: {
recovery: {
exclude_static_libs: [
- "libdexfile_external",
- "libdexfile_support",
+ "libdexfile_support_static",
],
},
},
@@ -225,13 +218,11 @@
"liblog",
"libminijail",
"libnativehelper",
+ "libunwindstack",
],
static_libs: [
"libdebuggerd",
- "libdexfile_external", // libunwindstack dependency
- "libdexfile_support", // libunwindstack dependency
- "libunwindstack",
],
local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 610b96b..60eb241 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -26,6 +26,7 @@
#include <chrono>
+#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -41,6 +42,7 @@
using namespace std::chrono_literals;
+using android::base::SendFileDescriptors;
using android::base::unique_fd;
static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
@@ -146,15 +148,16 @@
PLOG(ERROR) << "failed to set pipe buffer size";
}
- if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+ ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+ pipe_write.reset();
+ if (rc != sizeof(req)) {
PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
return false;
}
// Check to make sure we've successfully registered.
InterceptResponse response;
- ssize_t rc =
- TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+ rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
<< "timeout reached?";
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0bdfbf..3041664 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -193,6 +193,7 @@
fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
#endif
+ fprintf(stderr, " xom read execute-only memory\n");
fprintf(stderr, "\n");
fprintf(stderr, " LOG_ALWAYS_FATAL call liblog LOG_ALWAYS_FATAL\n");
fprintf(stderr, " LOG_ALWAYS_FATAL_IF call liblog LOG_ALWAYS_FATAL_IF\n");
@@ -314,6 +315,11 @@
} else if (!strcasecmp(arg, "seccomp")) {
set_system_seccomp_filter();
syscall(99999);
+#if defined(__LP64__)
+ } else if (!strcasecmp(arg, "xom")) {
+ // Try to read part of our code, which will fail if XOM is active.
+ printf("*%lx = %lx\n", reinterpret_cast<long>(usage), *reinterpret_cast<long*>(usage));
+#endif
#if defined(__arm__)
} else if (!strcasecmp(arg, "kuser_helper_version")) {
return __kuser_helper_version;
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index bea8b43..64df53e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -32,6 +32,7 @@
#include <android/fdsan.h>
#include <android/set_abort_message.h>
+#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -53,6 +54,8 @@
#include "util.h"
using namespace std::chrono_literals;
+
+using android::base::SendFileDescriptors;
using android::base::unique_fd;
#if defined(__LP64__)
@@ -123,12 +126,14 @@
ASSERT_GE(pipe_buffer_size, 1024 * 1024);
- if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+ ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());
+ output_pipe_write.reset();
+ if (rc != sizeof(req)) {
FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
}
InterceptResponse response;
- ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+ rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
if (rc == -1) {
FAIL() << "failed to read response from tombstoned: " << strerror(errno);
} else if (rc == 0) {
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/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index 753ebcb..94fcfb2 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -74,6 +74,7 @@
return;
}
+ unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < unwinder->NumFrames(); i++) {
_LOG(&log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(i).c_str());
}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 7c5304e..238c00c 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -18,6 +18,7 @@
#ifndef _DEBUGGERD_UTILITY_H
#define _DEBUGGERD_UTILITY_H
+#include <inttypes.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/types.h>
@@ -25,7 +26,6 @@
#include <string>
#include <android-base/macros.h>
-#include <backtrace/Backtrace.h>
struct log_t {
// Tombstone file descriptor.
@@ -61,6 +61,14 @@
OPEN_FILES
};
+#if defined(__LP64__)
+#define PRIPTR "016" PRIx64
+typedef uint64_t word_t;
+#else
+#define PRIPTR "08" PRIx64
+typedef uint32_t word_t;
+#endif
+
// Log information onto the tombstone.
void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index eed5bd3..3196ce8 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -15,6 +15,7 @@
*/
#include <stdlib.h>
+#include <sys/mman.h>
#include <time.h>
#include <memory>
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 8798ad3..cc337ed 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <time.h>
@@ -316,7 +317,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) {
@@ -371,6 +372,7 @@
}
void dump_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+ unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < unwinder->NumFrames(); i++) {
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d0c5234..7aebea8 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,7 +35,6 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <backtrace/Backtrace.h>
#include <debuggerd/handler.h>
#include <log/log.h>
#include <unwindstack/Memory.h>
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index c446dbb..7d25c50 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -24,6 +24,7 @@
#include <event2/event.h>
#include <event2/listener.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@@ -31,6 +32,7 @@
#include "protocol.h"
#include "util.h"
+using android::base::ReceiveFileDescriptors;
using android::base::unique_fd;
static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
@@ -96,7 +98,8 @@
{
unique_fd rcv_fd;
InterceptRequest intercept_request;
- ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+ ssize_t result =
+ ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
if (result == -1) {
PLOG(WARNING) << "failed to read from intercept socket";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index ad92067..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -31,6 +31,7 @@
#include <event2/listener.h>
#include <event2/thread.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -45,6 +46,7 @@
#include "intercept_manager.h"
using android::base::GetIntProperty;
+using android::base::SendFileDescriptors;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -224,7 +226,10 @@
TombstonedCrashPacket response = {
.packet_type = CrashPacketType::kPerformDump
};
- ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
+ ssize_t rc =
+ SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
+ output_fd.reset();
+
if (rc == -1) {
PLOG(WARNING) << "failed to send response to CrashRequest";
goto fail;
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index bdb4c1a..2c23c98 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -21,6 +21,7 @@
#include <utility>
+#include <android-base/cmsg.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <cutils/sockets.h>
@@ -28,6 +29,7 @@
#include "protocol.h"
#include "util.h"
+using android::base::ReceiveFileDescriptors;
using android::base::unique_fd;
bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
@@ -53,7 +55,7 @@
}
unique_fd tmp_output_fd;
- ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+ ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
if (rc == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"failed to read response to DumpRequest packet: %s", strerror(errno));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 50c5efc..a37b3b9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,73 +24,9 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include "protocol.h"
-using android::base::unique_fd;
-
-ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
- iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
- msghdr msg = {
- .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
- };
- auto cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
-
- return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
-}
-
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullable out_fd) {
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
- iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
- msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cmsg_buf,
- .msg_controllen = sizeof(cmsg_buf),
- .msg_flags = 0,
- };
- auto cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-
- ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
- if (result == -1) {
- return -1;
- }
-
- unique_fd fd;
- bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
- if (received_fd) {
- fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
- }
-
- if ((msg.msg_flags & MSG_TRUNC) != 0) {
- errno = EFBIG;
- return -1;
- } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
- errno = ERANGE;
- return -1;
- }
-
- if (out_fd) {
- *out_fd = std::move(fd);
- } else if (received_fd) {
- errno = ERANGE;
- return -1;
- }
-
- return result;
-}
-
std::string get_process_name(pid_t pid) {
std::string result = "<unknown>";
android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 8260b44..e964423 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -21,29 +21,5 @@
#include <sys/cdefs.h>
#include <sys/types.h>
-#include <android-base/unique_fd.h>
-
-// *** WARNING ***
-// tombstoned's sockets are SOCK_SEQPACKET sockets.
-// Short reads are treated as errors and short writes are assumed to not happen.
-
-// Sends a packet with an attached fd.
-ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
-
-// Receives a packet and optionally, its attached fd.
-// If out_fd is non-null, packets can optionally have an attached fd.
-// If out_fd is null, received packets must not have an attached fd.
-//
-// Errors:
-// EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
-// The first len bytes of the packet are stored in data, but the
-// rest of the packet is dropped.
-// ERANGE: too many file descriptors were attached to the packet.
-// ENOMSG: not enough file descriptors were attached to the packet.
-//
-// plus any errors returned by the underlying recvmsg.
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
- android::base::unique_fd* _Nullable out_fd);
-
std::string get_process_name(pid_t pid);
std::string get_thread_name(pid_t tid);
diff --git a/adf/Android.bp b/deprecated-adf/Android.bp
similarity index 100%
rename from adf/Android.bp
rename to deprecated-adf/Android.bp
diff --git a/adf/OWNERS b/deprecated-adf/OWNERS
similarity index 100%
rename from adf/OWNERS
rename to deprecated-adf/OWNERS
diff --git a/adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
similarity index 100%
rename from adf/libadf/Android.bp
rename to deprecated-adf/libadf/Android.bp
diff --git a/adf/libadf/adf.cpp b/deprecated-adf/libadf/adf.cpp
similarity index 100%
rename from adf/libadf/adf.cpp
rename to deprecated-adf/libadf/adf.cpp
diff --git a/adf/libadf/include/adf/adf.h b/deprecated-adf/libadf/include/adf/adf.h
similarity index 100%
rename from adf/libadf/include/adf/adf.h
rename to deprecated-adf/libadf/include/adf/adf.h
diff --git a/adf/libadf/include/video/adf.h b/deprecated-adf/libadf/include/video/adf.h
similarity index 100%
rename from adf/libadf/include/video/adf.h
rename to deprecated-adf/libadf/include/video/adf.h
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
similarity index 100%
rename from adf/libadf/original-kernel-headers/video/adf.h
rename to deprecated-adf/libadf/original-kernel-headers/video/adf.h
diff --git a/adf/libadf/tests/Android.bp b/deprecated-adf/libadf/tests/Android.bp
similarity index 100%
rename from adf/libadf/tests/Android.bp
rename to deprecated-adf/libadf/tests/Android.bp
diff --git a/adf/libadf/tests/adf_test.cpp b/deprecated-adf/libadf/tests/adf_test.cpp
similarity index 100%
rename from adf/libadf/tests/adf_test.cpp
rename to deprecated-adf/libadf/tests/adf_test.cpp
diff --git a/adf/libadfhwc/Android.bp b/deprecated-adf/libadfhwc/Android.bp
similarity index 100%
rename from adf/libadfhwc/Android.bp
rename to deprecated-adf/libadfhwc/Android.bp
diff --git a/adf/libadfhwc/adfhwc.cpp b/deprecated-adf/libadfhwc/adfhwc.cpp
similarity index 100%
rename from adf/libadfhwc/adfhwc.cpp
rename to deprecated-adf/libadfhwc/adfhwc.cpp
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
similarity index 100%
rename from adf/libadfhwc/include/adfhwc/adfhwc.h
rename to deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 4cdd8bc..f8f7eb3 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1407,7 +1407,7 @@
int LocalImageSource::OpenFile(const std::string& name) const {
auto path = find_item_given_name(name);
- return open(path.c_str(), O_RDONLY);
+ return open(path.c_str(), O_RDONLY | O_BINARY);
}
static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
@@ -1805,7 +1805,7 @@
auto format = [&](const std::string& partition) {
fb_perform_format(partition, 0, type_override, size_override, "");
};
- do_for_partitions(partition.c_str(), slot_override, format, true);
+ do_for_partitions(partition, slot_override, format, true);
} else if (command == "signature") {
std::string filename = next_arg(&args);
std::vector<char> data;
@@ -1876,7 +1876,7 @@
}
do_flash(partition.c_str(), fname.c_str());
};
- do_for_partitions(pname.c_str(), slot_override, flash, true);
+ do_for_partitions(pname, slot_override, flash, true);
} else if (command == "flash:raw") {
std::string partition = next_arg(&args);
std::string kernel = next_arg(&args);
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/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 280cfb6..bc13a8c 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -55,9 +55,11 @@
#include "test_utils.h"
#include "usb_transport_sniffer.h"
+using namespace std::literals::chrono_literals;
+
namespace fastboot {
-int FastBootTest::MatchFastboot(usb_ifc_info* info, const char* local_serial) {
+int FastBootTest::MatchFastboot(usb_ifc_info* info, const std::string& local_serial) {
if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
return -1;
}
@@ -66,8 +68,8 @@
// require matching serial number or device path if requested
// at the command line with the -s option.
- if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
- strcmp(local_serial, info->device_path) != 0))
+ if (!local_serial.empty() && local_serial != info->serial_number &&
+ local_serial != info->device_path)
return -1;
return 0;
}
@@ -111,7 +113,9 @@
ASSERT_TRUE(UsbStillAvailible()); // The device disconnected
}
- const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
+ const auto matcher = [](usb_ifc_info* info) -> int {
+ return MatchFastboot(info, device_serial);
+ };
for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
if (usb)
@@ -128,10 +132,14 @@
ASSERT_EQ(device_path, cb_scratch); // The path can not change
}
fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ // No error checking since non-A/B devices may not support the command
+ fb->GetVar("current-slot", &initial_slot);
}
void FastBootTest::TearDown() {
EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+ // No error checking since non-A/B devices may not support the command
+ fb->SetActive(initial_slot);
TearDownSerial();
@@ -159,6 +167,28 @@
}
}
+void FastBootTest::ReconnectFastbootDevice() {
+ fb.reset();
+ transport.reset();
+ while (UsbStillAvailible())
+ ;
+ printf("WAITING FOR DEVICE\n");
+ // Need to wait for device
+ const auto matcher = [](usb_ifc_info* info) -> int {
+ return MatchFastboot(info, device_serial);
+ };
+ while (!transport) {
+ std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+ if (usb) {
+ transport = std::unique_ptr<UsbTransportSniffer>(
+ new UsbTransportSniffer(std::move(usb), serial_port));
+ }
+ std::this_thread::sleep_for(1s);
+ }
+ device_path = cb_scratch;
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+}
+
void FastBootTest::SetLockState(bool unlock, bool assert_change) {
if (!fb) {
return;
@@ -197,25 +227,8 @@
std::string cmd = unlock ? "unlock" : "lock";
ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
<< "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
- fb.reset();
- transport.reset();
printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
- while (UsbStillAvailible())
- ; // Wait for disconnect
- printf("WAITING FOR DEVICE");
- // Need to wait for device
- const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
- while (!transport) {
- std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
- if (usb) {
- transport = std::unique_ptr<UsbTransportSniffer>(
- new UsbTransportSniffer(std::move(usb), serial_port));
- }
- std::this_thread::sleep_for(std::chrono::milliseconds(1000));
- putchar('.');
- }
- device_path = cb_scratch;
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ ReconnectFastbootDevice();
if (assert_change) {
ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
ASSERT_EQ(resp, unlock ? "yes" : "no")
@@ -227,7 +240,9 @@
std::string FastBootTest::device_path = "";
std::string FastBootTest::cb_scratch = "";
+std::string FastBootTest::initial_slot = "";
int FastBootTest::serial_port = 0;
+std::string FastBootTest::device_serial = "";
template <bool UNLOCKED>
void ModeTest<UNLOCKED>::SetUp() {
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index e0f829e..c71c897 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -43,11 +43,13 @@
class FastBootTest : public testing::Test {
public:
static int serial_port;
+ static std::string device_serial;
static constexpr int MAX_USB_TRIES = 10;
- static int MatchFastboot(usb_ifc_info* info, const char* local_serial = nullptr);
+ static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = "");
bool UsbStillAvailible();
bool UserSpaceFastboot();
+ void ReconnectFastbootDevice();
protected:
RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
@@ -69,6 +71,7 @@
// This is an annoying hack
static std::string cb_scratch;
static std::string device_path;
+ static std::string initial_slot;
};
template <bool UNLOCKED>
@@ -86,6 +89,7 @@
// differently
class BasicFunctionality : public ModeTest<true> {};
class Conformance : public ModeTest<true> {};
+class LogicalPartitionCompliance : public ModeTest<true> {};
class UnlockPermissions : public ModeTest<true> {};
class LockPermissions : public ModeTest<false> {};
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index ef34771..ff918a7 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -162,7 +162,7 @@
// Test that USB even works
TEST(USBFunctionality, USBConnect) {
const auto matcher = [](usb_ifc_info* info) -> int {
- return FastBootTest::MatchFastboot(info, nullptr);
+ return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
};
Transport* transport = nullptr;
for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
@@ -177,6 +177,103 @@
}
}
+// Test commands related to super partition
+TEST_F(LogicalPartitionCompliance, SuperPartition) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ std::string partition_type;
+ // getvar partition-type:super must fail for retrofit devices because the
+ // partition does not exist.
+ if (fb->GetVar("partition-type:super", &partition_type) == SUCCESS) {
+ std::string is_logical;
+ EXPECT_EQ(fb->GetVar("is-logical:super", &is_logical), SUCCESS)
+ << "getvar is-logical:super failed";
+ EXPECT_EQ(is_logical, "no") << "super must not be a logical partition";
+ std::string super_name;
+ EXPECT_EQ(fb->GetVar("super-partition-name", &super_name), SUCCESS)
+ << "'getvar super-partition-name' failed";
+ EXPECT_EQ(super_name, "super") << "'getvar super-partition-name' must return 'super' for "
+ "device with a super partition";
+ }
+}
+
+// Test 'fastboot getvar is-logical'
+TEST_F(LogicalPartitionCompliance, GetVarIsLogical) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ std::string has_slot;
+ EXPECT_EQ(fb->GetVar("has-slot:system", &has_slot), SUCCESS) << "getvar has-slot:system failed";
+ std::string is_logical_cmd_system = "is-logical:system";
+ std::string is_logical_cmd_vendor = "is-logical:vendor";
+ std::string is_logical_cmd_boot = "is-logical:boot";
+ if (has_slot == "yes") {
+ std::string current_slot;
+ ASSERT_EQ(fb->GetVar("current-slot", ¤t_slot), SUCCESS)
+ << "getvar current-slot failed";
+ std::string slot_suffix = "_" + current_slot;
+ is_logical_cmd_system += slot_suffix;
+ is_logical_cmd_vendor += slot_suffix;
+ is_logical_cmd_boot += slot_suffix;
+ }
+ std::string is_logical;
+ EXPECT_EQ(fb->GetVar(is_logical_cmd_system, &is_logical), SUCCESS)
+ << "system must be a logical partition";
+ EXPECT_EQ(is_logical, "yes");
+ EXPECT_EQ(fb->GetVar(is_logical_cmd_vendor, &is_logical), SUCCESS)
+ << "vendor must be a logical partition";
+ EXPECT_EQ(is_logical, "yes");
+ EXPECT_EQ(fb->GetVar(is_logical_cmd_boot, &is_logical), SUCCESS)
+ << "boot must not be logical partition";
+ EXPECT_EQ(is_logical, "no");
+}
+
+TEST_F(LogicalPartitionCompliance, FastbootRebootTest) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+ // Test 'fastboot reboot bootloader' from fastbootd
+ fb->RebootTo("bootloader");
+
+ // Test fastboot reboot fastboot from bootloader
+ ReconnectFastbootDevice();
+ ASSERT_FALSE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+ fb->RebootTo("fastboot");
+
+ ReconnectFastbootDevice();
+ ASSERT_TRUE(UserSpaceFastboot());
+}
+
+// Testing creation/resize/delete of logical partitions
+TEST_F(LogicalPartitionCompliance, CreateResizeDeleteLP) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Testing 'fastboot create-logical-partition' command";
+ EXPECT_EQ(fb->CreatePartition("test_partition_a", "0"), SUCCESS)
+ << "create-logical-partition failed";
+ GTEST_LOG_(INFO) << "Testing 'fastboot resize-logical-partition' command";
+ EXPECT_EQ(fb->ResizePartition("test_partition_a", "4096"), SUCCESS)
+ << "resize-logical-partition failed";
+ std::vector<char> buf(4096);
+
+ GTEST_LOG_(INFO) << "Flashing a logical partition..";
+ EXPECT_EQ(fb->FlashPartition("test_partition_a", buf), SUCCESS)
+ << "flash logical -partition failed";
+ GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+ // Reboot to bootloader mode and attempt to flash the logical partitions
+ fb->RebootTo("bootloader");
+
+ ReconnectFastbootDevice();
+ ASSERT_FALSE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Attempt to flash a logical partition..";
+ EXPECT_EQ(fb->FlashPartition("test_partition", buf), DEVICE_FAIL)
+ << "flash logical partition must fail in bootloader";
+ GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+ fb->RebootTo("fastboot");
+
+ ReconnectFastbootDevice();
+ ASSERT_TRUE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
+ EXPECT_EQ(fb->DeletePartition("test_partition_a"), SUCCESS)
+ << "delete logical-partition failed";
+}
+
// Conformance tests
TEST_F(Conformance, GetVar) {
std::string product;
@@ -1641,10 +1738,14 @@
fastboot::GenerateXmlTests(fastboot::config);
}
+ if (args.find("serial") != args.end()) {
+ fastboot::FastBootTest::device_serial = args.at("serial");
+ }
+
setbuf(stdout, NULL); // no buffering
printf("<Waiting for Device>\n");
const auto matcher = [](usb_ifc_info* info) -> int {
- return fastboot::FastBootTest::MatchFastboot(info, nullptr);
+ return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
};
Transport* transport = nullptr;
while (!transport) {
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/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index 2aac260..f89e598 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -94,7 +94,7 @@
and thus free dynamic partition space.
- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
with "*overlayfs: override_creds=off option bypass creator_cred*"
- if kernel is higher than 4.6.
+ if kernel is 4.4 or higher.
The patch is available on the upstream mailing list and the latest as of
Feb 8 2019 is https://lore.kernel.org/patchwork/patch/1009299/.
This patch adds an override_creds _mount_ option to overlayfs that
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index b69e773..045bb48 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!";
@@ -1098,9 +1127,7 @@
}
} else if ((current_entry.fs_mgr_flags.verify)) {
int rc = fs_mgr_setup_verity(¤t_entry, true);
- if (__android_log_is_debuggable() &&
- (rc == FS_MGR_SETUP_VERITY_DISABLED ||
- rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+ if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -1322,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!";
@@ -1331,9 +1358,7 @@
}
} else if (fstab_entry.fs_mgr_flags.verify) {
int rc = fs_mgr_setup_verity(&fstab_entry, true);
- if (__android_log_is_debuggable() &&
- (rc == FS_MGR_SETUP_VERITY_DISABLED ||
- rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+ if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -1584,14 +1609,7 @@
DeviceMapper& dm = DeviceMapper::Instance();
- std::string mount_point;
- if (entry.mount_point == "/") {
- // In AVB, the dm device name is vroot instead of system.
- mount_point = entry.fs_mgr_flags.avb ? "vroot" : "system";
- } else {
- mount_point = Basename(entry.mount_point);
- }
-
+ std::string mount_point = GetVerityDeviceName(entry);
if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
return false;
}
@@ -1614,6 +1632,27 @@
return false;
}
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
+ if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+ return false;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string device = GetVerityDeviceName(entry);
+
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
+ return false;
+ }
+ for (const auto& target : table) {
+ if (strcmp(target.spec.target_type, "verity") == 0 &&
+ target.data.find("check_at_most_once") != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
std::string fs_mgr_get_super_partition_name(int slot) {
// Devices upgrading to dynamic partitions are allowed to specify a super
// partition name, assumed to be A/B (non-A/B retrofit is not supported).
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4d44fcf..4043fc6 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;
}
@@ -588,18 +588,7 @@
return boot_devices;
}
-void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
- auto iter = std::remove_if(fstab->begin(), fstab->end(),
- [&](const auto& entry) { return entry.mount_point == mount_point; });
- fstab->erase(iter, fstab->end());
-}
-
-void TransformFstabForGsi(Fstab* fstab) {
- EraseFstabEntry(fstab, "/system");
- EraseFstabEntry(fstab, "/data");
-
- fstab->emplace_back(BuildGsiSystemFstabEntry());
-
+FstabEntry BuildGsiUserdataFstabEntry() {
constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
FstabEntry userdata = {
@@ -615,7 +604,41 @@
userdata.fs_mgr_flags.quota = true;
userdata.fs_mgr_flags.late_mount = true;
userdata.fs_mgr_flags.formattable = true;
- fstab->emplace_back(userdata);
+ return userdata;
+}
+
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+ auto iter = std::remove_if(fstab->begin(), fstab->end(),
+ [&](const auto& entry) { return entry.mount_point == mount_point; });
+ if (iter != fstab->end()) {
+ fstab->erase(iter, fstab->end());
+ return true;
+ }
+ return false;
+}
+
+void TransformFstabForGsi(Fstab* fstab) {
+ // Inherit fstab properties for userdata.
+ FstabEntry userdata;
+ if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
+ userdata = *entry;
+ userdata.blk_device = "userdata_gsi";
+ userdata.fs_mgr_flags.logical = true;
+ userdata.fs_mgr_flags.formattable = true;
+ if (!userdata.key_dir.empty()) {
+ userdata.key_dir += "/gsi";
+ }
+ } else {
+ userdata = BuildGsiUserdataFstabEntry();
+ }
+
+ if (EraseFstabEntry(fstab, "/system")) {
+ fstab->emplace_back(BuildGsiSystemFstabEntry());
+ }
+
+ if (EraseFstabEntry(fstab, "/data")) {
+ fstab->emplace_back(userdata);
+ }
}
} // namespace
@@ -731,19 +754,32 @@
FstabEntry BuildGsiSystemFstabEntry() {
// .logical_partition_name is required to look up AVB Hashtree descriptors.
- FstabEntry system = {.blk_device = "system_gsi",
- .mount_point = "/system",
- .fs_type = "ext4",
- .flags = MS_RDONLY,
- .fs_options = "barrier=1",
- .avb_key = "/gsi.avbpubkey",
- .logical_partition_name = "system"};
+ FstabEntry system = {
+ .blk_device = "system_gsi",
+ .mount_point = "/system",
+ .fs_type = "ext4",
+ .flags = MS_RDONLY,
+ .fs_options = "barrier=1",
+ // could add more keys separated by ':'.
+ .avb_keys = "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey",
+ .logical_partition_name = "system"};
system.fs_mgr_flags.wait = true;
system.fs_mgr_flags.logical = true;
system.fs_mgr_flags.first_stage_mount = true;
return system;
}
+std::string GetVerityDeviceName(const FstabEntry& entry) {
+ std::string base_device;
+ if (entry.mount_point == "/") {
+ // In AVB, the dm device name is vroot instead of system.
+ base_device = entry.fs_mgr_flags.avb ? "vroot" : "system";
+ } else {
+ base_device = android::base::Basename(entry.mount_point);
+ }
+ return base_device + "-verity";
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 87729cd..8984752 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -78,6 +78,10 @@
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
+ return {};
+}
+
bool fs_mgr_overlayfs_mount_all(Fstab*) {
return false;
}
@@ -153,7 +157,15 @@
fs_mgr_update_logical_partition(entry);
}
auto save_errno = errno;
+ errno = 0;
auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
+ if (!has_shared_blocks && (entry->mount_point == "/system")) {
+ has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
+ }
+ // special case for first stage init for system as root (taimen)
+ if (!has_shared_blocks && (errno == ENOENT) && (entry->blk_device == "/dev/root")) {
+ has_shared_blocks = true;
+ }
errno = save_errno;
return has_shared_blocks;
}
@@ -238,8 +250,7 @@
return ret;
}
-const char* fs_mgr_mount_point(const char* mount_point) {
- if (!mount_point) return mount_point;
+const std::string fs_mgr_mount_point(const std::string& mount_point) {
if ("/"s != mount_point) return mount_point;
return "/system";
}
@@ -526,40 +537,6 @@
}
}
-std::vector<std::string> fs_mgr_candidate_list(Fstab* fstab, const char* mount_point = nullptr) {
- std::vector<std::string> mounts;
- for (auto& entry : *fstab) {
- if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
- !fs_mgr_wants_overlayfs(&entry)) {
- continue;
- }
- std::string new_mount_point(fs_mgr_mount_point(entry.mount_point.c_str()));
- if (mount_point && (new_mount_point != mount_point)) continue;
-
- auto saved_errno = errno;
- auto verity_enabled = fs_mgr_is_verity_enabled(entry);
- if (errno == ENOENT || errno == ENXIO) errno = saved_errno;
- if (verity_enabled) continue;
-
- auto duplicate_or_more_specific = false;
- for (auto it = mounts.begin(); it != mounts.end();) {
- if ((*it == new_mount_point) ||
- (android::base::StartsWith(new_mount_point, *it + "/"))) {
- duplicate_or_more_specific = true;
- break;
- }
- if (android::base::StartsWith(*it, new_mount_point + "/")) {
- it = mounts.erase(it);
- } else {
- ++it;
- }
- }
- if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
- }
-
- return mounts;
-}
-
// Mount kScratchMountPoint
bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
bool readonly = false) {
@@ -569,6 +546,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;
}
@@ -580,6 +561,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;
@@ -589,12 +572,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);
@@ -631,7 +615,9 @@
if (!dm.GetDmDevicePathByName(partition_name, &path)) {
// non-DAP A/B device?
if (fs_mgr_access(super_device)) return "";
- path = kPhysicalDevice + "system" + (slot_number ? "_a" : "_b");
+ auto other_slot = fs_mgr_get_other_slot_suffix();
+ if (other_slot.empty()) return "";
+ path = kPhysicalDevice + "system" + other_slot;
}
}
return scratch_device_cache = path;
@@ -734,7 +720,7 @@
}
if (changed || partition_create) {
- if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+ if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
scratch_device))
return false;
@@ -795,19 +781,50 @@
// 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
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+ Fstab candidates;
+ for (const auto& entry : fstab) {
+ FstabEntry new_entry = entry;
+ if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
+ !fs_mgr_wants_overlayfs(&new_entry)) {
+ continue;
+ }
+ auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
+ auto duplicate_or_more_specific = false;
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ auto it_mount_point = fs_mgr_mount_point(it->mount_point);
+ if ((it_mount_point == new_mount_point) ||
+ (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
+ duplicate_or_more_specific = true;
+ break;
+ }
+ if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
+ it = candidates.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+ }
+ return candidates;
+}
+
bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
auto ret = false;
if (fs_mgr_overlayfs_invalid()) return ret;
auto scratch_can_be_mounted = true;
- for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ auto mount_point = fs_mgr_mount_point(entry.mount_point);
if (fs_mgr_overlayfs_already_mounted(mount_point)) {
ret = true;
continue;
@@ -840,8 +857,9 @@
return {};
}
- for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
- if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) continue;
auto device = fs_mgr_overlayfs_scratch_device();
if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
return {device};
@@ -867,8 +885,24 @@
return false;
}
errno = save_errno;
- auto mounts = fs_mgr_candidate_list(&fstab, fs_mgr_mount_point(mount_point));
- if (mounts.empty()) return ret;
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ if (mount_point &&
+ (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
+ it = candidates.erase(it);
+ continue;
+ }
+ save_errno = errno;
+ auto verity_enabled = fs_mgr_is_verity_enabled(*it);
+ if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+ if (verity_enabled) {
+ it = candidates.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (candidates.empty()) return ret;
std::string dir;
for (const auto& overlay_mount_point : kOverlayMountPoints) {
@@ -891,8 +925,8 @@
std::string overlay;
ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
- for (const auto& fsrec_mount_point : mounts) {
- ret |= fs_mgr_overlayfs_setup_one(overlay, fsrec_mount_point, change);
+ for (const auto& entry : candidates) {
+ ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
}
return ret;
}
@@ -901,7 +935,6 @@
// If something is altered, set *change.
bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
if (change) *change = false;
- mount_point = fs_mgr_mount_point(mount_point);
auto ret = true;
// If scratch exists, but is not mounted, lets gain access to clean
// specific override entries.
@@ -912,14 +945,15 @@
auto slot_number = fs_mgr_overlayfs_slot_number();
auto super_device = fs_mgr_overlayfs_super_device(slot_number);
const auto partition_name = android::base::Basename(kScratchMountPoint);
- CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+ CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
&scratch_device);
}
mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
fs_mgr_overlayfs_scratch_mount_type());
}
for (const auto& overlay_mount_point : kOverlayMountPoints) {
- ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
+ ret &= fs_mgr_overlayfs_teardown_one(
+ overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
}
if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
// After obligatory teardown to make sure everything is clean, but if
@@ -946,8 +980,9 @@
return false;
}
if (fs_mgr_overlayfs_invalid()) return false;
- for (const auto& mount_point : fs_mgr_candidate_list(&fstab)) {
- if (fs_mgr_overlayfs_already_mounted(mount_point)) return true;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
}
return false;
}
@@ -1009,7 +1044,7 @@
if (major > 4) {
return OverlayfsValidResult::kNotSupported;
}
- if (minor > 6) {
+ if (minor > 3) {
return OverlayfsValidResult::kNotSupported;
}
return OverlayfsValidResult::kOk;
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..cbe2008
--- /dev/null
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -0,0 +1,388 @@
+/*
+ * 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);
+ // Manufacture a / entry from /proc/mounts if missing.
+ if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
+ android::fs_mgr::Fstab mounts;
+ if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
+ if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
+ }
+ }
+ }
+ }
+ 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 " << argv[optind] << ", 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 " << argv[optind] << " 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 " << argv[optind] << ", 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;
+ }
+ // Find overlayfs mount point?
+ 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);
+
+ // Find system-as-root mount point?
+ if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+ GetEntryForMountPoint(&mounts, "/")) {
+ mount_point = "/";
+ }
+
+ // 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(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+ // If errno is 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.
+ if (errno == EROFS) {
+ LOG(ERROR) << "Consider providing all the dependencies to enable overlayfs";
+ }
+ retval = REMOUNT_FAILED;
+ }
+
+ if (reboot_later) reboot(false);
+
+ return retval;
+}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index a3bb852..8abe609 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -77,6 +77,10 @@
bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
+// Returns true if the given fstab entry has verity enabled, *and* the verity
+// device is in "check_at_most_once" mode.
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry);
+
int fs_mgr_do_format(const android::fs_mgr::FstabEntry& entry, bool reserve_footer);
#define FS_MGR_SETUP_VERITY_SKIPPED (-3)
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 64682cc..6aaf1f3 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,6 +21,8 @@
#include <string>
#include <vector>
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+
bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index a3d9fdd..88da41d 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;
@@ -107,5 +107,10 @@
std::set<std::string> GetBootDevices();
+// Return the name of the dm-verity device for the given fstab entry. This does
+// not check whether the device is valid or exists; it merely returns the
+// expected name.
+std::string GetVerityDeviceName(const FstabEntry& entry);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libdm/Android.mk b/fs_mgr/libdm/Android.mk
new file mode 100644
index 0000000..6aedc25
--- /dev/null
+++ b/fs_mgr/libdm/Android.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsKernelLibdmTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libdm/AndroidTest.xml b/fs_mgr/libdm/AndroidTest.xml
new file mode 100644
index 0000000..b4e0c23
--- /dev/null
+++ b/fs_mgr/libdm/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?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 VTS VtsKernelLibdmTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <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="VtsKernelLibdmTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/libdm_test/libdm_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/libdm_test/libdm_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="1m"/>
+ <option name="precondition-first-api-level" value="29" />
+ </test>
+</configuration>
+
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/Android.mk b/fs_mgr/libfiemap_writer/Android.mk
new file mode 100644
index 0000000..3c07b8e
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/Android.mk
@@ -0,0 +1,22 @@
+#
+# 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 := VtsFiemapWriterTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libfiemap_writer/AndroidTest.xml b/fs_mgr/libfiemap_writer/AndroidTest.xml
new file mode 100644
index 0000000..08cff0e
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/AndroidTest.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 VTS VtsFiemapWriterTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <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="VtsFiemapWriterTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="1m"/>
+ </test>
+</configuration>
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index b9b75f8..e3803d5 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 (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;
+ 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;
}
@@ -341,9 +407,25 @@
#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
#endif
+ // f2fs: export FS_NOCOW_FL flag to user
+ uint32_t flags;
+ int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
+ if (error < 0) {
+ if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+ PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
+ } else {
+ PLOG(ERROR) << "Failed to get flags: " << file_path;
+ }
+ return false;
+ }
+ if (!(flags & FS_NOCOW_FL)) {
+ LOG(ERROR) << "It is not pinned: " << file_path;
+ return false;
+ }
+
// F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
uint32_t moved_blocks_nr;
- int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
+ error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
if (error < 0) {
if ((errno == ENOTTY) || (errno == ENOTSUP)) {
PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
@@ -433,6 +515,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 +588,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 +625,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 +648,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 41fa959..dda7dfd 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,23 +33,29 @@
#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;
using namespace android::fiemap_writer;
using unique_fd = android::base::unique_fd;
using LoopDevice = android::dm::LoopDevice;
-std::string testbdev = "";
+std::string gTestDir;
uint64_t testfile_size = 536870912; // default of 512MiB
+size_t gBlockSize = 0;
class FiemapWriterTest : public ::testing::Test {
protected:
void SetUp() override {
const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- std::string exec_dir = ::android::base::GetExecutableDirectory();
- testfile = ::android::base::StringPrintf("%s/testdata/%s", exec_dir.c_str(), tinfo->name());
+ testfile = gTestDir + "/"s + tinfo->name();
}
void TearDown() override { unlink(testfile.c_str()); }
@@ -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,9 +145,10 @@
}
TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
- EXPECT_EQ(fptr->size(), 4096);
- EXPECT_EQ(fptr->bdev_path(), testbdev);
+ 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);
}
TEST_F(FiemapWriterTest, CheckFileCreated) {
@@ -129,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) {
@@ -138,45 +173,223 @@
EXPECT_GT(fptr->extents().size(), 0);
}
-class TestExistingFile : public ::testing::Test {
- protected:
- void SetUp() override {
- std::string exec_dir = ::android::base::GetExecutableDirectory();
- unaligned_file_ = exec_dir + "/testdata/unaligned_file";
- file_4k_ = exec_dir + "/testdata/file_4k";
- file_32k_ = exec_dir + "/testdata/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,17 +476,51 @@
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 << "Filepath with its bdev path must be provided as follows:" << endl;
- cerr << " $ fiemap_writer_test </dev/block/XXXX" << endl;
- cerr << " where, /dev/block/XXX is the block device where the file resides" << endl;
+ if (argc > 1 && argv[1] == "-h"s) {
+ cerr << "Usage: [test_dir] [file_size]\n";
+ cerr << "\n";
+ cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
exit(EXIT_FAILURE);
}
::android::base::InitLogging(argv, ::android::base::StderrLogger);
- testbdev = argv[1];
+ std::string root_dir = "/data/local/unencrypted";
+ if (access(root_dir.c_str(), F_OK)) {
+ root_dir = "/data";
+ }
+
+ std::string tempdir = root_dir + "/XXXXXX"s;
+ if (!mkdtemp(tempdir.data())) {
+ cerr << "unable to create tempdir on " << root_dir << "\n";
+ exit(EXIT_FAILURE);
+ }
+ 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);
if (testfile_size == ULLONG_MAX) {
@@ -281,5 +528,14 @@
}
}
- return RUN_ALL_TESTS();
+ if (!DetermineBlockSize()) {
+ exit(EXIT_FAILURE);
+ }
+
+ auto result = RUN_ALL_TESTS();
+
+ std::string cmd = "rm -rf " + gTestDir;
+ system(cmd.c_str());
+
+ return result;
}
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/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 3e93265..a3c76ab 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -24,6 +24,8 @@
"avb_ops.cpp",
"avb_util.cpp",
"fs_avb.cpp",
+ "fs_avb_util.cpp",
+ "types.cpp",
"util.cpp",
],
static_libs: [
@@ -98,6 +100,7 @@
srcs: [
"tests/basic_test.cpp",
"tests/fs_avb_test.cpp",
+ "tests/fs_avb_util_test.cpp",
],
}
@@ -115,3 +118,26 @@
"tests/util_test.cpp",
],
}
+
+cc_test {
+ name: "libfs_avb_device_test",
+ test_suites: ["device-tests"],
+ static_libs: [
+ "libavb",
+ "libdm",
+ "libfs_avb",
+ "libfstab",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ ],
+ srcs: [
+ "tests/fs_avb_device_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index 6a3e2c0..c192bf5 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -36,6 +36,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <libavb/libavb.h>
+#include <libdm/dm.h>
#include <utils/Compat.h>
#include "util.h"
@@ -104,6 +105,20 @@
return AVB_IO_RESULT_OK;
}
+// Converts a partition name (with ab_suffix) to the corresponding mount point.
+// e.g., "system_a" => "/system",
+// e.g., "vendor_a" => "/vendor",
+static std::string DeriveMountPoint(const std::string& partition_name) {
+ const std::string ab_suffix = fs_mgr_get_slot_suffix();
+ std::string mount_point(partition_name);
+ auto found = partition_name.rfind(ab_suffix);
+ if (found != std::string::npos) {
+ mount_point.erase(found); // converts system_a => system
+ }
+
+ return "/" + mount_point;
+}
+
FsManagerAvbOps::FsManagerAvbOps() {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
@@ -122,14 +137,53 @@
avb_ops_.user_data = this;
}
+// Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding
+// dm-linear path for it. e.g., /dev/block/dm-0. If not found, returns an empty string.
+// This assumes that the prefix of the partition name and the mount point are the same.
+// e.g., partition vendor_a is mounted under /vendor, product_a is mounted under /product, etc.
+// This might not be true for some special fstab files, e.g., fstab.postinstall.
+// But it's good enough for the default fstab. Also note that the logical path is a
+// fallback solution when the physical path (/dev/block/by-name/<partition>) cannot be found.
+std::string FsManagerAvbOps::GetLogicalPath(const std::string& partition_name) {
+ if (fstab_.empty() && !ReadDefaultFstab(&fstab_)) {
+ return "";
+ }
+
+ const auto mount_point = DeriveMountPoint(partition_name);
+ if (mount_point.empty()) return "";
+
+ auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point);
+ if (!fstab_entry) return "";
+
+ std::string device_path;
+ if (fstab_entry->fs_mgr_flags.logical) {
+ dm::DeviceMapper& dm = dm::DeviceMapper::Instance();
+ if (!dm.GetDmDevicePathByName(fstab_entry->blk_device, &device_path)) {
+ LERROR << "Failed to resolve logical device path for: " << fstab_entry->blk_device;
+ return "";
+ }
+ return device_path;
+ }
+
+ return "";
+}
+
AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
size_t num_bytes, void* buffer,
size_t* out_num_read) {
- const std::string path = "/dev/block/by-name/"s + partition;
+ std::string path = "/dev/block/by-name/"s + partition;
// Ensures the device path (a symlink created by init) is ready to access.
if (!WaitForFile(path, 1s)) {
- return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ LERROR << "Device path not found: " << path;
+ // Falls back to logical path if the physical path is not found.
+ // This mostly only works for emulator (no bootloader). Because in normal
+ // device, bootloader is unable to read logical partitions. So if libavb in
+ // the bootloader failed to read a physical partition, it will failed to boot
+ // the HLOS and we won't reach the code here.
+ path = GetLogicalPath(partition);
+ if (path.empty() || !WaitForFile(path, 1s)) return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ LINFO << "Fallback to use logical device path: " << path;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index c0f12aa..b39812d 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -27,7 +27,8 @@
#include <string>
#include <vector>
-#include <fs_avb/fs_avb.h>
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
#include <libavb/libavb.h>
namespace android {
@@ -60,7 +61,9 @@
std::vector<VBMetaData>* out_vbmeta_images);
private:
+ std::string GetLogicalPath(const std::string& partition_name);
AvbOps avb_ops_;
+ Fstab fstab_;
};
} // namespace fs_mgr
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 08f87b4..d9650f3 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -28,62 +28,31 @@
#include "util.h"
using android::base::Basename;
+using android::base::ReadFileToString;
using android::base::StartsWith;
using android::base::unique_fd;
namespace android {
namespace fs_mgr {
-// Helper functions to print enum class VBMetaVerifyResult.
-const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
- // clang-format off
- static const char* const name[] = {
- "ResultSuccess",
- "ResultError",
- "ResultErrorVerification",
- "ResultUnknown",
- };
- // clang-format on
-
- uint32_t index = static_cast<uint32_t>(result);
- uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
- if (index >= unknown_index) {
- index = unknown_index;
+std::string GetAvbPropertyDescriptor(const std::string& key,
+ const std::vector<VBMetaData>& vbmeta_images) {
+ size_t value_size;
+ for (const auto& vbmeta : vbmeta_images) {
+ const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
+ key.size(), &value_size);
+ if (value != nullptr) {
+ return {value, value_size};
+ }
}
-
- return name[index];
-}
-
-std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
- os << VBMetaVerifyResultToString(result);
- return os;
-}
-
-// class VBMetaData
-// ----------------
-std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
- auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();
-
- if (!vbmeta_header) return nullptr;
-
- /* Byteswap the header. */
- avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
- vbmeta_header.get());
- if (update_vbmeta_size) {
- vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
- vbmeta_header->authentication_data_block_size +
- vbmeta_header->auxiliary_data_block_size;
- }
-
- return vbmeta_header;
+ return "";
}
// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
// See the following link for more details:
// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt,
- const std::string& root_digest, const std::string& blk_device,
- android::dm::DmTable* table) {
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+ const std::string& blk_device, android::dm::DmTable* table) {
// Loads androidboot.veritymode from kernel cmdline.
std::string verity_mode;
if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
@@ -104,12 +73,12 @@
std::ostringstream hash_algorithm;
hash_algorithm << hashtree_desc.hash_algorithm;
- android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
- hashtree_desc.dm_verity_version, blk_device, blk_device,
- hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
- hashtree_desc.image_size / hashtree_desc.data_block_size,
- hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
- hash_algorithm.str(), root_digest, salt);
+ android::dm::DmTargetVerity target(
+ 0, hashtree_desc.image_size / 512, hashtree_desc.dm_verity_version, blk_device,
+ blk_device, hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+ hashtree_desc.image_size / hashtree_desc.data_block_size,
+ hashtree_desc.tree_offset / hashtree_desc.hash_block_size, hash_algorithm.str(),
+ hashtree_desc.root_digest, hashtree_desc.salt);
if (hashtree_desc.fec_size > 0) {
target.UseFec(blk_device, hashtree_desc.fec_num_roots,
hashtree_desc.fec_offset / hashtree_desc.data_block_size,
@@ -126,26 +95,25 @@
return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
}
-bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest,
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
bool wait_for_verity_dev) {
android::dm::DmTable table;
- if (!ConstructVerityTable(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
- !table.valid()) {
+ if (!ConstructVerityTable(hashtree_desc, fstab_entry->blk_device, &table) || !table.valid()) {
LERROR << "Failed to construct verity table.";
return false;
}
table.set_readonly(true);
const std::string mount_point(Basename(fstab_entry->mount_point));
+ const std::string device_name(GetVerityDeviceName(*fstab_entry));
android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
- if (!dm.CreateDevice(mount_point, table)) {
+ if (!dm.CreateDevice(device_name, table)) {
LERROR << "Couldn't create verity device!";
return false;
}
std::string dev_path;
- if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+ if (!dm.GetDmDevicePathByName(device_name, &dev_path)) {
LERROR << "Couldn't get verity device path!";
return false;
}
@@ -164,12 +132,11 @@
return true;
}
-std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor(
- const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images,
- std::string* out_salt, std::string* out_digest) {
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
bool found = false;
const uint8_t* desc_partition_name;
- auto hashtree_desc = std::make_unique<AvbHashtreeDescriptor>();
+ auto hashtree_desc = std::make_unique<FsAvbHashtreeDescriptor>();
for (const auto& vbmeta : vbmeta_images) {
size_t num_descriptors;
@@ -209,15 +176,17 @@
}
if (!found) {
- LERROR << "Partition descriptor not found: " << partition_name.c_str();
+ LERROR << "Hashtree descriptor not found: " << partition_name;
return nullptr;
}
+ hashtree_desc->partition_name = partition_name;
+
const uint8_t* desc_salt = desc_partition_name + hashtree_desc->partition_name_len;
- *out_salt = BytesToHex(desc_salt, hashtree_desc->salt_len);
+ hashtree_desc->salt = BytesToHex(desc_salt, hashtree_desc->salt_len);
const uint8_t* desc_digest = desc_salt + hashtree_desc->salt_len;
- *out_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);
+ hashtree_desc->root_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);
return hashtree_desc;
}
@@ -235,18 +204,15 @@
return false;
}
- std::string salt;
- std::string root_digest;
- std::unique_ptr<AvbHashtreeDescriptor> hashtree_descriptor =
- GetHashtreeDescriptor(partition_name, vbmeta_images, &salt, &root_digest);
+ std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =
+ GetHashtreeDescriptor(partition_name, vbmeta_images);
if (!hashtree_descriptor) {
return false;
}
// Converts HASHTREE descriptor to verity table to load into kernel.
// When success, the new device path will be returned, e.g., /dev/block/dm-2.
- return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, salt, root_digest,
- wait_for_verity_dev);
+ return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, wait_for_verity_dev);
}
// Converts a AVB partition_name (without A/B suffix) to a device partition name.
@@ -347,7 +313,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;
}
@@ -360,6 +327,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) {
@@ -383,7 +365,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;
@@ -420,6 +402,10 @@
uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;
bool is_vbmeta_partition = StartsWith(partition_name, "vbmeta");
+ if (out_verify_result) {
+ *out_verify_result = VBMetaVerifyResult::kError;
+ }
+
if (!is_vbmeta_partition) {
std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
if (!footer) {
@@ -445,7 +431,10 @@
auto verify_result =
VerifyVBMetaSignature(*vbmeta, expected_public_key_blob, out_public_key_data);
- if (out_verify_result != nullptr) *out_verify_result = verify_result;
+
+ if (out_verify_result != nullptr) {
+ *out_verify_result = verify_result;
+ }
if (verify_result == VBMetaVerifyResult::kSuccess ||
verify_result == VBMetaVerifyResult::kErrorVerification) {
@@ -508,6 +497,10 @@
const std::string& expected_public_key_blob, bool allow_verification_error,
bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result) {
+ if (out_verify_result) {
+ *out_verify_result = VBMetaVerifyResult::kError;
+ }
+
// Ensures the device path (might be a symlink created by init) is ready to access.
if (!WaitForFile(image_path, 1s)) {
PERROR << "No such path: " << image_path;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 4b54e27..09c786a 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -24,19 +24,11 @@
#include <libavb/libavb.h>
#include <libdm/dm.h>
-#include "fs_avb/fs_avb.h"
+#include "fs_avb/types.h"
namespace android {
namespace fs_mgr {
-enum class VBMetaVerifyResult {
- kSuccess = 0,
- kError = 1,
- kErrorVerification = 2,
-};
-
-std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
-
struct ChainInfo {
std::string partition_name;
std::string public_key_blob;
@@ -45,17 +37,17 @@
: partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
};
+std::string GetAvbPropertyDescriptor(const std::string& key,
+ const std::vector<VBMetaData>& vbmeta_images);
+
// AvbHashtreeDescriptor to dm-verity table setup.
-std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor(
- const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images,
- std::string* out_salt, std::string* out_digest);
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
-bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt,
- const std::string& root_digest, const std::string& blk_device,
- android::dm::DmTable* table);
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+ const std::string& blk_device, android::dm::DmTable* table);
-bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest,
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
bool wait_for_verity_dev);
// Searches a Avb hashtree descriptor in vbmeta_images for fstab_entry, to enable dm-verity.
@@ -86,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 1af3b33..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 {
@@ -76,33 +78,6 @@
return std::make_pair(digest, total_size);
}
-// Helper functions to dump enum class AvbHandleStatus.
-const char* AvbHandleStatusToString(AvbHandleStatus status) {
- // clang-format off
- static const char* const name[] = {
- "Success",
- "Uninitialized",
- "HashtreeDisabled",
- "VerificationDisabled",
- "VerificationError",
- "Unknown",
- };
- // clang-format on
-
- uint32_t index = static_cast<uint32_t>(status);
- uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
- if (index >= unknown_index) {
- index = unknown_index;
- }
-
- return name[index];
-}
-
-std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
- os << AvbHandleStatusToString(status);
- return os;
-}
-
// class AvbVerifier
// -----------------
// Reads the following values from kernel cmdline and provides the
@@ -290,6 +265,68 @@
return avb_handle;
}
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
+ if (fstab_entry.avb_keys.empty()) {
+ LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
+ return nullptr;
+ }
+
+ // Binds allow_verification_error and rollback_protection to device unlock state.
+ bool allow_verification_error = IsDeviceUnlocked();
+ bool rollback_protection = !allow_verification_error;
+
+ 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_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;
+ return nullptr;
+ }
+
+ AvbUniquePtr avb_handle(new AvbHandle());
+ if (!avb_handle) {
+ LERROR << "Failed to allocate AvbHandle";
+ return nullptr;
+ }
+ avb_handle->vbmeta_images_.emplace_back(std::move(*vbmeta));
+
+ switch (verify_result) {
+ case VBMetaVerifyResult::kSuccess:
+ avb_handle->status_ = AvbHandleStatus::kSuccess;
+ break;
+ case VBMetaVerifyResult::kErrorVerification:
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
+ break;
+ default:
+ LERROR << "LoadAndVerifyVbmetaByPath failed, result: " << verify_result;
+ 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;
+ }
+
+ LINFO << "Returning avb_handle for '" << fstab_entry.mount_point
+ << "' with status: " << avb_handle->status_;
+ return avb_handle;
+}
+
AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
// Loads inline vbmeta images, starting from /vbmeta.
return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
@@ -385,52 +422,12 @@
AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
bool wait_for_verity_dev) {
- if (fstab_entry->avb_key.empty()) {
- LERROR << "avb_key=/path/to/key is missing for " << fstab_entry->mount_point;
+ auto avb_handle = LoadAndVerifyVbmeta(*fstab_entry);
+ if (!avb_handle) {
return AvbHashtreeResult::kFail;
}
- // Binds allow_verification_error and rollback_protection to device unlock state.
- 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 AvbHashtreeResult::kFail;
- }
- LWARNING << "Allowing no expected key blob when verification error is permitted";
- expected_key_blob.clear();
- }
-
- bool verification_disabled = false;
- 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, nullptr /* out_verify_result */);
-
- if (!vbmeta) {
- LERROR << "Failed to load vbmeta: " << fstab_entry->blk_device;
- return AvbHashtreeResult::kFail;
- }
-
- if (verification_disabled) {
- LINFO << "AVB verification disabled on: " << fstab_entry->mount_point;
- return AvbHashtreeResult::kDisabled;
- }
-
- // Puts the vbmeta into a vector, for LoadAvbHashtreeToEnableVerity() to use.
- std::vector<VBMetaData> vbmeta_images;
- vbmeta_images.emplace_back(std::move(*vbmeta));
- if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images,
- fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
- return AvbHashtreeResult::kFail;
- }
-
- return AvbHashtreeResult::kSuccess;
+ return avb_handle->SetUpAvbHashtree(fstab_entry, wait_for_verity_dev);
}
AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
@@ -452,5 +449,19 @@
return AvbHashtreeResult::kSuccess;
}
+std::string AvbHandle::GetSecurityPatchLevel(const FstabEntry& fstab_entry) const {
+ if (vbmeta_images_.size() < 1) {
+ return "";
+ }
+ std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+ fs_mgr_get_other_slot_suffix());
+ auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
+ return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
+}
+
+bool AvbHandle::IsDeviceUnlocked() {
+ return android::fs_mgr::IsDeviceUnlocked();
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
new file mode 100644
index 0000000..f82f83d
--- /dev/null
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 "fs_avb/fs_avb_util.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "avb_util.h"
+#include "util.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data,
+ std::string* out_avb_partition_name,
+ VBMetaVerifyResult* out_verify_result) {
+ // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+ // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+ std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+ fs_mgr_get_other_slot_suffix());
+ if (out_avb_partition_name) {
+ *out_avb_partition_name = avb_partition_name;
+ }
+
+ // Updates fstab_entry->blk_device from <partition> to /dev/block/dm-<N> if
+ // it's a logical partition.
+ std::string device_path = fstab_entry.blk_device;
+ if (fstab_entry.fs_mgr_flags.logical &&
+ !android::base::StartsWith(fstab_entry.blk_device, "/")) {
+ dm::DeviceMapper& dm = dm::DeviceMapper::Instance();
+ if (!dm.GetDmDevicePathByName(fstab_entry.blk_device, &device_path)) {
+ LERROR << "Failed to resolve logical device path for: " << fstab_entry.blk_device;
+ return nullptr;
+ }
+ }
+
+ return LoadAndVerifyVbmetaByPath(device_path, avb_partition_name, expected_public_key_blob,
+ true /* allow_verification_error */,
+ false /* rollback_protection */, false /* is_chained_vbmeta */,
+ out_public_key_data, nullptr /* out_verification_disabled */,
+ out_verify_result);
+}
+
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& avb_partition_name, VBMetaData&& vbmeta) {
+ if (!vbmeta.size()) return nullptr;
+
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+ return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index d4e3a6e..7127fa6 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -21,32 +21,13 @@
#include <string>
#include <vector>
+#include <fs_avb/types.h>
#include <fstab/fstab.h>
#include <libavb/libavb.h>
namespace android {
namespace fs_mgr {
-enum class AvbHashtreeResult {
- kSuccess = 0,
- kFail,
- kDisabled,
-};
-
-enum class HashAlgorithm {
- kInvalid = 0,
- kSHA256 = 1,
- kSHA512 = 2,
-};
-
-enum class AvbHandleStatus {
- kSuccess = 0,
- kUninitialized = 1,
- kHashtreeDisabled = 2,
- kVerificationDisabled = 3,
- kVerificationError = 4,
-};
-
struct VBMetaInfo {
std::string digest;
HashAlgorithm hash_algorithm;
@@ -58,51 +39,6 @@
: digest(std::move(digest_value)), hash_algorithm(algorithm), total_size(size) {}
};
-class VBMetaData {
- public:
- // Constructors
- VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
-
- VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
- : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
- vbmeta_size_(size),
- partition_name_(partition_name) {
- // The ownership of data is NOT transferred, i.e., the caller still
- // needs to release the memory as we make a copy here.
- memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
- }
-
- explicit VBMetaData(size_t size, const std::string& partition_name)
- : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
- vbmeta_size_(size),
- partition_name_(partition_name) {}
-
- // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
- // true to update vbmeta_size_ to the actual size with valid content.
- std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
-
- // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.
- // e.g.,
- // - /dev/block/by-name/system_a
- // - /path/to/system_other.img.
- void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }
-
- // Get methods for each data member.
- const std::string& partition() const { return partition_name_; }
- const std::string& vbmeta_path() const { return vbmeta_path_; }
- uint8_t* data() const { return vbmeta_ptr_.get(); }
- const size_t& size() const { return vbmeta_size_; }
-
- // Maximum size of a vbmeta data - 64 KiB.
- static const size_t kMaxVBMetaSize = 64 * 1024;
-
- private:
- std::unique_ptr<uint8_t[]> vbmeta_ptr_;
- size_t vbmeta_size_;
- std::string partition_name_;
- std::string vbmeta_path_;
-};
-
class FsManagerAvbOps;
class AvbHandle;
@@ -149,6 +85,8 @@
// TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
static AvbUniquePtr Open(); // loads inline vbmeta, via libavb.
static AvbUniquePtr LoadAndVerifyVbmeta(); // loads inline vbmeta.
+ static AvbUniquePtr LoadAndVerifyVbmeta(
+ const FstabEntry& fstab_entry); // loads offline vbmeta.
static AvbUniquePtr LoadAndVerifyVbmeta( // loads offline vbmeta.
const std::string& partition_name, const std::string& ab_suffix,
const std::string& ab_other_suffix, const std::string& expected_public_key,
@@ -172,6 +110,10 @@
static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
bool wait_for_verity_dev = true);
+ static bool IsDeviceUnlocked();
+
+ std::string GetSecurityPatchLevel(const FstabEntry& fstab_entry) const;
+
const std::string& avb_version() const { return avb_version_; }
const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
AvbHandleStatus status() const { return status_; }
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
new file mode 100644
index 0000000..ec8badb
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data,
+ std::string* out_avb_partition_name,
+ VBMetaVerifyResult* out_verify_result);
+
+// Gets the hashtree descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& avb_partition_name, VBMetaData&& vbmeta);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
new file mode 100644
index 0000000..bd638e6
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -0,0 +1,110 @@
+/*
+ * 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 <cstring>
+#include <memory>
+#include <ostream>
+
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+enum class VBMetaVerifyResult {
+ kSuccess = 0,
+ kError = 1,
+ kErrorVerification = 2,
+};
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
+
+enum class AvbHashtreeResult {
+ kSuccess = 0,
+ kFail,
+ kDisabled,
+};
+
+enum class HashAlgorithm {
+ kInvalid = 0,
+ kSHA256 = 1,
+ kSHA512 = 2,
+};
+
+enum class AvbHandleStatus {
+ kSuccess = 0,
+ kUninitialized = 1,
+ kHashtreeDisabled = 2,
+ kVerificationDisabled = 3,
+ kVerificationError = 4,
+};
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
+
+struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
+ std::string partition_name;
+ std::string salt;
+ std::string root_digest;
+};
+
+class VBMetaData {
+ public:
+ // Constructors
+ VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
+
+ VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
+ : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+ vbmeta_size_(size),
+ partition_name_(partition_name) {
+ // The ownership of data is NOT transferred, i.e., the caller still
+ // needs to release the memory as we make a copy here.
+ std::memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
+ }
+
+ explicit VBMetaData(size_t size, const std::string& partition_name)
+ : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+ vbmeta_size_(size),
+ partition_name_(partition_name) {}
+
+ // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
+ // true to update vbmeta_size_ to the actual size with valid content.
+ std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
+
+ // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.
+ // e.g.,
+ // - /dev/block/by-name/system_a
+ // - /path/to/system_other.img.
+ void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }
+
+ // Get methods for each data member.
+ const std::string& partition() const { return partition_name_; }
+ const std::string& vbmeta_path() const { return vbmeta_path_; }
+ uint8_t* data() const { return vbmeta_ptr_.get(); }
+ const size_t& size() const { return vbmeta_size_; }
+
+ // Maximum size of a vbmeta data - 64 KiB.
+ static const size_t kMaxVBMetaSize = 64 * 1024;
+
+ private:
+ std::unique_ptr<uint8_t[]> vbmeta_ptr_;
+ size_t vbmeta_size_;
+ std::string partition_name_;
+ std::string vbmeta_path_;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/run_tests.sh b/fs_mgr/libfs_avb/run_tests.sh
new file mode 100755
index 0000000..5d2ce3d
--- /dev/null
+++ b/fs_mgr/libfs_avb/run_tests.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Run host tests
+atest libfs_avb_test # Tests public libfs_avb APIs.
+atest libfs_avb_internal_test # Tests libfs_avb private APIs.
+
+# Run device tests
+atest libfs_avb_device_test # Test public libfs_avb APIs on a device.
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 835410f..0d342d3 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -30,13 +30,14 @@
using android::fs_mgr::DeriveAvbPartitionName;
using android::fs_mgr::FstabEntry;
using android::fs_mgr::GetAvbFooter;
+using android::fs_mgr::GetAvbPropertyDescriptor;
using android::fs_mgr::GetChainPartitionInfo;
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;
@@ -268,6 +269,67 @@
EXPECT_EQ(nullptr, footer);
}
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_Basic) {
+ // Makes a vbmeta.img with some properties.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {}, /* chain_partitions */
+ "--prop foo:android "
+ "--prop bar:treble "
+ "--internal_release_string \"unit test\" ");
+ auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+ // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+
+ EXPECT_EQ("android", GetAvbPropertyDescriptor("foo", vbmeta_images));
+ EXPECT_EQ("treble", GetAvbPropertyDescriptor("bar", vbmeta_images));
+ EXPECT_EQ("", GetAvbPropertyDescriptor("non-existent", vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_SecurityPatchLevel) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--prop com.android.build.system.security_patch:2019-04-05 "
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta.img including the 'system' chained descriptor.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {boot_path}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ auto vbmeta = LoadVBMetaData("vbmeta.img");
+ auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+ // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+ vbmeta_images.emplace_back(std::move(system_vbmeta));
+
+ EXPECT_EQ("2019-04-05",
+ GetAvbPropertyDescriptor("com.android.build.system.security_patch", vbmeta_images));
+}
+
TEST_F(AvbUtilTest, GetVBMetaHeader) {
// Generates a raw boot.img
const size_t image_size = 5 * 1024 * 1024;
@@ -353,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);
@@ -363,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) {
@@ -380,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
new file mode 100644
index 0000000..c8605d7
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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/properties.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_avb/fs_avb_util.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+
+namespace fs_avb_device_test {
+
+// system vbmeta might not be at the end of /system when dynamic partition is
+// enabled. Therefore, disable it by default.
+TEST(FsAvbUtilTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) {
+ Fstab fstab;
+ EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+ FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+ EXPECT_NE(nullptr, system_entry);
+
+ std::string out_public_key_data;
+ std::string out_avb_partition_name;
+ VBMetaVerifyResult out_verify_result;
+ std::unique_ptr<VBMetaData> vbmeta =
+ LoadAndVerifyVbmeta(*system_entry, "" /* expected_public_key_blob */,
+ &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+ EXPECT_EQ("system", out_avb_partition_name);
+ EXPECT_NE("", out_public_key_data);
+}
+
+TEST(FsAvbUtilTest, GetHashtreeDescriptor_SystemOther) {
+ // Non-A/B device doesn't have system_other partition.
+ if (fs_mgr_get_slot_suffix() == "") return;
+
+ // Skip running this test if system_other is a logical partition.
+ // Note that system_other is still a physical partition on "retrofit" devices.
+ if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+ !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+ return;
+ }
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+ // It should have two lines in the fstab, the first for logical system_other,
+ // the other for physical system_other.
+ EXPECT_EQ(2UL, fstab.size());
+
+ // Use the 2nd fstab entry, which is for physical system_other partition.
+ FstabEntry* system_other = &fstab[1];
+ EXPECT_NE(nullptr, system_other);
+
+ std::string out_public_key_data;
+ std::string out_avb_partition_name;
+ VBMetaVerifyResult out_verify_result;
+ std::unique_ptr<VBMetaData> system_other_vbmeta =
+ LoadAndVerifyVbmeta(*system_other, "" /* expected_public_key_blob */,
+ &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+ EXPECT_NE(nullptr, system_other_vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+ EXPECT_EQ("system_other", out_avb_partition_name);
+ EXPECT_NE("", out_public_key_data);
+
+ auto hashtree_desc =
+ GetHashtreeDescriptor(out_avb_partition_name, std::move(*system_other_vbmeta));
+ EXPECT_NE(nullptr, hashtree_desc);
+}
+
+TEST(AvbHandleTest, LoadAndVerifyVbmeta_SystemOther) {
+ // Non-A/B device doesn't have system_other partition.
+ if (fs_mgr_get_slot_suffix() == "") return;
+
+ // Skip running this test if system_other is a logical partition.
+ // Note that system_other is still a physical partition on "retrofit" devices.
+ if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+ !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+ return;
+ }
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+ // It should have two lines in the fstab, the first for logical system_other,
+ // the other for physical system_other.
+ EXPECT_EQ(2UL, fstab.size());
+
+ // 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_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'?";
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+}
+
+TEST(AvbHandleTest, GetSecurityPatchLevel) {
+ Fstab fstab;
+ EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta();
+ EXPECT_NE(nullptr, avb_handle) << "Failed to load inline vbmeta. Try 'adb root'?";
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+ // Gets security patch level with format: YYYY-MM-DD (e.g., 2019-04-05).
+ FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+ EXPECT_NE(nullptr, system_entry);
+ EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*system_entry).length());
+
+ FstabEntry* vendor_entry = GetEntryForMountPoint(&fstab, "/vendor");
+ EXPECT_NE(nullptr, vendor_entry);
+ EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*vendor_entry).length());
+
+ FstabEntry* product_entry = GetEntryForMountPoint(&fstab, "/product");
+ EXPECT_NE(nullptr, product_entry);
+ EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*product_entry).length());
+}
+
+} // namespace fs_avb_device_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
index 2e46644..ab1980b 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
@@ -29,7 +29,7 @@
#include <android-base/unique_fd.h>
#include <base/files/file_path.h>
#include <base/strings/stringprintf.h>
-#include <fs_avb/fs_avb.h>
+#include <fs_avb/types.h>
#include <gtest/gtest.h>
// Utility macro to run the command expressed by the printf()-style string
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
new file mode 100644
index 0000000..7c34009
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 <fs_avb/fs_avb_util.h>
+
+#include "fs_avb_test_util.h"
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbUtilTest : public BaseFsAvbTest {
+ public:
+ PublicFsAvbUtilTest(){};
+
+ protected:
+ ~PublicFsAvbUtilTest(){};
+};
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor) {
+ // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+ auto hashtree_desc =
+ GetHashtreeDescriptor("system" /* avb_partition_name */, std::move(system_vbmeta));
+ EXPECT_NE(nullptr, hashtree_desc);
+
+ // Checks the returned hashtree_desc matches the following info returned by avbtool.
+ EXPECT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 15728640 bytes\n"
+ "Original image size: 10485760 bytes\n"
+ "VBMeta offset: 10661888\n"
+ "VBMeta size: 2112 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 576 bytes\n"
+ "Auxiliary Block: 1280 bytes\n"
+ "Algorithm: SHA512_RSA4096\n"
+ "Rollback Index: 20\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 10485760 bytes\n"
+ " Tree Offset: 10485760\n"
+ " Tree Size: 86016 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 10571776\n"
+ " FEC size: 90112 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: system\n"
+ " Salt: d00df00d\n"
+ " Root Digest: a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+ " Flags: 0\n",
+ InfoImage(system_path));
+
+ EXPECT_EQ(1UL, hashtree_desc->dm_verity_version);
+ EXPECT_EQ(10485760UL, hashtree_desc->image_size);
+ EXPECT_EQ(10485760UL, hashtree_desc->tree_offset);
+ EXPECT_EQ(86016UL, hashtree_desc->tree_size);
+ EXPECT_EQ(4096UL, hashtree_desc->data_block_size);
+ EXPECT_EQ(4096UL, hashtree_desc->hash_block_size);
+ EXPECT_EQ(2UL, hashtree_desc->fec_num_roots);
+ EXPECT_EQ(10571776UL, hashtree_desc->fec_offset);
+ EXPECT_EQ(90112UL, hashtree_desc->fec_size);
+ EXPECT_EQ(std::string("sha1"),
+ std::string(reinterpret_cast<const char*>(hashtree_desc->hash_algorithm)));
+ EXPECT_EQ(std::string("system").length(), hashtree_desc->partition_name_len);
+ EXPECT_EQ(hashtree_desc->partition_name, "system");
+ EXPECT_EQ(hashtree_desc->salt, "d00df00d");
+ EXPECT_EQ(hashtree_desc->root_digest, "a3d5dd307341393d85de356c384ff543ec1ed81b");
+
+ // Checks it's null if partition name doesn't match.
+ EXPECT_EQ(nullptr, GetHashtreeDescriptor("system_not_exist" /* avb_partition_name */,
+ std::move(system_vbmeta)));
+}
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor_NotFound) {
+ // Generates a raw boot.img
+ const size_t image_size = 5 * 1024 * 1024;
+ const size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", image_size);
+ // Appends AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+ // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+ auto boot_vbmeta = ExtractAndLoadVBMetaData(boot_path, "boot-vbmeta.img");
+
+ auto hashtree_desc =
+ GetHashtreeDescriptor("boot" /* avb_partition_name */, std::move(boot_vbmeta));
+ EXPECT_EQ(nullptr, hashtree_desc);
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/types.cpp b/fs_mgr/libfs_avb/types.cpp
new file mode 100644
index 0000000..3c277f3
--- /dev/null
+++ b/fs_mgr/libfs_avb/types.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "fs_avb/types.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper functions to print enum class VBMetaVerifyResult.
+const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
+ // clang-format off
+ static const char* const name[] = {
+ "ResultSuccess",
+ "ResultError",
+ "ResultErrorVerification",
+ "ResultUnknown",
+ };
+ // clang-format on
+
+ uint32_t index = static_cast<uint32_t>(result);
+ uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+ if (index >= unknown_index) {
+ index = unknown_index;
+ }
+
+ return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
+ os << VBMetaVerifyResultToString(result);
+ return os;
+}
+
+// Helper functions to dump enum class AvbHandleStatus.
+const char* AvbHandleStatusToString(AvbHandleStatus status) {
+ // clang-format off
+ static const char* const name[] = {
+ "Success",
+ "Uninitialized",
+ "HashtreeDisabled",
+ "VerificationDisabled",
+ "VerificationError",
+ "Unknown",
+ };
+ // clang-format on
+
+ uint32_t index = static_cast<uint32_t>(status);
+ uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+ if (index >= unknown_index) {
+ index = unknown_index;
+ }
+
+ return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
+ os << AvbHandleStatusToString(status);
+ return os;
+}
+
+// class VBMetaData
+// ----------------
+std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
+ auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();
+
+ if (!vbmeta_header) return nullptr;
+
+ /* Byteswap the header. */
+ avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
+ vbmeta_header.get());
+ if (update_vbmeta_size) {
+ vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
+ vbmeta_header->authentication_data_block_size +
+ vbmeta_header->auxiliary_data_block_size;
+ }
+
+ return vbmeta_header;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 355b7a1..7039994 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -43,6 +43,11 @@
windows: {
enabled: true,
},
+ android: {
+ shared_libs: [
+ "libcutils",
+ ],
+ },
},
export_include_dirs: ["include"],
}
diff --git a/fs_mgr/liblp/Android.mk b/fs_mgr/liblp/Android.mk
new file mode 100644
index 0000000..7f7f891
--- /dev/null
+++ b/fs_mgr/liblp/Android.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsKernelLiblpTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
new file mode 100644
index 0000000..007a302
--- /dev/null
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?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 VTS VtsKernelLiblpTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <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="VtsKernelLiblpTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/liblp_test/liblp_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/liblp_test/liblp_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="1m"/>
+ <option name="precondition-first-api-level" value="29" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index c39fbe7..41c01da 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;
}
@@ -992,7 +1057,7 @@
if (sABOverrideSet) {
return sABOverrideValue;
}
- return android::base::GetBoolProperty("ro.build.ab_update", false);
+ return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
}
bool MetadataBuilder::IsRetrofitDevice() const {
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/images.cpp b/fs_mgr/liblp/images.cpp
index 5a498f9..db27022 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -68,7 +68,7 @@
}
std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file) {
- unique_fd fd(open(image_file.c_str(), O_RDONLY | O_CLOEXEC));
+ unique_fd fd = GetControlFileOrOpen(image_file.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << " open failed: " << image_file;
return nullptr;
@@ -98,11 +98,12 @@
return WriteToImageFile(fd, input);
}
-SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images)
+ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify)
: metadata_(metadata),
geometry_(metadata.geometry),
block_size_(block_size),
+ sparsify_(sparsify),
images_(images) {
uint64_t total_size = GetTotalSuperPartitionSize(metadata);
if (block_size % LP_SECTOR_SIZE != 0) {
@@ -144,11 +145,11 @@
}
}
-bool SparseBuilder::IsValid() const {
+bool ImageBuilder::IsValid() const {
return device_images_.size() == metadata_.block_devices.size();
}
-bool SparseBuilder::Export(const char* file) {
+bool ImageBuilder::Export(const char* file) {
unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << "open failed: " << file;
@@ -158,8 +159,8 @@
LERROR << "Cannot export to a single image on retrofit builds.";
return false;
}
- // No gzip compression; sparseify; no checksum.
- int ret = sparse_file_write(device_images_[0].get(), fd, false, true, false);
+ // No gzip compression; no checksum.
+ int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);
if (ret != 0) {
LERROR << "sparse_file_write failed (error code " << ret << ")";
return false;
@@ -167,7 +168,7 @@
return true;
}
-bool SparseBuilder::ExportFiles(const std::string& output_dir) {
+bool ImageBuilder::ExportFiles(const std::string& output_dir) {
for (size_t i = 0; i < device_images_.size(); i++) {
std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
std::string file_name = "super_" + name + ".img";
@@ -179,8 +180,8 @@
PERROR << "open failed: " << file_path;
return false;
}
- // No gzip compression; sparseify; no checksum.
- int ret = sparse_file_write(device_images_[i].get(), fd, false, true, false);
+ // No gzip compression; no checksum.
+ int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);
if (ret != 0) {
LERROR << "sparse_file_write failed (error code " << ret << ")";
return false;
@@ -189,7 +190,7 @@
return true;
}
-bool SparseBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
+bool ImageBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
uint32_t block;
if (!SectorToBlock(sector, &block)) {
return false;
@@ -203,7 +204,7 @@
return true;
}
-bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+bool ImageBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
// The caller must ensure that the metadata has an alignment that is a
// multiple of the block size. liblp will take care of the rest, ensuring
// that all partitions are on an aligned boundary. Therefore all writes
@@ -218,11 +219,11 @@
return true;
}
-uint64_t SparseBuilder::BlockToSector(uint64_t block) const {
+uint64_t ImageBuilder::BlockToSector(uint64_t block) const {
return (block * block_size_) / LP_SECTOR_SIZE;
}
-bool SparseBuilder::Build() {
+bool ImageBuilder::Build() {
if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
LERROR << "Could not add initial sparse block for reserved zeroes";
return false;
@@ -275,8 +276,8 @@
return true;
}
-bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
- const std::string& file) {
+bool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+ const std::string& file) {
// Track which extent we're processing.
uint32_t extent_index = partition.first_extent_index;
@@ -371,7 +372,7 @@
return true;
}
-uint64_t SparseBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+uint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
uint64_t sectors = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
@@ -386,7 +387,7 @@
//
// Without this, it would be more difficult to find the appropriate extent for
// an output block. With this guarantee it is a linear walk.
-bool SparseBuilder::CheckExtentOrdering() {
+bool ImageBuilder::CheckExtentOrdering() {
std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
for (const auto& extent : metadata_.extents) {
@@ -407,8 +408,8 @@
return true;
}
-int SparseBuilder::OpenImageFile(const std::string& file) {
- android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+int ImageBuilder::OpenImageFile(const std::string& file) {
+ android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
if (source_fd < 0) {
PERROR << "open image file failed: " << file;
return -1;
@@ -437,15 +438,16 @@
return temp_fds_.back().get();
}
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images) {
- SparseBuilder builder(metadata, block_size, images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify) {
+ ImageBuilder builder(metadata, block_size, images, sparsify);
return builder.IsValid() && builder.Build() && builder.Export(file);
}
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
- uint32_t block_size, const std::map<std::string, std::string>& images) {
- SparseBuilder builder(metadata, block_size, images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images,
+ bool sparsify) {
+ ImageBuilder builder(metadata, block_size, images, sparsify);
return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
}
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 44217a0..75060f9 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -32,13 +32,13 @@
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
bool WriteToImageFile(int fd, const LpMetadata& metadata);
-// We use an object to build the sparse file since it requires that data
+// We use an object to build the image file since it requires that data
// pointers be held alive until the sparse file is destroyed. It's easier
// to do this when the data pointers are all in one place.
-class SparseBuilder {
+class ImageBuilder {
public:
- SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images);
+ ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify);
bool Build();
bool Export(const char* file);
@@ -60,6 +60,7 @@
const LpMetadata& metadata_;
const LpMetadataGeometry& geometry_;
uint32_t block_size_;
+ bool sparsify_;
std::vector<SparsePtr> device_images_;
std::string all_metadata_;
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/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 6348f55..5f782b0 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -72,8 +72,8 @@
// Read/Write logical partition metadata to an image file, for diagnostics or
// flashing.
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify);
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
@@ -83,8 +83,9 @@
// is intended for retrofit devices, and will generate one sparse file per
// block device (each named super_<name>.img) and placed in the specified
// output folder.
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
- uint32_t block_size, const std::map<std::string, std::string>& images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images,
+ bool sparsify);
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 9f3314d..8fc02cb 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -598,7 +598,7 @@
ASSERT_NE(exported, nullptr);
// Build the sparse file.
- SparseBuilder sparse(*exported.get(), 512, {});
+ ImageBuilder sparse(*exported.get(), 512, {}, true /* sparsify */);
ASSERT_TRUE(sparse.IsValid());
ASSERT_TRUE(sparse.Build());
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 898f241..3b12213 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include "utility.h"
@@ -37,7 +38,7 @@
namespace {
std::string GetPartitionAbsolutePath(const std::string& path) {
- if (path[0] == '/') {
+ if (android::base::StartsWith(path, "/")) {
return path;
}
return "/dev/block/by-name/" + path;
@@ -45,7 +46,7 @@
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
#if defined(__linux__)
- unique_fd fd(open(block_device.c_str(), O_RDONLY));
+ unique_fd fd = GetControlFileOrOpen(block_device.c_str(), O_RDONLY);
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
return false;
@@ -85,7 +86,7 @@
unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
std::string path = GetPartitionAbsolutePath(partition_name);
- return unique_fd{open(path.c_str(), flags | O_CLOEXEC)};
+ return GetControlFileOrOpen(path.c_str(), flags | O_CLOEXEC);
}
bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index ecf94a4..72a3c57 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -28,6 +28,10 @@
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
+#ifdef __ANDROID__
+#include <cutils/android_get_control_file.h>
+#endif
+
#include "utility.h"
namespace android {
@@ -171,5 +175,19 @@
#endif
}
+base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
+#if defined(__ANDROID__)
+ int fd = android_get_control_file(path);
+ if (fd >= 0) {
+ int newfd = TEMP_FAILURE_RETRY(dup(fd));
+ if (newfd >= 0) {
+ return base::unique_fd(newfd);
+ }
+ PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
+ }
+#endif
+ return base::unique_fd(open(path, flags));
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index e8b2ca9..96f1717 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include "liblp/liblp.h"
@@ -92,6 +93,8 @@
// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
bool SetBlockReadonly(int fd, bool readonly);
+::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index ea12e96..eb9f525 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -14,6 +14,7 @@
cc_test {
name: "fs_mgr_unit_test",
+ test_suites: ["device-tests"],
shared_libs: [
"libbase",
@@ -23,9 +24,6 @@
"libfs_mgr",
"libfstab",
],
- data: [
- "data/*",
- ],
srcs: [
"fs_mgr_test.cpp",
],
@@ -36,3 +34,40 @@
"-Werror",
],
}
+
+cc_prebuilt_binary {
+ name: "adb-remount-test.sh",
+ srcs: ["adb-remount-test.sh"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ android: {
+ enabled: false,
+ },
+ },
+ host_supported: true,
+}
+
+sh_test {
+ name: "adb-remount-sh",
+ src: "adb-remount-test.sh",
+ filename: "adb-remount-test.sh",
+ test_suites: ["general-tests"],
+ test_config: "adb-remount-sh.xml",
+}
+
+java_test_host {
+ name: "fs_mgr_vendor_overlay_test",
+
+ srcs: ["src/**/VendorOverlayHostTest.java"],
+
+ libs: ["tradefed"],
+
+ test_config: "vendor-overlay-test.xml",
+
+ test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/adb-remount-sh.xml b/fs_mgr/tests/adb-remount-sh.xml
new file mode 100644
index 0000000..fa0d63f
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-sh.xml
@@ -0,0 +1,25 @@
+<?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 adb remount test cases">
+ <option name="test-suite-tag" value="adb-remount" />
+ <!-- This test requires a device, so it's not annotated with a null-device -->
+ <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+ <option name="binary" value="adb-remount-test.sh" />
+ <!-- Increase default timeout as script is quite long -->
+ <option name="per-binary-timeout" value="1h" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 8298bf2..c22176b 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -11,23 +11,24 @@
## USAGE
##
-USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]
-adb remount tests (overlayfs focus)
+adb remount tests
+
+--help This help
+--serial Specify device (must if multiple are present)
+--color Dress output with highlighting colors
+--print-time Report the test duration
Conditions:
- Must be a userdebug build.
- Must be in adb mode.
- - Kernel must have overlayfs enabled and patched to support override_creds.
- - Must have either squashfs, ext4-dedupe or right-sized partitions.
- - Minimum expectation system and vender are overlayfs covered partitions.
+ - Also tests overlayfs
+ - Kernel must have overlayfs enabled and patched to support override_creds.
+ - Must have either erofs, squashfs, ext4-dedupe or full partitions.
+ - Minimum expectation system and vender are overlayfs covered partitions.
"
-if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
- echo "${USAGE}" >&2
- exit 0
-fi
-
##
## Helper Variables
##
@@ -44,6 +45,10 @@
ORANGE="${ESCAPE}[38;5;255:165:0m"
BLUE="${ESCAPE}[35m"
NORMAL="${ESCAPE}[0m"
+TMPDIR=${TMPDIR:-/tmp}
+print_time=false
+start_time=`date +%s`
+ACTIVE_SLOT=
##
## Helper Functions
@@ -66,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
@@ -74,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
@@ -139,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
@@ -161,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
@@ -171,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
@@ -199,14 +223,40 @@
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 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
+ echo "(USB stack borken?)"
fi
}
@@ -214,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
@@ -234,11 +333,11 @@
Returns: true if device in root state" ]
adb_root() {
- [ `adb_sh echo '${USER}'` != root ] || return 0
+ [ root != "`adb_sh echo '${USER}' </dev/null`" ] || return 0
adb root >/dev/null </dev/null 2>/dev/null
sleep 2
adb_wait 2m &&
- [ `adb_sh echo '${USER}'` = root ]
+ [ root = "`adb_sh echo '${USER}' </dev/null`" ]
}
[ "USAGE: adb_unroot
@@ -247,22 +346,22 @@
Returns: true if device in un root state" ]
adb_unroot() {
- [ `adb_sh echo '${USER}'` = root ] || return 0
+ [ root = "`adb_sh echo '${USER}' </dev/null`" ] || return 0
adb unroot >/dev/null </dev/null 2>/dev/null
sleep 2
adb_wait 2m &&
- [ `adb_sh echo '${USER}'` != root ]
+ [ 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
@@ -278,13 +377,50 @@
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.
+
+Returns: reverses configurations" ]
+restore() {
+ true
+}
+
[ "USAGE: cleanup
Do nothing: should be redefined when necessary
-Returns: cleans up any latent resources, reverses configurations" ]
-cleanup () {
- :
+Returns: cleans up any latent resources" ]
+cleanup() {
+ true
+}
+
+[ "USAGE: test_duration >/dev/stderr
+
+Prints the duration of the test
+
+Returns: reports duration" ]
+test_duration() {
+ if ${print_time}; then
+ echo "${BLUE}[ INFO ]${NORMAL} end `date`"
+ [ -n "${start_time}" ] || return
+ end_time=`date +%s`
+ local diff_time=`expr ${end_time} - ${start_time}`
+ echo "${BLUE}[ INFO ]${NORMAL} duration `format_duration ${diff_time}`"
+ fi >&2
}
[ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
@@ -306,63 +442,77 @@
fi
echo "${RED}[ FAILED ]${NORMAL} ${@}" >&2
cleanup
+ restore
+ test_duration
exit 1
}
-[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+[ "USAGE: EXPECT_EQ <lval> <rval> [--warning [message]]
Returns true if (regex) lval matches rval" ]
EXPECT_EQ() {
- lval="${1}"
- rval="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
+ local error=1
+ local prefix="${RED}[ ERROR ]${NORMAL}"
+ if [ X"${1}" = X"--warning" ]; then
+ prefix="${RED}[ WARNING ]${NORMAL}"
+ error=0
+ shift 1
+ fi
if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
*}" ]; then
- echo "ERROR: expected \"${lval}\"" >&2
- echo " got \"${rval}\"" |
+ echo "${prefix} expected \"${lval}\"" >&2
+ echo "${prefix} got \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
t again' >&2
if [ -n "${*}" ] ; then
- echo " ${*}" >&2
+ echo "${prefix} ${*}" >&2
fi
else
- echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
+ echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}" >&2
fi
- return 1
+ return ${error}
fi
if [ -n "${*}" ] ; then
- if [ X"${lval}" != X"${rval}" ]; then
+ prefix="${GREEN}[ INFO ]${NORMAL}"
+ if [ X"${lval}" != X"${rval}" ]; then # we were supplied a regex?
if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
- echo "INFO: ok \"${lval}\"" >&2
+ echo "${prefix} ok \"${lval}\"" >&2
echo " = \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
t again' >&2
if [ -n "${*}" ] ; then
- echo " ${*}" >&2
+ echo "${prefix} ${*}" >&2
fi
else
- echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+ echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}" >&2
fi
else
- echo "INFO: ok \"${lval}\" ${*}" >&2
+ echo "${prefix} ok \"${lval}\" ${*}" >&2
fi
fi
return 0
}
-[ "USAGE: check_eq <lval> <rval> [message]
+[ "USAGE: check_eq <lval> <rval> [--warning [message]]
Exits if (regex) lval mismatches rval" ]
check_eq() {
- left="${1}"
- right="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
- EXPECT_EQ "${left}" "${right}" ||
+ if [ X"${1}" = X"--warning" ]; then
+ EXPECT_EQ "${lval}" "${rval}" ${*}
+ return
+ fi
+ EXPECT_EQ "${lval}" "${rval}" ||
die "${@}"
}
@@ -376,32 +526,93 @@
cat -
fi |
grep -v \
- -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\) " \
- -e "^\(bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
+ -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
-e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-e "^rootfs / rootfs rw," \
-e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
}
+[ "USAGE: skip_unrelated_mounts < /proc/mounts
+
+or output from df
+
+Filters out all apex and vendor override administrative overlay mounts
+uninteresting to the test" ]
+skip_unrelated_mounts() {
+ grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
+ grep -v "[%] /\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+}
+
##
## MAINLINE
##
-if [ X"-s" = X"${1}" -a -n "${2}" ]; then
- export ANDROID_SERIAL="${2}"
- shift 2
+OPTIONS=`getopt --alternative --unquoted \
+ --longoptions help,serial:,colour,color,no-colour,no-color \
+ --longoptions gtest_print_time,print-time \
+ -- "?hs:" ${*}` ||
+ ( echo "${USAGE}" >&2 ; false ) ||
+ die "getopt failure"
+set -- ${OPTIONS}
+
+color=false
+while [ ${#} -gt 0 ]; do
+ case ${1} in
+ -h | --help | -\?)
+ echo "${USAGE}" >&2
+ exit 0
+ ;;
+ -s | --serial)
+ export ANDROID_SERIAL=${2}
+ shift
+ ;;
+ --color | --colour)
+ color=true
+ ;;
+ --no-color | --no-colour)
+ color=false
+ ;;
+ --print-time | --gtest_print_time)
+ print_time=true
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "${USAGE}" >&2
+ die "${0}: error unknown option ${1}"
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+if ! ${color}; then
+ GREEN=""
+ RED=""
+ ORANGE=""
+ BLUE=""
+ NORMAL=""
+fi
+
+if ${print_time}; then
+ echo "${BLUE}[ INFO ]${NORMAL}" start `date` >&2
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
@@ -409,7 +620,7 @@
D=`get_property ro.serialno`
[ -n "${D}" ] || D=`get_property ro.boot.serialno`
-[ -z "${D}" ] || ANDROID_SERIAL=${D}
+[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
USB_SERIAL=
[ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
grep usb |
@@ -424,16 +635,60 @@
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
-VERITY_WAS_ENABLED=false
+# Report existing partition sizes
+adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+ sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
+ while read name device; do
+ case ${name} in
+ system_[ab] | system | vendor_[ab] | vendor | super | cache)
+ case ${device} in
+ sd*)
+ device=${device%%[0-9]*}/${device}
+ ;;
+ esac
+ size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+ size=`expr ${size} / 2` &&
+ echo "${BLUE}[ INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+ ;;
+ esac
+ done
+
+# Can we test remount -R command?
+overlayfs_supported=true
if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
"2" = "`get_property partition.system.verified`" ]; then
- VERITY_WAS_ENABLED=true
+ restore() {
+ ${overlayfs_supported} || return 0
+ inFastboot &&
+ fastboot reboot &&
+ adb_wait 2m
+ inAdb &&
+ adb_root &&
+ adb enable-verity >/dev/null 2>/dev/null &&
+ 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
-overlayfs_supported=true;
adb_wait || die "wait for device failed"
adb_sh ls -d /sys/module/overlay </dev/null >/dev/null 2>/dev/null ||
adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
@@ -444,20 +699,17 @@
) ||
overlayfs_supported=false
if ${overlayfs_supported}; then
- case `adb_sh uname -r </dev/null` in
- 4.[6789].* | 4.[1-9][0-9]* | [56789].*)
- adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
- echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
- (
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
- false
- ) ||
- overlayfs_supported=false;
- ;;
- *)
- echo "${GREEN}[ OK ]${NORMAL} overlay module uses callers creds" >&2
- ;;
- esac
+ 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].*)
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+ overlayfs_supported=false
+ ;;
+ *)
+ echo "${GREEN}[ OK ]${NORMAL} overlay module uses caller's creds" >&2
+ ;;
+ esac
fi
adb_root ||
@@ -484,9 +736,9 @@
echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
adb_reboot &&
adb_wait 2m ||
- die "lost device after reboot after wipe (USB stack broken?)"
+ die "lost device after reboot after wipe `usb_status`"
adb_root ||
- die "lost device after elevation to root after wipe (USB stack broken?)"
+ die "lost device after elevation to root after wipe `usb_status`"
fi
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
@@ -505,11 +757,12 @@
D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
no_dedupe=true
for d in ${D}; do
- adb_sh tune2fs -l $d 2>&1 |
+ adb_sh tune2fs -l $d </dev/null 2>&1 |
grep "Filesystem features:.*shared_blocks" >/dev/null &&
no_dedupe=false
done
-D=`adb_sh df -k ${D} </dev/null`
+D=`adb_sh df -k ${D} </dev/null |
+ sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
echo "${D}"
if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
overlayfs_needed=false
@@ -523,8 +776,8 @@
H=`adb disable-verity 2>&1`
err=${?}
L=
-D="${H%?Now reboot your device for settings to take effect}"
-if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+D="${H%?Now reboot your device for settings to take effect*}"
+if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
fi
if [ ${err} != 0 ]; then
@@ -548,16 +801,16 @@
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
adb_reboot &&
adb_wait 2m ||
- die "lost device after reboot requested (USB stack broken?)"
+ die "lost device after reboot requested `usb_status`"
adb_root ||
- die "lost device after elevation to root (USB stack broken?)"
+ die "lost device after elevation to root `usb_status`"
rebooted=true
# re-disable verity to see the setup remarks expected
T=`adb_date`
H=`adb disable-verity 2>&1`
err=${?}
- D="${H%?Now reboot your device for settings to take effect}"
- if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+ D="${H%?Now reboot your device for settings to take effect*}"
+ if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
fi
if [ ${err} != 0 ]; then
@@ -588,12 +841,16 @@
echo "${GREEN}[ RUN ]${NORMAL} remount" >&2
-adb remount ||
+D=`adb remount 2>&1`
+ret=${?}
+echo "${D}"
+[ ${ret} != 0 ] ||
+ [ X"${D}" = X"${D##*remount failed}" ] ||
( [ -n "${L}" ] && echo "${L}" && false ) ||
die -t "${T}" "adb remount failed"
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
+ D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` ||
( [ -n "${L}" ] && echo "${L}" && false )
ret=${?}
uses_dynamic_scratch=false
@@ -637,11 +894,34 @@
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
die "overlay takeover after remount"
!(adb_sh grep "^overlay " /proc/mounts </dev/null |
- grep -v "^overlay /\(vendor\|system\|bionic\)/..* overlay ro," |
- grep " overlay ro,") &&
- !(adb_sh grep " rw," /proc/mounts </dev/null |
- skip_administrative_mounts data) ||
+ skip_unrelated_mounts |
+ grep " overlay ro,") ||
die "remount overlayfs missed a spot (ro)"
+ D=`adb_sh grep " rw," /proc/mounts </dev/null |
+ skip_administrative_mounts data`
+ if echo "${D}" | grep /dev/root >/dev/null; then
+ D=`echo / /
+ echo "${D}" | grep -v /dev/root`
+ fi
+ D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+ bad_rw=false
+ for d in ${D}; do
+ if adb_sh tune2fs -l $d </dev/null 2>&1 |
+ grep "Filesystem features:.*shared_blocks" >/dev/null; then
+ bad_rw=true
+ else
+ d=`adb_sh df -k ${D} </dev/null |
+ sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
+ [ X"${d}" = X"${d##* 100[%] }" ] ||
+ bad_rw=true
+ fi
+ done
+ [ -z "${D}" ] ||
+ D=`adb_sh df -k ${D} </dev/null |
+ sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
+ -e 's/^Filesystem /Filesystem (rw) /'`
+ [ -z "${D}" ] || echo "${D}"
+ ${bad_rw} && die "remount overlayfs missed a spot (rw)"
else
if [ ${ret} = 0 ]; then
die -t ${T} "unexpected overlay takeover"
@@ -683,7 +963,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` &&
@@ -694,26 +974,25 @@
adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
skip_administrative_mounts |
- grep -v ' \(squashfs\|ext4\|f2fs\) ' &&
+ grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
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
@@ -722,23 +1001,37 @@
adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
die "pull libc.so from device"
diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
-rm -r ${tempdir}
+rm -rf ${tempdir}
+cleanup() {
+ true
+}
echo "${GREEN}[ OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
echo "${GREEN}[ RUN ]${NORMAL} flash vendor, confirm its content disappears" >&2
H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
+is_bootloader_fastboot=false
+# cuttlefish?
+[ X"${H}" != X"${H#vsoc}" ] || is_bootloader_fastboot=true
is_userspace_fastboot=false
-if [ -z "${ANDROID_PRODUCT_OUT}" ]; then
+
+if ! ${is_bootloader_fastboot}; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} does not support fastboot, skipping"
+elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
echo "${ORANGE}[ WARNING ]${NORMAL} build tree not setup, skipping"
elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
echo "${ORANGE}[ WARNING ]${NORMAL} vendor image missing, skipping"
elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
echo "${ORANGE}[ WARNING ]${NORMAL} wrong vendor image, skipping"
+elif [ -z "${ANDROID_HOST_OUT}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} please run lunch, skipping"
else
- adb reboot-fastboot &&
- fastboot_wait 2m &&
- fastboot flash vendor ||
+ adb reboot fastboot ||
+ die "fastbootd not supported (wrong adb in path?)"
+ 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"
fastboot_getvar is-userspace yes &&
@@ -777,12 +1070,12 @@
die "can not reboot out of fastboot"
echo "${ORANGE}[ WARNING ]${NORMAL} adb after fastboot"
adb_wait 2m ||
- die "did not reboot after flash"
+ die "did not reboot after flash `usb_status`"
if ${overlayfs_needed}; then
adb_root &&
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
+ D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
echo "${H}" &&
echo "${D}" &&
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
@@ -791,26 +1084,22 @@
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
+ check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+ --warning vendor content after flash vendor
fi
fi
@@ -820,7 +1109,7 @@
H=`adb remount 2>&1`
err=${?}
L=
-D="${H%?Now reboot your device for settings to take effect}"
+D="${H%?Now reboot your device for settings to take effect*}"
if [ X"${H}" != X"${D}" ]; then
echo "${ORANGE}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
@@ -838,30 +1127,29 @@
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
echo "${GREEN}[ RUN ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
- adb reboot-fastboot ||
+ adb reboot fastboot ||
die "Reboot into fastbootd"
+ img=${TMPDIR}/adb-remount-test-${$}.img
cleanup() {
- rm /tmp/adb-remount-test.img
+ rm ${img}
}
- dd if=/dev/zero of=/tmp/adb-remount-test.img bs=4096 count=16 2>/dev/null &&
+ dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
fastboot_wait 2m ||
- die "reboot into fastboot"
- fastboot flash --force ${scratch_partition} /tmp/adb-remount-test.img
+ die "reboot into fastboot `usb_status`"
+ fastboot flash --force ${scratch_partition} ${img}
err=${?}
cleanup
cleanup() {
- :
+ true
}
fastboot reboot ||
die "can not reboot out of fastboot"
@@ -873,14 +1161,32 @@
T=`adb_date`
D=`adb disable-verity 2>&1`
err=${?}
- adb remount ||
- die "remount failed"
+ if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
+ then
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+ adb_reboot &&
+ adb_wait 2m &&
+ adb_root ||
+ die "failed to reboot"
+ T=`adb_date`
+ D="${D}
+`adb disable-verity 2>&1`"
+ err=${?}
+ fi
+
echo "${D}"
[ ${err} = 0 ] &&
[ X"${D}" = X"${D##*setup failed}" ] &&
- [ X"${D}" != X"${D##*using overlayfs}" ] &&
+ [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
echo "${GREEN}[ OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
die -t ${T} "setup for overlayfs"
+ D=`adb remount 2>&1`
+ err=${?}
+ echo "${D}"
+ [ ${err} != 0 ] ||
+ [ X"${D}" = X"${D##*remount failed}" ] ||
+ ( echo "${D}" && false ) ||
+ die -t ${T} "remount failed"
fi
echo "${GREEN}[ RUN ]${NORMAL} test raw remount commands" >&2
@@ -888,21 +1194,76 @@
# 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 &&
+ die "lost device after reboot to ro state `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
die "/vendor is not read-only"
-adb_su mount -o rw,remount /vendor ||
+adb_su mount -o rw,remount /vendor </dev/null ||
die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null ||
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
die "/vendor is not read-write"
echo "${GREEN}[ OK ]${NORMAL} mount -o rw,remount command works" >&2
-if $VERITY_WAS_ENABLED && $overlayfs_supported; then
+# 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 enable-verity &&
- adb_reboot &&
+ adb remount -R &&
adb_wait 2m ||
- die "failed to restore verity" >&2
+ 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
+}
+
+[ ${err} = 0 ] ||
+ die "failed to restore verity" >&2
+
echo "${GREEN}[ PASSED ]${NORMAL} adb remount" >&2
+
+test_duration
diff --git a/fs_mgr/tests/data/fstab.example b/fs_mgr/tests/data/fstab.example
deleted file mode 100644
index aebce32..0000000
--- a/fs_mgr/tests/data/fstab.example
+++ /dev/null
@@ -1,15 +0,0 @@
-# Android fstab file.
-
-#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
-
-/dev/block/bootdevice/by-name/system / ext4 ro,barrier=1 wait,slotselect,avb
-/dev/block/bootdevice/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard wait,formattable
-/dev/block/bootdevice/by-name/userdata /data f2fs noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
-/dev/block/bootdevice/by-name/misc /misc emmc defaults defaults
-/dev/block/bootdevice/by-name/modem /vendor/firmware_mnt vfat ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait,slotselect
-/devices/platform/soc/a600000.ssusb/a600000.dwc3* auto vfat defaults voldmanaged=usb:auto
-/dev/block/zram0 none swap defaults zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0 none2 swap nodiratime,remount,bind zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0 none3 swap unbindable,private,slave zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0 none4 swap noexec,shared,rec zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0 none5 swap rw zramsize=1073741824,max_comp_streams=8
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1815a38..72afa69 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -203,10 +203,32 @@
EXPECT_EQ(i, fstab.size());
}
-TEST(fs_mgr, ReadFstabFromFile_MountOptions) {
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_MountOptions) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source / ext4 ro,barrier=1 wait,slotselect,avb
+source /metadata ext4 noatime,nosuid,nodev,discard wait,formattable
+
+source /data f2fs noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
+
+source /misc emmc defaults defaults
+
+source /vendor/firmware_mnt vfat ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait,slotselect
+
+source auto vfat defaults voldmanaged=usb:auto
+source none swap defaults zramsize=1073741824,max_comp_streams=8
+source none2 swap nodiratime,remount,bind zramsize=1073741824,max_comp_streams=8
+source none3 swap unbindable,private,slave zramsize=1073741824,max_comp_streams=8
+source none4 swap noexec,shared,rec zramsize=1073741824,max_comp_streams=8
+source none5 swap rw zramsize=1073741824,max_comp_streams=8
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
Fstab fstab;
- std::string fstab_file = android::base::GetExecutableDirectory() + "/data/fstab.example";
- EXPECT_TRUE(ReadFstabFromFile(fstab_file, &fstab));
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(11U, fstab.size());
EXPECT_EQ("/", fstab[0].mount_point);
EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[0].flags);
@@ -286,7 +308,8 @@
// clang-format on
}
-TEST(fs_mgr, ReadFstabFromFile_FsMgrFlags) {
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_FsMgrFlags) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
@@ -297,7 +320,7 @@
source none4 swap defaults checkpoint=fs
source none5 swap defaults defaults
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -376,7 +399,7 @@
source none2 swap defaults forcefdeorfbe=
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -456,7 +479,7 @@
std::string fstab_contents = R"fs(
source none0 swap defaults encryptable=/dir/key
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -480,7 +503,7 @@
source none2 swap defaults voldmanaged=sdcard:3
source none3 swap defaults voldmanaged=sdcard:auto
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -521,7 +544,7 @@
source none0 swap defaults length=blah
source none1 swap defaults length=123456
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -547,7 +570,7 @@
source none0 swap defaults swapprio=blah
source none1 swap defaults swapprio=123456
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -577,7 +600,7 @@
source none4 swap defaults zramsize=105%
source none5 swap defaults zramsize=%
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -623,7 +646,7 @@
source none0 swap defaults verify=/dir/key
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -646,7 +669,7 @@
source none0 swap defaults forceencrypt=/dir/key
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -669,7 +692,7 @@
source none0 swap defaults forcefdeorfbe=/dir/key
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -704,7 +727,7 @@
source none10 swap defaults fileencryption=ice:adiantum:
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -787,7 +810,7 @@
source none0 swap defaults max_comp_streams=blah
source none1 swap defaults max_comp_streams=123456
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -815,7 +838,7 @@
source none2 swap defaults reservedsize=1K
source none3 swap defaults reservedsize=2m
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -853,7 +876,7 @@
source none2 swap defaults eraseblk=5000
source none3 swap defaults eraseblk=8192
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -891,7 +914,7 @@
source none2 swap defaults logicalblk=5000
source none3 swap defaults logicalblk=8192
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -925,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::WriteStringToFd(fstab_contents, tf.fd));
+ 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);
@@ -941,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) {
@@ -950,7 +980,7 @@
source none0 swap defaults keydirectory=/dir/key
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -972,7 +1002,7 @@
source none0 swap defaults sysfs_path=/sys/device
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -1002,7 +1032,7 @@
)fs";
- ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+ boolean wasRoot = false;
+
+ @Before
+ public void setup() throws DeviceNotAvailableException {
+ wasRoot = getDevice().isAdbRoot();
+ if (!wasRoot) {
+ Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+ }
+
+ Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+ testConditionsMet());
+
+ getDevice().remountSystemWritable();
+ // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+ Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+ Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+ CommandResult result = getDevice().executeShellV2Command("df");
+ Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+ vendorPattern.matcher(result.getStdout()).find());
+ Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+ productPattern.matcher(result.getStdout()).find());
+ }
+
+ private boolean cmdSucceeded(CommandResult result) {
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+
+ private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+ CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+ Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+ }
+
+ /**
+ * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+ */
+ @Test
+ public void testVendorOverlay() throws DeviceNotAvailableException {
+ String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+ // Create files and modify policy
+ CommandResult result = getDevice().executeShellV2Command(
+ "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+ "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+ Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+ assumeMkdirSuccess("/vendor/testdir");
+ assumeMkdirSuccess("/vendor/diffcontext");
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+ result = getDevice().executeShellV2Command(
+ "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+ Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+ result = getDevice().executeShellV2Command(
+ "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+ Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+ getDevice().reboot();
+
+ // Test that the file was overlaid properly
+ result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+ Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+ result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+ Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+ result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+ Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+ }
+
+ // Duplicate of fs_mgr_overlayfs_valid() logic
+ // Requires root
+ public boolean testConditionsMet() throws DeviceNotAvailableException {
+ if (cmdSucceeded(getDevice().executeShellV2Command(
+ "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+ return true;
+ }
+ if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+ return false;
+ }
+ CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+ Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+ Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+ kernelVersionMatcher.find();
+ int majorKernelVersion;
+ int minorKernelVersion;
+ try {
+ majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+ minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+ } catch (Exception e) {
+ return false;
+ }
+ if (majorKernelVersion < 4) {
+ return true;
+ }
+ if (majorKernelVersion > 4) {
+ return false;
+ }
+ if (minorKernelVersion > 6) {
+ return false;
+ }
+ return true;
+ }
+
+ @After
+ public void tearDown() throws DeviceNotAvailableException {
+ if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+ getDevice().reboot();
+ }
+ if (!wasRoot) {
+ getDevice().disableAdbRoot();
+ }
+ }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?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 vendor overlay test cases">
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 63661f0..9309aad 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -316,7 +316,7 @@
DeviceMapper& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableStatus(argv[0], &table)) {
+ if (!dm.GetTableInfo(argv[0], &table)) {
std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
return -EINVAL;
}
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_draw.cpp b/healthd/healthd_draw.cpp
index 706dc80..3da8bda 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -64,7 +64,8 @@
clear_screen();
/* try to display *something* */
- if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
+ if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||
+ batt_anim->num_frames == 0)
draw_unknown(surf_unknown);
else
draw_battery(batt_anim);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 8f2f727..edf34f7 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -36,6 +36,7 @@
#include <linux/netlink.h>
#include <sys/socket.h>
+#include <cutils/android_get_control_file.h>
#include <cutils/klog.h>
#include <cutils/misc.h>
#include <cutils/properties.h>
@@ -54,6 +55,9 @@
using namespace android;
+// main healthd loop
+extern int healthd_main(void);
+
char* locale;
#ifndef max
@@ -73,6 +77,8 @@
#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
#define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
+#define MAX_BATT_LEVEL_WAIT_TIME (3 * MSEC_PER_SEC)
+#define UNPLUGGED_SHUTDOWN_TIME_PROP "ro.product.charger.unplugged_shutdown_time"
#define LAST_KMSG_MAX_SZ (32 * 1024)
@@ -101,6 +107,7 @@
int64_t next_screen_transition;
int64_t next_key_check;
int64_t next_pwr_check;
+ int64_t wait_batt_level_timestamp;
key_state keys[KEY_MAX + 1];
@@ -203,10 +210,9 @@
#define MAX_KLOG_WRITE_BUF_SZ 256
static void dump_last_kmsg(void) {
- char* buf;
+ std::string buf;
char* ptr;
- unsigned sz = 0;
- int len;
+ size_t len;
LOGW("\n");
LOGW("*************** LAST KMSG ***************\n");
@@ -218,21 +224,25 @@
"/proc/last_kmsg",
// clang-format on
};
- for (size_t i = 0; i < arraysize(kmsg); ++i) {
- buf = (char*)load_file(kmsg[i], &sz);
- if (buf && sz) break;
+ for (size_t i = 0; i < arraysize(kmsg) && buf.empty(); ++i) {
+ auto fd = android_get_control_file(kmsg[i]);
+ if (fd >= 0) {
+ android::base::ReadFdToString(fd, &buf);
+ } else {
+ android::base::ReadFileToString(kmsg[i], &buf);
+ }
}
- if (!buf || !sz) {
+ if (buf.empty()) {
LOGW("last_kmsg not found. Cold reset?\n");
goto out;
}
- len = min(sz, LAST_KMSG_MAX_SZ);
- ptr = buf + (sz - len);
+ len = min(buf.size(), LAST_KMSG_MAX_SZ);
+ ptr = &buf[buf.size() - len];
while (len > 0) {
- int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
+ size_t cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
char yoink;
char* nl;
@@ -248,8 +258,6 @@
ptr += cnt;
}
- free(buf);
-
out:
LOGW("\n");
LOGW("************* END LAST KMSG *************\n");
@@ -285,6 +293,21 @@
if (!batt_anim->run || now < charger->next_screen_transition) return;
+ // If battery level is not ready, keep checking in the defined time
+ if (batt_prop == nullptr ||
+ (batt_prop->batteryLevel == 0 && batt_prop->batteryStatus == BATTERY_STATUS_UNKNOWN)) {
+ if (charger->wait_batt_level_timestamp == 0) {
+ // Set max delay time and skip drawing screen
+ charger->wait_batt_level_timestamp = now + MAX_BATT_LEVEL_WAIT_TIME;
+ LOGV("[%" PRId64 "] wait for battery capacity ready\n", now);
+ return;
+ } else if (now <= charger->wait_batt_level_timestamp) {
+ // Do nothing, keep waiting
+ return;
+ }
+ // If timeout and battery level is still not ready, draw unknown battery
+ }
+
if (healthd_draw == nullptr) {
if (healthd_config && healthd_config->screen_on) {
if (!healthd_config->screen_on(batt_prop)) {
@@ -491,6 +514,7 @@
}
static void handle_power_supply_state(charger* charger, int64_t now) {
+ int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
if (!charger->have_battery_state) return;
if (!charger->charger_connected) {
@@ -503,12 +527,14 @@
* Reset & kick animation to show complete animation cycles
* when charger disconnected.
*/
+ timer_shutdown =
+ property_get_int32(UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME);
charger->next_screen_transition = now - 1;
reset_animation(charger->batt_anim);
kick_animation(charger->batt_anim);
- charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
+ charger->next_pwr_check = now + timer_shutdown;
LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
- now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+ now, (int64_t)timer_shutdown, charger->next_pwr_check);
} else if (now >= charger->next_pwr_check) {
LOGW("[%" PRId64 "] shutting down\n", now);
reboot(RB_POWER_OFF);
@@ -704,6 +730,7 @@
charger->next_screen_transition = -1;
charger->next_key_check = -1;
charger->next_pwr_check = -1;
+ charger->wait_batt_level_timestamp = 0;
// Initialize Health implementation (which initializes the internal BatteryMonitor).
Health::initInstance(config);
@@ -711,3 +738,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..69ee34f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -61,6 +61,7 @@
static_libs: [
"libseccomp_policy",
"libavb",
+ "libcgrouprc_format",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
@@ -82,6 +83,7 @@
"liblogwrap",
"liblp",
"libprocessgroup",
+ "libprocessgroup_setup",
"libselinux",
"libutils",
],
@@ -91,7 +93,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 +112,7 @@
"init.cpp",
"keychords.cpp",
"modalias_handler.cpp",
+ "mount_handler.cpp",
"mount_namespace.cpp",
"parser.cpp",
"persistent_properties.cpp",
@@ -132,7 +135,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..39af0e6 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..51deb5a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -57,7 +57,7 @@
The intention of these directories is:
1. /system/etc/init/ is for core system items such as
- SurfaceFlinger, MediaService, and logcatd.
+ SurfaceFlinger, MediaService, and logd.
2. /vendor/etc/init/ is for SoC vendor items such as actions or
daemons needed for core SoC functionality.
3. /odm/etc/init/ is for device manufacturer items such as
@@ -72,7 +72,7 @@
init .rc file should additionally contain any actions associated with
its service.
-An example is the logcatd.rc and Android.mk files located in the
+An example is the userdebug logcatd.rc and Android.mk files located in the
system/core/logcat directory. The LOCAL\_INIT\_RC macro in the
Android.mk file places logcatd.rc in /system/etc/init/ during the
build process. Init loads logcatd.rc during the mount\_all command and
@@ -277,10 +277,6 @@
since it has some peculiarities for backwards compatibility reasons. The 'imports' section of
this file has more details on the order.
-`parse_apex_configs`
- Parses config file(s) from the mounted APEXes. Intented to be used only once
- when apexd notifies the mount event by setting apexd.status to ready.
-
`priority <priority>`
> Scheduling priority of the service process. This value has to be in range
-20 to 19. Default priority is 0. Priority is set via setpriority().
@@ -321,7 +317,7 @@
See the below section on debugging for how this can be used.
`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
-> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
+> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
group default to 0. 'seclabel' is the SELinux security context for the
socket. It defaults to the service security context, as specified by
@@ -492,7 +488,11 @@
This is included in the default init.rc.
`loglevel <level>`
-> Sets the kernel log level to level. Properties are expanded within _level_.
+> Sets init's log level to the integer level, from 7 (all logging) to 0
+ (fatal logging only). The numeric values correspond to the kernel log
+ levels, but this command does not affect the kernel log level. Use the
+ `write` command to write to `/proc/sys/kernel/printk` to change that.
+ Properties are expanded within _level_.
`mkdir <path> [mode] [owner] [group]`
> Create a directory at _path_, optionally with the given mode, owner, and
@@ -510,7 +510,11 @@
> Attempt to mount the named device at the directory _dir_
_flag_s include "ro", "rw", "remount", "noatime", ...
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
- a comma separated string, eg: barrier=1,noauto\_da\_alloc
+ a comma separated string, e.g. barrier=1,noauto\_da\_alloc
+
+`parse_apex_configs`
+> Parses config file(s) from the mounted APEXes. Intended to be used only once
+ when apexd notifies the mount event by setting apexd.status to ready.
`restart <service>`
> Stops and restarts a running service, does nothing if the service is currently
@@ -572,7 +576,7 @@
`symlink <target> <path>`
> Create a symbolic link at _path_ with the value _target_
-`sysclktz <mins_west_of_gmt>`
+`sysclktz <minutes_west_of_gmt>`
> Set the system clock base (0 if system clock ticks in GMT)
`trigger <event>`
@@ -635,7 +639,7 @@
earlier executed trigger, or 2) place it in an Action with the same
trigger within the same file at an earlier line.
-Nonetheless, the defacto order for first stage mount devices is:
+Nonetheless, the de facto order for first stage mount devices is:
1. /init.rc is parsed then recursively each of its imports are
parsed.
2. The contents of /system/etc/init/ are alphabetized and parsed
@@ -660,12 +664,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
-----------
@@ -718,7 +729,7 @@
A handy script named compare-bootcharts.py can be used to compare the
start/end time of selected processes. The aforementioned grab-bootchart.sh
will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
-If two such barballs are preserved on the host machine under different
+If two such tarballs are preserved on the host machine under different
directories, the script can list the timestamps differences. For example:
Usage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_
@@ -766,7 +777,7 @@
This option will send SIGSTOP to a service immediately before calling exec. This gives a window
where developers can attach a debugger, strace, etc before continuing the service with SIGCONT.
-This flag can also be dynamically controled via the ctl.sigstop_on and ctl.sigstop_off properties.
+This flag can also be dynamically controlled via the ctl.sigstop_on and ctl.sigstop_off properties.
Below is an example of dynamically debugging logd via the above:
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/devices.cpp b/init/devices.cpp
index 1a77ba1..159c75e 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -21,8 +21,14 @@
#include <sys/sysmacros.h>
#include <unistd.h>
+#include <chrono>
+#include <map>
#include <memory>
+#include <string>
+#include <thread>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -37,12 +43,16 @@
#error "Do not include init.h in files used by ueventd; it will expose init's globals"
#endif
+using namespace std::chrono_literals;
+
using android::base::Basename;
using android::base::Dirname;
+using android::base::ReadFileToString;
using android::base::Readlink;
using android::base::Realpath;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::Trim;
namespace android {
namespace init {
@@ -101,6 +111,31 @@
return true;
}
+// Given a path that may start with a virtual dm block device, populate
+// the supplied buffer with the dm module's instantiated name.
+// If it doesn't start with a virtual block device, or there is some
+// error, return false.
+static bool FindDmDevicePartition(const std::string& path, std::string* result) {
+ result->clear();
+ if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
+ if (getpid() == 1) return false; // first_stage_init has no sepolicy needs
+
+ static std::map<std::string, std::string> cache;
+ // wait_for_file will not work, the content is also delayed ...
+ for (android::base::Timer t; t.duration() < 200ms; std::this_thread::sleep_for(10ms)) {
+ if (ReadFileToString("/sys" + path + "/dm/name", result) && !result->empty()) {
+ // Got it, set cache with result, when node arrives
+ cache[path] = *result = Trim(*result);
+ return true;
+ }
+ }
+ auto it = cache.find(path);
+ if ((it == cache.end()) || (it->second.empty())) return false;
+ // Return cached results, when node goes away
+ *result = it->second;
+ return true;
+}
+
Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
: name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
// Set 'prefix_' or 'wildcard_' based on the below cases:
@@ -293,6 +328,7 @@
std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
std::string device;
std::string type;
+ std::string partition;
if (FindPlatformDevice(uevent.path, &device)) {
// Skip /devices/platform or /devices/ if present
@@ -310,6 +346,8 @@
type = "pci";
} else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
+ } else if (FindDmDevicePartition(uevent.path, &partition)) {
+ return {"/dev/block/mapper/" + partition};
} else {
return {};
}
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 1b077bc..3900f72 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -47,6 +47,7 @@
using android::base::Split;
using android::base::Timer;
using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
using android::fs_mgr::AvbHashtreeResult;
using android::fs_mgr::AvbUniquePtr;
using android::fs_mgr::BuildGsiSystemFstabEntry;
@@ -77,8 +78,9 @@
ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
bool InitRequiredDevices();
bool InitMappedDevice(const std::string& verity_device);
+ bool InitDeviceMapper();
bool CreateLogicalPartitions();
- bool MountPartition(const Fstab::iterator& begin, bool erase_used_fstab_entry,
+ bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end = nullptr);
bool MountPartitions();
@@ -96,6 +98,7 @@
virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
bool need_dm_verity_;
+ bool gsi_not_on_userdata_ = false;
Fstab fstab_;
std::string lp_metadata_partition_;
@@ -188,6 +191,29 @@
return true;
}
+static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta,
+ const AvbHandle& standalone_vbmeta,
+ const FstabEntry& fstab_entry) {
+ std::string old_spl = builtin_vbmeta.GetSecurityPatchLevel(fstab_entry);
+ std::string new_spl = standalone_vbmeta.GetSecurityPatchLevel(fstab_entry);
+
+ bool rollbacked = false;
+ if (old_spl.empty() || new_spl.empty() || new_spl < old_spl) {
+ rollbacked = true;
+ }
+
+ if (rollbacked) {
+ LOG(ERROR) << "Image rollback detected for " << fstab_entry.mount_point
+ << ", SPL switches from '" << old_spl << "' to '" << new_spl << "'";
+ if (AvbHandle::IsDeviceUnlocked()) {
+ LOG(INFO) << "Allowing rollbacked standalone image when the device is unlocked";
+ return false;
+ }
+ }
+
+ return rollbacked;
+}
+
// Class Definitions
// -----------------
FirstStageMount::FirstStageMount(Fstab fstab)
@@ -243,8 +269,6 @@
}
required_devices_partition_names_.emplace(super_partition_name_);
- // When booting from live GSI images, userdata is the super device.
- required_devices_partition_names_.emplace("userdata");
return true;
}
@@ -257,25 +281,7 @@
}
if (IsDmLinearEnabled() || need_dm_verity_) {
- const std::string dm_path = "/devices/virtual/misc/device-mapper";
- bool found = false;
- auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
- if (uevent.path == dm_path) {
- device_handler_->HandleUevent(uevent);
- found = true;
- return ListenerAction::kStop;
- }
- return ListenerAction::kContinue;
- };
- uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
- if (!found) {
- LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
- Timer t;
- uevent_listener_.Poll(dm_callback, 10s);
- LOG(INFO) << "Wait for device-mapper returned after " << t;
- }
- if (!found) {
- LOG(ERROR) << "device-mapper device not found after polling timeout";
+ if (!InitDeviceMapper()) {
return false;
}
}
@@ -303,11 +309,36 @@
return true;
}
+bool FirstStageMount::InitDeviceMapper() {
+ const std::string dm_path = "/devices/virtual/misc/device-mapper";
+ bool found = false;
+ auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+ if (uevent.path == dm_path) {
+ device_handler_->HandleUevent(uevent);
+ found = true;
+ return ListenerAction::kStop;
+ }
+ return ListenerAction::kContinue;
+ };
+ uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+ if (!found) {
+ LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(dm_callback, 10s);
+ LOG(INFO) << "Wait for device-mapper returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "device-mapper device not found after polling timeout";
+ return false;
+ }
+ return true;
+}
+
bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);
for (const auto& partition_name : partition_names) {
- const auto super_device = android::fs_mgr::GetMetadataSuperBlockDevice(metadata);
- if (partition_name == android::fs_mgr::GetBlockDevicePartitionName(*super_device)) {
+ // The super partition was found in the earlier pass.
+ if (partition_name == super_partition_name_) {
continue;
}
required_devices_partition_names_.emplace(partition_name);
@@ -406,21 +437,26 @@
uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
if (!found) {
- LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+ LOG(INFO) << "dm device '" << dm_device << "' not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(verity_callback, 10s);
- LOG(INFO) << "wait for dm-verity device returned after " << t;
+ LOG(INFO) << "wait for dm device '" << dm_device << "' returned after " << t;
}
if (!found) {
- LOG(ERROR) << "dm-verity device not found after polling timeout";
+ LOG(ERROR) << "dm device '" << dm_device << "' not found after polling timeout";
return false;
}
return true;
}
-bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_used_fstab_entry,
+bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end) {
+ // Sets end to begin + 1, so we can just return on failure below.
+ if (end) {
+ *end = begin + 1;
+ }
+
if (begin->fs_mgr_flags.logical) {
if (!fs_mgr_update_logical_partition(&(*begin))) {
return false;
@@ -446,7 +482,7 @@
mounted = (fs_mgr_do_mount_one(*current) == 0);
}
}
- if (erase_used_fstab_entry) {
+ if (erase_same_mounts) {
current = fstab_.erase(begin, current);
}
if (end) {
@@ -463,7 +499,7 @@
return entry.mount_point == "/metadata";
});
if (metadata_partition != fstab_.end()) {
- if (MountPartition(metadata_partition, true /* erase_used_fstab_entry */)) {
+ if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
UseGsiIfPresent();
}
}
@@ -474,7 +510,11 @@
if (system_partition == fstab_.end()) return true;
- if (MountPartition(system_partition, false)) {
+ if (MountPartition(system_partition, false /* erase_same_mounts */)) {
+ if (gsi_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+ LOG(ERROR) << "check_most_at_once forbidden on external media";
+ return false;
+ }
SwitchRoot("/system");
} else {
PLOG(ERROR) << "Failed to mount /system";
@@ -525,7 +565,7 @@
}
Fstab::iterator end;
- if (!MountPartition(current, false, &end)) {
+ if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
if (current->fs_mgr_flags.no_fail) {
LOG(INFO) << "Failed to mount " << current->mount_point
<< ", ignoring mount for no_fail partition";
@@ -556,7 +596,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);
}
@@ -581,7 +628,29 @@
return;
}
- if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), "/dev/block/by-name/userdata")) {
+ if (!InitDmLinearBackingDevices(*metadata.get())) {
+ return;
+ }
+
+ // Device-mapper might not be ready if the device doesn't use DAP or verity
+ // (for example, hikey).
+ if (access("/dev/device-mapper", F_OK) && !InitDeviceMapper()) {
+ return;
+ }
+
+ // Find the name of the super partition for the GSI. It will either be
+ // "userdata", or a block device such as an sdcard. There are no by-name
+ // partitions other than userdata that we support installing GSIs to.
+ auto super = GetMetadataSuperBlockDevice(*metadata.get());
+ std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+ std::string super_path;
+ if (super_name == "userdata") {
+ super_path = "/dev/block/by-name/" + super_name;
+ } else {
+ super_path = "/dev/block/" + super_name;
+ }
+
+ if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
LOG(ERROR) << "GSI partition layout could not be instantiated";
return;
}
@@ -599,6 +668,7 @@
fstab_.erase(system_partition);
}
fstab_.emplace_back(BuildGsiSystemFstabEntry());
+ gsi_not_on_userdata_ = (super_name != "userdata");
}
bool FirstStageMountVBootV1::GetDmVerityDevices() {
@@ -732,13 +802,38 @@
bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
AvbHashtreeResult hashtree_result;
- if (fstab_entry->fs_mgr_flags.avb) {
+ // It's possible for a fstab_entry to have both avb_keys and avb flag.
+ // In this case, try avb_keys first, then fallback to avb flag.
+ 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 ||
+ avb_handle_->status() == AvbHandleStatus::kVerificationDisabled) {
+ LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for "
+ << fstab_entry->mount_point;
+ return true; // Returns true to mount the partition directly.
+ } else {
+ auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+ if (!avb_standalone_handle) {
+ LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
+ // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
+ if (!fstab_entry->fs_mgr_flags.avb) return false;
+ LOG(INFO) << "Fallback to built-in hashtree for " << fstab_entry->mount_point;
+ hashtree_result =
+ avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+ } else {
+ // Sets up hashtree via the standalone handle.
+ if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
+ return false;
+ }
+ hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
+ fstab_entry, false /* wait_for_verity_dev */);
+ }
+ }
+ } else if (fstab_entry->fs_mgr_flags.avb) {
if (!InitAvbHandle()) return false;
hashtree_result =
avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
- } else if (!fstab_entry->avb_key.empty()) {
- hashtree_result =
- AvbHandle::SetUpStandaloneAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
} else {
return true; // No need AVB, returns true to mount the partition directly.
}
@@ -754,8 +849,6 @@
default:
return false;
}
-
- return true; // Returns true to mount the partition.
}
bool FirstStageMountVBootV2::InitAvbHandle() {
diff --git a/init/init.cpp b/init/init.cpp
index a8924f2..0f44efd 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -39,10 +39,13 @@
#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>
+#include <libgsi/libgsi.h>
#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
#include <selinux/android.h>
#ifndef RECOVERY
@@ -55,6 +58,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"
@@ -73,6 +77,7 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::fs_mgr::AvbHandle;
namespace android {
namespace init {
@@ -91,6 +96,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;
@@ -352,8 +358,8 @@
static Result<Success> SetupCgroupsAction(const BuiltinArguments&) {
// Have to create <CGROUPS_RC_DIR> using make_dir function
// for appropriate sepolicy to be set for it
- make_dir(CGROUPS_RC_DIR, 0711);
- if (!CgroupSetupCgroups()) {
+ make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
+ if (!CgroupSetup()) {
return ErrnoError() << "Failed to setup cgroups";
}
@@ -654,10 +660,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();
@@ -671,10 +684,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;
@@ -695,6 +709,13 @@
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
+ // Make the GSI status available before scripts start running.
+ if (android::gsi::IsGsiRunning()) {
+ property_set("ro.gsid.image_running", "1");
+ } else {
+ property_set("ro.gsid.image_running", "0");
+ }
+
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueEventTrigger("early-init");
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
new file mode 100644
index 0000000..c8f0e76
--- /dev/null
+++ b/init/mount_handler.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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 <vector>
+
+#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 <libdm/dm.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") {
+ auto& dm = dm::DeviceMapper::Instance();
+ std::string path;
+ if (dm.GetDmDevicePathByName("system", &path) || dm.GetDmDevicePathByName("vroot", &path)) {
+ fields[0] = path;
+ } else if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
+ auto entry = GetEntryForMountPoint(&fstab, "/");
+ if (entry || (entry = GetEntryForMountPoint(&fstab, "/system"))) {
+ 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 = "";
+ // Clear the noise associated with loopback and APEX.
+ if (android::base::StartsWith(value, "loop")) value = "";
+ if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
+ }
+ auto mount_prop = entry.mount_point;
+ if (mount_prop == "/") mount_prop = "/root";
+ std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
+ mount_prop = "dev.mnt.blk" + mount_prop;
+ // Set property even if its value does not change to trigger 'on property:'
+ // handling, except for clearing non-existent or already clear property.
+ // Goal is reduction of empty properties and associated triggers.
+ if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
+ property_set(mount_prop, 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());
+ std::vector<MountHandlerEntry> touched;
+ auto untouched = mounts_;
+ char* buf = nullptr;
+ size_t len = 0;
+ while (getline(&buf, &len, fp_.get()) != -1) {
+ auto entry = ParseMount(std::string(buf));
+ auto match = untouched.find(entry);
+ if (match == untouched.end()) {
+ touched.emplace_back(std::move(entry));
+ } else {
+ untouched.erase(match);
+ }
+ }
+ free(buf);
+ for (auto entry : untouched) {
+ SetMountProperty(entry, false);
+ mounts_.erase(entry);
+ }
+ for (auto entry : touched) {
+ SetMountProperty(entry, true);
+ mounts_.emplace(std::move(entry));
+ }
+}
+
+} // 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 46e5e12..467568c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,6 +39,7 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
+#include <map>
#include <memory>
#include <queue>
#include <vector>
@@ -442,8 +443,8 @@
}
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr, std::string* error) {
+uint32_t CheckPermissions(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr, std::string* error) {
if (!IsLegalPropertyName(name)) {
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
@@ -456,7 +457,6 @@
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
- HandleControlMessage(name.c_str() + 4, value, cr.pid);
return PROP_SUCCESS;
}
@@ -475,6 +475,21 @@
return PROP_ERROR_INVALID_VALUE;
}
+ return PROP_SUCCESS;
+}
+
+// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr, std::string* error) {
+ if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
+ return ret;
+ }
+
+ if (StartsWith(name, "ctl.")) {
+ HandleControlMessage(name.c_str() + 4, value, cr.pid);
+ return PROP_SUCCESS;
+ }
+
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") {
@@ -542,9 +557,8 @@
uint32_t result =
HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
break;
@@ -564,9 +578,8 @@
std::string error;
uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
socket.SendUint32(result);
break;
@@ -579,13 +592,15 @@
}
}
-static bool load_properties_from_file(const char *, const char *);
+static bool load_properties_from_file(const char*, const char*,
+ std::map<std::string, std::string>*);
/*
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
-static void LoadProperties(char* data, const char* filter, const char* filename) {
+static void LoadProperties(char* data, const char* filter, const char* filename,
+ std::map<std::string, std::string>* properties) {
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
@@ -624,7 +639,7 @@
while (isspace(*key)) key++;
}
- load_properties_from_file(fn, key);
+ load_properties_from_file(fn, key, properties);
} else {
value = strchr(key, '=');
@@ -651,12 +666,19 @@
continue;
}
- uint32_t result = 0;
ucred cr = {.pid = 1, .uid = 0, .gid = 0};
std::string error;
- result = HandlePropertySet(key, value, context, cr, &error);
- if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+ if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {
+ auto it = properties->find(key);
+ if (it == properties->end()) {
+ (*properties)[key] = value;
+ } else if (it->second != value) {
+ LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
+ << it->second << "' with new value '" << value << "'";
+ it->second = value;
+ }
+ } else {
+ LOG(ERROR) << "Do not have permissions to set '" << key << "' to '" << value
<< "' in property file '" << filename << "': " << error;
}
}
@@ -665,7 +687,8 @@
// Filter is used to decide which properties to load: NULL loads all keys,
// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter) {
+static bool load_properties_from_file(const char* filename, const char* filter,
+ std::map<std::string, std::string>* properties) {
Timer t;
auto file_contents = ReadFile(filename);
if (!file_contents) {
@@ -675,7 +698,7 @@
}
file_contents->push_back('\n');
- LoadProperties(file_contents->data(), filter, filename);
+ LoadProperties(file_contents->data(), filter, filename, properties);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
return true;
}
@@ -698,7 +721,15 @@
static void load_override_properties() {
if (ALLOW_LOCAL_PROP_OVERRIDE) {
- load_properties_from_file("/data/local.prop", NULL);
+ std::map<std::string, std::string> properties;
+ load_properties_from_file("/data/local.prop", nullptr, &properties);
+ for (const auto& [name, value] : properties) {
+ std::string error;
+ if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+ LOG(ERROR) << "Could not set '" << name << "' to '" << value
+ << "' in /data/local.prop: " << error;
+ }
+ }
}
}
@@ -833,26 +864,41 @@
}
}
-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
- // TODO(b/122864654): read the prop files from all partitions and then
- // resolve the duplication by their origin so that RO and non-RO properties
- // have a consistent overriding order.
- if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
+ // 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
+ // property files, regardless of if they are "ro." properties or not.
+ std::map<std::string, std::string> properties;
+ if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
// Try recovery path
- if (!load_properties_from_file("/prop.default", NULL)) {
+ if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
// Try legacy path
- load_properties_from_file("/default.prop", NULL);
+ load_properties_from_file("/default.prop", nullptr, &properties);
}
}
- load_properties_from_file("/product/build.prop", NULL);
- load_properties_from_file("/product_services/build.prop", NULL);
- load_properties_from_file("/odm/default.prop", NULL);
- load_properties_from_file("/vendor/default.prop", NULL);
- load_properties_from_file("/system/build.prop", NULL);
- load_properties_from_file("/odm/build.prop", NULL);
- load_properties_from_file("/vendor/build.prop", NULL);
- load_properties_from_file("/factory/factory.prop", "ro.*");
+ load_properties_from_file("/system/build.prop", nullptr, &properties);
+ load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+ load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+ load_properties_from_file("/odm/default.prop", nullptr, &properties);
+ load_properties_from_file("/odm/build.prop", nullptr, &properties);
+ load_properties_from_file("/product/build.prop", nullptr, &properties);
+ 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) {
+ LOG(ERROR) << "Could not set '" << name << "' to '" << value
+ << "' while loading .prop files" << error;
+ }
+ }
property_initialize_ro_product_props();
property_derive_build_fingerprint();
@@ -908,6 +954,13 @@
LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
&property_infos);
}
+ if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
+ &property_infos);
+ }
+ if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
+ }
} else {
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
return;
@@ -916,6 +969,8 @@
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
}
+ LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
+ LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
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..f5c13b9 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -177,7 +177,7 @@
Result<Success> Service::EnterNamespaces() const {
for (const auto& [nstype, path] : namespaces_to_enter_) {
auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
- if (!fd) {
+ if (fd == -1) {
return ErrnoError() << "Could not open namespace at " << path;
}
if (setns(fd, nstype) == -1) {
@@ -756,6 +756,11 @@
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
return Error() << "file type must be 'r', 'w' or 'rw'";
}
+ std::string expanded;
+ if (!expand_props(args[1], &expanded)) {
+ return Error() << "Could not expand property in file path '" << args[1] << "'";
+ }
+ args[1] = std::move(expanded);
if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
return Error() << "file name must not be relative";
}
@@ -1059,7 +1064,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/janitors/OWNERS b/janitors/OWNERS
index 0610b41..3e32c26 100644
--- a/janitors/OWNERS
+++ b/janitors/OWNERS
@@ -1,4 +1,6 @@
# OWNERS file for projects that don't really have owners so much as volunteer janitors.
+ccross@google.com
+dwillemsen@google.com
enh@google.com
hhb@google.com
narayan@google.com
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 6bec63c..71980d7 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -170,5 +170,7 @@
return "Failed to unwind due to invalid unwind information";
case BACKTRACE_UNWIND_ERROR_REPEATED_FRAME:
return "Failed to unwind due to same sp/pc repeating";
+ case BACKTRACE_UNWIND_ERROR_INVALID_ELF:
+ return "Failed to unwind due to invalid elf";
}
}
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 6a967f7..781819a 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -46,8 +46,7 @@
}
}
-BacktraceMap::~BacktraceMap() {
-}
+BacktraceMap::~BacktraceMap() {}
void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
ScopedBacktraceMapIteratorLock lock(this);
@@ -68,12 +67,13 @@
char permissions[5];
int name_pos;
-// Mac OS vmmap(1) output:
-// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0 1 2 3 4 5
- if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c %n",
- &start, &end, permissions, &name_pos) != 3) {
+ // Mac OS vmmap(1) output:
+ // __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW
+ // /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+ // 012345678901234567890123456789012345678901234567890123456789
+ // 0 1 2 3 4 5
+ if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c %n", &start, &end,
+ permissions, &name_pos) != 3) {
return false;
}
@@ -90,21 +90,21 @@
map->flags |= PROT_EXEC;
}
- map->name = line+name_pos;
- if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
- map->name.erase(map->name.length()-1);
+ map->name = line + name_pos;
+ if (!map->name.empty() && map->name[map->name.length() - 1] == '\n') {
+ map->name.erase(map->name.length() - 1);
}
- ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
- reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
- map->flags, map->name.c_str());
+ ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", reinterpret_cast<void*>(map->start),
+ reinterpret_cast<void*>(map->end), map->flags, map->name.c_str());
return true;
}
#endif // defined(__APPLE__)
bool BacktraceMap::Build() {
#if defined(__APPLE__)
- char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
+ char
+ cmd[sizeof(pid_t) * 3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
char line[1024];
// cmd is guaranteed to always be big enough to hold this string.
snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
@@ -113,7 +113,7 @@
return false;
}
- while(fgets(line, sizeof(line), fp)) {
+ while (fgets(line, sizeof(line), fp)) {
backtrace_map_t map;
if (ParseLine(line, &map)) {
maps_.push_back(map);
@@ -123,7 +123,7 @@
return true;
#else
return android::procinfo::ReadProcessMaps(
- pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) {
+ pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t, const char* name) {
maps_.resize(maps_.size() + 1);
backtrace_map_t& map = maps_.back();
map.start = start;
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index f5f9b2a..36640cd 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -89,6 +89,10 @@
case unwindstack::ERROR_REPEATED_FRAME:
error->error_code = BACKTRACE_UNWIND_ERROR_REPEATED_FRAME;
break;
+
+ case unwindstack::ERROR_INVALID_ELF:
+ error->error_code = BACKTRACE_UNWIND_ERROR_INVALID_ELF;
+ break;
}
}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
index 33c4282..4ec591d 100644
--- a/libbacktrace/UnwindStack.h
+++ b/libbacktrace/UnwindStack.h
@@ -58,7 +58,7 @@
bool Unwind(size_t num_ignore_frames, void* context) override;
- std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset);
+ std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
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/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 10e790b..404e7e8 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -64,6 +64,8 @@
BACKTRACE_UNWIND_ERROR_UNWIND_INFO,
// Unwind information stopped due to sp/pc repeating.
BACKTRACE_UNWIND_ERROR_REPEATED_FRAME,
+ // Unwind information stopped due to invalid elf.
+ BACKTRACE_UNWIND_ERROR_INVALID_ELF,
};
struct BacktraceUnwindError {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index b4b8cd1..619bc56 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -18,7 +18,6 @@
// they correspond to features not used by our host development tools
// which are also hard or even impossible to port to native Win32
libcutils_nonwindows_sources = [
- "android_get_control_file.cpp",
"fs.cpp",
"hashmap.cpp",
"multiuser.cpp",
@@ -84,6 +83,8 @@
],
},
windows: {
+ host_ldlibs: ["-lws2_32"],
+
srcs: [
"socket_inaddr_any_server_windows.cpp",
"socket_network_client_windows.cpp",
@@ -92,9 +93,6 @@
],
enabled: true,
- shared: {
- enabled: false,
- },
cflags: [
"-D_GNU_SOURCE",
],
@@ -102,6 +100,7 @@
android: {
srcs: libcutils_nonwindows_sources + [
+ "android_get_control_file.cpp",
"android_reboot.cpp",
"ashmem-dev.cpp",
"fs_config.cpp",
@@ -236,6 +235,7 @@
"libbase",
"libjsoncpp",
"libprocessgroup",
+ "libcgrouprc",
]
cc_test {
@@ -250,7 +250,10 @@
name: "libcutils_test_static",
test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
- static_libs: ["libc"] + test_libraries,
+ static_libs: [
+ "libc",
+ "libcgrouprc_format",
+ ] + test_libraries,
stl: "libc++_static",
target: {
diff --git a/libcutils/android_get_control_env.h b/libcutils/android_get_control_env.h
index 638c831..a830269 100644
--- a/libcutils/android_get_control_env.h
+++ b/libcutils/android_get_control_env.h
@@ -14,20 +14,13 @@
* limitations under the License.
*/
-#ifndef __CUTILS_ANDROID_GET_CONTROL_ENV_H
-#define __CUTILS_ANDROID_GET_CONTROL_ENV_H
+#pragma once
-/* To declare library function hidden and internal */
-#define LIBCUTILS_HIDDEN __attribute__((visibility("hidden")))
+#include <sys/cdefs.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
-LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
- const char* name);
-#ifdef __cplusplus
-}
-#endif
+int __android_get_control_from_env(const char* prefix, const char* name)
+ __attribute__((visibility("hidden")));
-#endif /* __CUTILS_ANDROID_GET_CONTROL_ENV_H */
+__END_DECLS
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
index d8121f5..d5b0894 100644
--- a/libcutils/android_get_control_file.cpp
+++ b/libcutils/android_get_control_file.cpp
@@ -39,14 +39,14 @@
#include <sys/types.h>
#include <unistd.h>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
#include "android_get_control_env.h"
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
-#endif
-
-LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
- const char* name) {
+int __android_get_control_from_env(const char* prefix, const char* name) {
if (!prefix || !name) return -1;
char *key = NULL;
@@ -67,48 +67,33 @@
long fd = strtol(val, NULL, 10);
if (errno) return -1;
- // validity checking
+ // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
if ((fd < 0) || (fd > INT_MAX)) return -1;
- // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
-
// Still open?
-#if defined(F_GETFD) // Lowest overhead
if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;
-#elif defined(F_GETFL) // Alternate lowest overhead
- if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1;
-#else // Hail Mary pass
- struct stat s;
- if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1;
-#endif
return static_cast<int>(fd);
}
int android_get_control_file(const char* path) {
- int fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+ std::string given_path;
+ if (!android::base::Realpath(path, &given_path)) return -1;
-#if defined(__linux__)
- // Find file path from /proc and make sure it is correct
- char *proc = NULL;
- if (asprintf(&proc, "/proc/self/fd/%d", fd) < 0) return -1;
- if (!proc) return -1;
-
- size_t len = strlen(path);
- // readlink() does not guarantee a nul byte, len+2 so we catch truncation.
- char *buf = static_cast<char *>(calloc(1, len + 2));
- if (!buf) {
- free(proc);
- return -1;
+ // Try path, then realpath(path), as keys to get the fd from env.
+ auto fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+ if (fd < 0) {
+ fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, given_path.c_str());
+ if (fd < 0) return fd;
}
- ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1));
- free(proc);
- int cmp = (len != static_cast<size_t>(ret)) || strcmp(buf, path);
- free(buf);
- if (ret < 0) return -1;
- if (cmp != 0) return -1;
+
+ // Find file path from /proc and make sure it is correct
+ auto proc = android::base::StringPrintf("/proc/self/fd/%d", fd);
+ std::string fd_path;
+ if (!android::base::Realpath(proc, &fd_path)) return -1;
+
+ if (given_path != fd_path) return -1;
// It is what we think it is
-#endif
return fd;
}
diff --git a/libcutils/android_reboot.cpp b/libcutils/android_reboot.cpp
index ce41cd3..e0def71 100644
--- a/libcutils/android_reboot.cpp
+++ b/libcutils/android_reboot.cpp
@@ -23,12 +23,12 @@
#define TAG "android_reboot"
-int android_reboot(int cmd, int /*flags*/, const char* arg) {
+int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
int ret;
const char* restart_cmd = NULL;
char* prop_value;
- switch (static_cast<unsigned>(cmd)) {
+ switch (cmd) {
case ANDROID_RB_RESTART: // deprecated
case ANDROID_RB_RESTART2:
restart_cmd = "reboot";
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index e35b91a..e67b458 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -73,6 +73,8 @@
#ifndef __ANDROID_VNDK__
using openFdType = int (*)();
+static openFdType openFd;
+
openFdType initOpenAshmemFd() {
openFdType openFd = nullptr;
void* handle = dlopen("libashmemd_client.so", RTLD_NOW);
@@ -221,7 +223,10 @@
int fd = -1;
#ifndef __ANDROID_VNDK__
- static auto openFd = initOpenAshmemFd();
+ if (!openFd) {
+ openFd = initOpenAshmemFd();
+ }
+
if (openFd) {
fd = openFd();
}
@@ -480,3 +485,11 @@
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
}
+
+void ashmem_init() {
+#ifndef __ANDROID_VNDK__
+ pthread_mutex_lock(&__ashmem_lock);
+ openFd = initOpenAshmemFd();
+ pthread_mutex_unlock(&__ashmem_lock);
+#endif //__ANDROID_VNDK__
+}
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index bb990d5..32446d4 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -82,3 +82,5 @@
return buf.st_size;
}
+
+void ashmem_init() {}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index f1dcd50..6217bc8 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -91,7 +91,7 @@
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/xbin" },
@@ -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/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 99030ed..cd27eef 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __CUTILS_ANDROID_REBOOT_H__
-#define __CUTILS_ANDROID_REBOOT_H__
+#pragma once
#include <sys/cdefs.h>
@@ -36,10 +35,8 @@
/* Reboot or shutdown the system.
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
* Due to that, process calling this should have proper selinux permission
- * to write to the property. Otherwise, the call will fail.
+ * to write to the property or the call will fail.
*/
-int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot(unsigned cmd, int flags, const char* arg);
__END_DECLS
-
-#endif /* __CUTILS_ANDROID_REBOOT_H__ */
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..abc5068 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,6 +26,7 @@
int ashmem_pin_region(int fd, size_t offset, size_t len);
int ashmem_unpin_region(int fd, size_t offset, size_t len);
int ashmem_get_size_region(int fd);
+void ashmem_init();
#ifdef __cplusplus
}
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/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 2248817..6acdcd8 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -16,8 +16,6 @@
#include <cutils/sockets.h>
-#define LOG_TAG "socket-unix"
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -27,9 +25,6 @@
#include <time.h>
#include <unistd.h>
-#include <cutils/android_get_control_file.h>
-#include <log/log.h>
-
#include "android_get_control_env.h"
int socket_close(int sock) {
@@ -62,6 +57,7 @@
return writev(sock, iovec_buffers, num_buffers);
}
+#if defined(__ANDROID__)
int android_get_control_socket(const char* name) {
int fd = __android_get_control_from_env(ANDROID_SOCKET_ENV_PREFIX, name);
@@ -82,3 +78,8 @@
}
return -1;
}
+#else
+int android_get_control_socket(const char*) {
+ return -1;
+}
+#endif
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index c9580af..e3da77b 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -80,7 +80,7 @@
// Determine whether application-level tracing is enabled for this process.
static bool atrace_is_app_tracing_enabled()
{
- bool sys_debuggable = __android_log_is_debuggable();
+ bool sys_debuggable = property_get_bool("ro.debuggable", 0);
bool result = false;
if (sys_debuggable || atrace_is_debuggable) {
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..fe89e62 100644
--- a/libkeyutils/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl.cpp
@@ -18,159 +18,73 @@
* 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 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 == "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..79b4680
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 <error.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/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 key_serial_t GetKeyringIdOrDie(const std::string& keyring_desc) {
+ // If the keyring id is already a hex number, directly convert it to keyring id
+ key_serial_t keyring_id;
+ if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {
+ return keyring_id;
+ }
+
+ // Only keys allowed by SELinux rules will be shown here.
+ std::ifstream proc_keys_file("/proc/keys");
+ if (!proc_keys_file.is_open()) {
+ error(1, errno, "Failed to open /proc/keys");
+ return -1;
+ }
+
+ 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 = "0x" + 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;
+ }
+ if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {
+ error(1, 0, "Unexpected key format in /proc/keys: %s", key_id.c_str());
+ return -1;
+ }
+ return keyring_id;
+ }
+ return -1;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+ if (keyctl_unlink(key, keyring_id) < 0) {
+ error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str());
+ 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) {
+ error(1, 0, "Certificate too large");
+ return 1;
+ }
+
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ error(1, errno, "Failed to add key");
+ return 1;
+ }
+
+ std::cout << key << std::endl;
+ return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+
+ // read from stdin to get the certificates
+ std::istreambuf_iterator<char> begin(std::cin), end;
+ std::string data(begin, end);
+
+ if (data.size() > kMaxCertSize) {
+ error(1, 0, "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) {
+ error(1, errno, "Failed to add key");
+ return 1;
+ }
+
+ std::cout << key << std::endl;
+ return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str());
+ 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) {
+ error(1, errno, "Cannot get security context of key %x", key);
+ return std::string();
+ }
+ if (retval > kMaxSupportedSize) {
+ error(1, 0, "The key has unexpectedly long security context than %d", 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..3616831
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -0,0 +1,35 @@
+/*
+ * 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 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/Android.bp b/liblog/Android.bp
index 1d4a0a0..da475cb 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -46,6 +46,8 @@
vendor_available: true,
recovery_available: true,
export_include_dirs: ["include"],
+ system_shared_libs: [],
+ stl: "none",
target: {
windows: {
enabled: true,
@@ -99,6 +101,11 @@
header_libs: ["liblog_headers"],
export_header_lib_headers: ["liblog_headers"],
+ stubs: {
+ symbol_file: "liblog.map.txt",
+ versions: ["10000"],
+ },
+
cflags: [
"-Werror",
// This is what we want to do:
@@ -132,6 +139,5 @@
llndk_library {
name: "liblog",
symbol_file: "liblog.map.txt",
- unversioned: true,
export_include_dirs: ["include_vndk"],
}
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 191ef1b..ce4c53c 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -19,12 +19,12 @@
android_logger_get_log_readable_size; # vndk
android_logger_get_log_version; # vndk
android_logger_get_log_size; # vndk
- android_logger_list_alloc; # vndk
- android_logger_list_alloc_time; # vndk
- android_logger_list_free; # vndk
+ android_logger_list_alloc; # apex vndk
+ android_logger_list_alloc_time; # apex vndk
+ android_logger_list_free; # apex vndk
android_logger_list_open; # vndk
- android_logger_list_read; # vndk
- android_logger_open; # vndk
+ android_logger_list_read; # apex vndk
+ android_logger_open; # apex vndk
android_logger_set_log_size; # vndk
};
@@ -33,18 +33,18 @@
android_logger_get_prune_list; # vndk
android_logger_set_prune_list; # vndk
android_logger_get_statistics; # vndk
- __android_log_error_write; # vndk
+ __android_log_error_write; # apex vndk
__android_log_is_loggable;
- create_android_logger; #vndk
- android_log_destroy; #vndk
- android_log_write_list_begin; #vndk
- android_log_write_list_end; #vndk
- android_log_write_int32; #vndk
- android_log_write_int64; #vndk
- android_log_write_string8; #vndk
- android_log_write_string8_len; #vndk
- android_log_write_float32; #vndk
- android_log_write_list; #vndk
+ create_android_logger; # apex vndk
+ android_log_destroy; # apex vndk
+ android_log_write_list_begin; # apex vndk
+ android_log_write_list_end; # apex vndk
+ android_log_write_int32; # apex vndk
+ android_log_write_int64; # apex vndk
+ android_log_write_string8; # apex vndk
+ android_log_write_string8_len; # apex vndk
+ android_log_write_float32; # apex vndk
+ android_log_write_list; # apex vndk
};
@@ -56,19 +56,19 @@
LIBLOG_Q {
global:
+ __android_log_bswrite; # apex
+ __android_log_btwrite; # apex
+ __android_log_bwrite; # apex
+ __android_log_close; # apex
+ __android_log_security; # apex
android_log_reset; #vndk
android_log_parser_reset; #vndk
};
LIBLOG_PRIVATE {
global:
- __android_log_bswrite;
- __android_log_btwrite;
- __android_log_bwrite;
- __android_log_close;
__android_log_pmsg_file_read;
__android_log_pmsg_file_write;
- __android_log_security;
__android_log_security_bswrite;
__android_logger_get_buffer_size;
__android_logger_property_get_bool;
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 2f0af4a..b7ba782 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -100,148 +100,29 @@
return -EBADF;
}
-/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
+// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
+// O_CLOEXEC is always set.
+static int socket_local_client(const std::string& name, int type) {
+ sockaddr_un addr = {.sun_family = AF_LOCAL};
-#if defined(_WIN32)
-
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId, int type) {
- errno = ENOSYS;
- return -ENOSYS;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
-/* Private copy of ../libcutils/socket_local.h prevent library loops */
-#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
-#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
-/* End of ../libcutils/socket_local.h */
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-LIBLOG_WEAK int socket_make_sockaddr_un(const char* name, int namespaceId,
- struct sockaddr_un* p_addr, socklen_t* alen) {
- memset(p_addr, 0, sizeof(*p_addr));
- size_t namelen;
-
- switch (namespaceId) {
- case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
-#if defined(__linux__)
- namelen = strlen(name);
-
- /* Test with length +1 for the *initial* '\0'. */
- if ((namelen + 1) > sizeof(p_addr->sun_path)) {
- goto error;
- }
-
- /*
- * Note: The path in this case is *not* supposed to be
- * '\0'-terminated. ("man 7 unix" for the gory details.)
- */
-
- p_addr->sun_path[0] = 0;
- memcpy(p_addr->sun_path + 1, name, namelen);
-#else
- /* this OS doesn't have the Linux abstract namespace */
-
- namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
-#endif
- break;
-
- case ANDROID_SOCKET_NAMESPACE_RESERVED:
- namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
- break;
-
- case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
- namelen = strlen(name);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, name);
- break;
-
- default:
- /* invalid namespace id */
- return -1;
+ std::string path = "/dev/socket/" + name;
+ if (path.size() + 1 > sizeof(addr.sun_path)) {
+ return -1;
}
+ strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
- p_addr->sun_family = AF_LOCAL;
- *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
- return 0;
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name" on fd
- * returns same fd or -1 on error.
- * fd is not closed on error. that's your job.
- *
- * Used by AndroidSocketImpl
- */
-LIBLOG_WEAK int socket_local_client_connect(int fd, const char* name, int namespaceId,
- int type __unused) {
- struct sockaddr_un addr;
- socklen_t alen;
- int err;
-
- err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
- if (err < 0) {
- goto error;
- }
-
- if (connect(fd, (struct sockaddr*)&addr, alen) < 0) {
- goto error;
- }
-
- return fd;
-
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name"
- * returns fd or -1 on error
- */
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId, int type) {
- int s;
-
- s = socket(AF_LOCAL, type, 0);
- if (s < 0) return -1;
-
- if (0 > socket_local_client_connect(s, name, namespaceId, type)) {
- close(s);
+ int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
+ if (fd == 0) {
return -1;
}
- return s;
-}
+ if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+ close(fd);
+ return -1;
+ }
-#endif /* !_WIN32 */
-/* End of ../libcutils/socket_local_client.c */
+ return fd;
+}
/* worker for sending the command to the logger */
static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
@@ -250,7 +131,7 @@
size_t len;
char* cp;
int errno_save = 0;
- int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ int sock = socket_local_client("logd", SOCK_STREAM);
if (sock < 0) {
return sock;
}
@@ -460,10 +341,10 @@
return sock;
}
- sock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+ sock = socket_local_client("logdr", SOCK_SEQPACKET);
if (sock == 0) {
/* Guarantee not file descriptor zero */
- int newsock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+ int newsock = socket_local_client("logdr", SOCK_SEQPACKET);
close(sock);
sock = newsock;
}
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
index 3aa6841..ece0550 100644
--- a/liblog/logger_name.cpp
+++ b/liblog/logger_name.cpp
@@ -50,8 +50,9 @@
unsigned int ret;
if (!logName) {
- return static_cast<log_id_t>(0xFFFFFFFF);
+ return static_cast<log_id_t>(LOG_ID_MAX);
}
+
b = strrchr(logName, '/');
if (!b) {
b = logName;
@@ -65,5 +66,6 @@
return static_cast<log_id_t>(ret);
}
}
- return static_cast<log_id_t>(0xFFFFFFFF); /* should never happen */
+
+ return static_cast<log_id_t>(LOG_ID_MAX);
}
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/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 4ab2acb..50755ce 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -31,6 +31,7 @@
shared_libs: [
"libm",
"libbase",
+ "libcutils",
],
static_libs: ["liblog"],
srcs: ["liblog_benchmark.cpp"],
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 6d78ed6..3534eb8 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -23,7 +23,7 @@
#ifdef __ANDROID__
#ifndef NO_PSTORE
FILE* fp;
- ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
+ ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "ae")));
static const char message[] = "libc.__pstore_append\n";
ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
int fflushReturn = fflush(fp);
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index c8ac321..7d11ccf 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -172,7 +172,7 @@
* Measure the time it takes to submit the android logging data to pstore
*/
static void BM_pmsg_short(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -248,7 +248,7 @@
* best case aligned single block.
*/
static void BM_pmsg_short_aligned(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -323,7 +323,7 @@
* best case aligned single block.
*/
static void BM_pmsg_short_unaligned1(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -398,7 +398,7 @@
* best case aligned single block.
*/
static void BM_pmsg_long_aligned(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -471,7 +471,7 @@
* best case aligned single block.
*/
static void BM_pmsg_long_unaligned1(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -949,8 +949,8 @@
// Must be functionally identical to liblog internal __send_log_msg.
static void send_to_control(char* buf, size_t len) {
- int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM);
+ int sock =
+ socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
if (sock < 0) return;
size_t writeLen = strlen(buf) + 1;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 83f0fa9..1f87b3e 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -93,7 +93,7 @@
static std::string popenToString(const std::string& command) {
std::string ret;
- FILE* fp = popen(command.c_str(), "r");
+ FILE* fp = popen(command.c_str(), "re");
if (fp) {
if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
pclose(fp);
@@ -645,7 +645,7 @@
char buffer[512];
snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid);
- FILE* fp = fopen(buffer, "r");
+ FILE* fp = fopen(buffer, "re");
if (!fp) {
return;
}
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
index f202c67..fa1255e 100644
--- a/liblog/tests/log_radio_test.cpp
+++ b/liblog/tests/log_radio_test.cpp
@@ -91,7 +91,7 @@
"logcat -b radio --pid=%u -d -s"
" TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
(unsigned)getpid());
- FILE* fp = popen(buf.c_str(), "r");
+ FILE* fp = popen(buf.c_str(), "re");
int count = 0;
int count_false = 0;
if (fp) {
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
index 0656c0b..13f026d 100644
--- a/liblog/tests/log_system_test.cpp
+++ b/liblog/tests/log_system_test.cpp
@@ -91,7 +91,7 @@
"logcat -b system --pid=%u -d -s"
" TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
(unsigned)getpid());
- FILE* fp = popen(buf.c_str(), "r");
+ FILE* fp = popen(buf.c_str(), "re");
int count = 0;
int count_false = 0;
if (fp) {
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo.cpp b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
index 439cf68..9fb22a1 100644
--- a/libmeminfo/libdmabufinfo/dmabufinfo.cpp
+++ b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
@@ -150,18 +150,14 @@
auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
[&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
if (buf != dmabufs->end()) {
- if (buf->name() == "" || buf->name() == "<unknown>")
- buf->SetName(name);
- if (buf->exporter() == "" || buf->exporter() == "<unknown>")
- buf->SetExporter(exporter);
- if (buf->count() == 0)
- buf->SetCount(count);
+ if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
+ if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
+ if (buf->count() == 0) buf->SetCount(count);
buf->AddFdRef(pid);
continue;
}
- DmaBuffer& db =
- dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
+ DmaBuffer& db = dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
db.AddFdRef(pid);
}
@@ -182,29 +178,12 @@
// Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
// if it was already found. If it wasn't create a new one and append it to 'dmabufs'
auto account_dmabuf = [&](uint64_t start, uint64_t end, uint16_t /* flags */,
- uint64_t /* pgoff */, const char* name) {
+ uint64_t /* pgoff */, ino_t inode, const char* name) {
// no need to look into this mapping if it is not dmabuf
if (!FileIsDmaBuf(std::string(name))) {
return;
}
- // TODO (b/123532375) : Add inode number to the callback of ReadMapFileContent.
- //
- // Workaround: we know 'name' points to the name at the end of 'line'.
- // We use that to backtrack and pick up the inode number from the line as well.
- // start end flag pgoff mj:mn inode name
- // 00400000-00409000 r-xp 00000000 00:00 426998 /dmabuf (deleted)
- const char* p = name;
- p--;
- // skip spaces
- while (p != line && *p == ' ') {
- p--;
- }
- // walk backwards to the beginning of inode number
- while (p != line && isdigit(*p)) {
- p--;
- }
- uint64_t inode = strtoull(p, nullptr, 10);
auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
[&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
if (buf != dmabufs->end()) {
diff --git a/libmeminfo/libdmabufinfo/tools/Android.bp b/libmeminfo/libdmabufinfo/tools/Android.bp
index 0af3c48..224b68e 100644
--- a/libmeminfo/libdmabufinfo/tools/Android.bp
+++ b/libmeminfo/libdmabufinfo/tools/Android.bp
@@ -26,5 +26,5 @@
static_libs: [
"libdmabufinfo",
],
- soc_specific: true,
+ product_specific: true,
}
\ No newline at end of file
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index 069b6b3..934d65c 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -246,7 +246,7 @@
// parse and read /proc/<pid>/maps
std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
if (!::android::procinfo::ReadMapFile(
- maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+ maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
const char* name) {
maps_.emplace_back(Vma(start, end, pgoff, flags, name));
})) {
@@ -394,7 +394,7 @@
// If it has, we are looking for the vma stats
// 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
if (!::android::procinfo::ReadMapFileContent(
- line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+ line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
const char* name) {
vma.start = start;
vma.end = end;
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/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
index 21a684c..5e89254 100644
--- a/libmeminfo/tools/procrank.cpp
+++ b/libmeminfo/tools/procrank.cpp
@@ -14,11 +14,17 @@
* limitations under the License.
*/
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <linux/kernel-page-flags.h>
#include <linux/oom.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -29,14 +35,6 @@
#include <sstream>
#include <vector>
-#include <android-base/file.h>
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include <meminfo/procmeminfo.h>
-#include <meminfo/sysmeminfo.h>
-
using ::android::meminfo::MemUsage;
using ::android::meminfo::ProcMemInfo;
@@ -460,8 +458,16 @@
auto mark_swap_usage = [&](pid_t pid) -> bool {
ProcessRecord proc(pid, show_wss, pgflags, pgflags_mask);
if (!proc.valid()) {
- std::cerr << "Failed to create process record for: " << pid << std::endl;
- return false;
+ // Check to see if the process is still around, skip the process if the proc
+ // directory is inaccessible. It was most likely killed while creating the process
+ // record
+ std::string procdir = ::android::base::StringPrintf("/proc/%d", pid);
+ if (access(procdir.c_str(), F_OK | R_OK)) return true;
+
+ // Warn if we failed to gather process stats even while it is still alive.
+ // Return success here, so we continue to print stats for other processes.
+ std::cerr << "warning: failed to create process record for: " << pid << std::endl;
+ return true;
}
// Skip processes with no memory mappings
@@ -479,9 +485,9 @@
return true;
};
- // Get a list of all pids currently running in the system in
- // 1st pass through all processes. Mark each swap offset used by the process as we find them
- // for calculating proportional swap usage later.
+ // Get a list of all pids currently running in the system in 1st pass through all processes.
+ // Mark each swap offset used by the process as we find them for calculating proportional
+ // swap usage later.
if (!read_all_pids(&pids, mark_swap_usage)) {
std::cerr << "Failed to read all pids from the system" << std::endl;
exit(EXIT_FAILURE);
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 89837f7..7cae048 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -59,12 +59,19 @@
}
}
+// Sanitizers may consider certain memory inaccessible through certain pointers.
+// With MTE this will need to use unchecked instructions or disable tag checking globally.
+static uintptr_t ReadWordAtAddressUnsafe(uintptr_t word_ptr)
+ __attribute__((no_sanitize("address", "hwaddress"))) {
+ return *reinterpret_cast<uintptr_t*>(word_ptr);
+}
+
bool HeapWalker::WordContainsAllocationPtr(uintptr_t word_ptr, Range* range, AllocationInfo** info) {
walking_ptr_ = word_ptr;
// This access may segfault if the process under test has done something strange,
// for example mprotect(PROT_NONE) on a native heap page. If so, it will be
// caught and handled by mmaping a zero page over the faulting page.
- uintptr_t value = *reinterpret_cast<uintptr_t*>(word_ptr);
+ uintptr_t value = ReadWordAtAddressUnsafe(word_ptr);
walking_ptr_ = 0;
if (value >= valid_allocations_range_.begin && value < valid_allocations_range_.end) {
AllocationMap::iterator it = allocations_.find(Range{value, value + 1});
@@ -200,6 +207,6 @@
}
}
-ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+Allocator<ScopedSignalHandler::SignalFnMap>::unique_ptr ScopedSignalHandler::handler_map_;
} // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 9e3db08..f00bcca 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -52,7 +52,8 @@
allocation_bytes_(0),
roots_(allocator),
root_vals_(allocator),
- segv_handler_(),
+ sigsegv_handler_(allocator),
+ sigbus_handler_(allocator),
walking_ptr_(0),
walking_range_{0, 0},
segv_logged_(false),
@@ -62,10 +63,14 @@
valid_mappings_range_.end = 0;
valid_mappings_range_.begin = ~valid_allocations_range_.end;
- segv_handler_.install(
+ sigsegv_handler_.install(
SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
this->HandleSegFault(handler, signal, siginfo, uctx);
});
+ sigbus_handler_.install(
+ SIGBUS, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+ this->HandleSegFault(handler, signal, siginfo, uctx);
+ });
}
~HeapWalker() {}
@@ -106,7 +111,8 @@
allocator::vector<Range> roots_;
allocator::vector<uintptr_t> root_vals_;
- ScopedSignalHandler segv_handler_;
+ ScopedSignalHandler sigsegv_handler_;
+ ScopedSignalHandler sigbus_handler_;
volatile uintptr_t walking_ptr_;
Range walking_range_;
bool segv_logged_;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 3d7b8a8..299c320 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -217,6 +217,10 @@
return ret == 0;
}
+static bool is_sanitizer_mapping(const allocator::string& s) {
+ return s == "[anon:low shadow]" || s == "[anon:high shadow]" || has_prefix(s, "[anon:hwasan");
+}
+
bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
allocator::vector<Mapping>& heap_mappings,
allocator::vector<Mapping>& anon_mappings,
@@ -258,7 +262,8 @@
} else if (mapping_name.size() == 0) {
globals_mappings.emplace_back(*it);
} else if (has_prefix(mapping_name, "[anon:") &&
- mapping_name != "[anon:leak_detector_malloc]") {
+ mapping_name != "[anon:leak_detector_malloc]" &&
+ !is_sanitizer_mapping(mapping_name)) {
// TODO(ccross): it would be nice to treat named anonymous mappings as
// possible leaks, but naming something in a .bss or .data section makes
// it impossible to distinguish them from mmaped and then named mappings.
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 701ce16..8e1be4c 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <string.h>
+#include <sys/types.h>
#include <unistd.h>
#include <android-base/unique_fd.h>
@@ -30,7 +31,8 @@
struct ReadMapCallback {
ReadMapCallback(allocator::vector<Mapping>& mappings) : mappings_(mappings) {}
- void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) const {
+ void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t,
+ const char* name) const {
mappings_.emplace_back(start, end, flags & PROT_READ, flags & PROT_WRITE, flags & PROT_EXEC,
name);
}
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 9e08a8e..ef4473f 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -24,6 +24,7 @@
#include "android-base/macros.h"
+#include "Allocator.h"
#include "log.h"
namespace android {
@@ -32,17 +33,29 @@
public:
using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
- explicit ScopedSignalHandler() : signal_(-1) {}
+ explicit ScopedSignalHandler(Allocator<ScopedSignalHandler> allocator) : signal_(-1) {
+ if (handler_map_ == nullptr) {
+ Allocator<SignalFnMap> map_allocator = allocator;
+ handler_map_ = map_allocator.make_unique(allocator);
+ }
+ }
~ScopedSignalHandler() { reset(); }
template <class F>
void install(int signal, F&& f) {
if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
- handler_ = SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
+ if (handler_map_->find(signal) != handler_map_->end()) {
+ MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed for %d", signal);
+ }
+
+ (*handler_map_)[signal] =
+ SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
struct sigaction act {};
- act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
+ act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
+ ((*handler_map_)[signal])(signal, si, uctx);
+ };
act.sa_flags = SA_SIGINFO;
int ret = sigaction(signal, &act, &old_act_);
@@ -59,19 +72,22 @@
if (ret < 0) {
MEM_ALOGE("failed to uninstall segfault handler");
}
- handler_ = SignalFn{};
+
+ handler_map_->erase(signal_);
+ if (handler_map_->empty()) {
+ handler_map_.reset();
+ }
signal_ = -1;
}
}
private:
using SignalFn = std::function<void(int, siginfo_t*, void*)>;
+ using SignalFnMap = allocator::unordered_map<int, SignalFn>;
DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
int signal_;
struct sigaction old_act_;
- // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
- // to be a static map of signals to handlers, but allocated with Allocator.
- static SignalFn handler_;
+ static Allocator<SignalFnMap>::unique_ptr handler_map_;
};
} // namespace android
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index c54570e..10d42e4 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,5 +1,18 @@
+cc_defaults {
+ name: "libnativebridge-defaults",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ cppflags: [
+ "-fvisibility=protected",
+ ],
+ header_libs: ["libnativebridge-headers"],
+ export_header_lib_headers: ["libnativebridge-headers"],
+}
+
cc_library_headers {
- name: "libnativebridge-dummy-headers",
+ name: "libnativebridge-headers",
host_supported: true,
export_include_dirs: ["include"],
@@ -7,6 +20,7 @@
cc_library {
name: "libnativebridge",
+ defaults: ["libnativebridge-defaults"],
host_supported: true,
srcs: ["native_bridge.cc"],
@@ -16,6 +30,8 @@
shared_libs: [
"liblog",
],
+ // TODO(jiyong): remove this line after aosp/885921 lands
+ export_include_dirs: ["include"],
target: {
android: {
@@ -30,16 +46,16 @@
symbol_file: "libnativebridge.map.txt",
versions: ["1"],
},
+}
- export_include_dirs: ["include"],
+// TODO(b/124250621): eliminate the need for this library
+cc_library {
+ name: "libnativebridge_lazy",
+ defaults: ["libnativebridge-defaults"],
- cflags: [
- "-Werror",
- "-Wall",
- ],
- cppflags: [
- "-fvisibility=protected",
- ],
+ host_supported: false,
+ srcs: ["native_bridge_lazy.cc"],
+ required: ["libnativebridge"],
}
subdirs = ["tests"]
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
index 6f0824b..daf87f4 100644
--- a/libnativebridge/OWNERS
+++ b/libnativebridge/OWNERS
@@ -1,2 +1,4 @@
dimitry@google.com
eaeltsin@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/libnativebridge/native_bridge_lazy.cc b/libnativebridge/native_bridge_lazy.cc
new file mode 100644
index 0000000..94c8084
--- /dev/null
+++ b/libnativebridge/native_bridge_lazy.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "nativebridge/native_bridge.h"
+#define LOG_TAG "nativebridge"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+ static void* handle = dlopen("libnativebridge.so", RTLD_NOW);
+ LOG_FATAL_IF(handle == nullptr, "Failed to load libnativebridge.so: %s", dlerror());
+ return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+ auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+ LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+ return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+} // namespace
+
+bool LoadNativeBridge(const char* native_bridge_library_filename,
+ const struct NativeBridgeRuntimeCallbacks* runtime_callbacks) {
+ static auto f = GET_FUNC_PTR(LoadNativeBridge);
+ return f(native_bridge_library_filename, runtime_callbacks);
+}
+
+bool NeedsNativeBridge(const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(NeedsNativeBridge);
+ return f(instruction_set);
+}
+
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(PreInitializeNativeBridge);
+ return f(app_data_dir, instruction_set);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(InitializeNativeBridge);
+ return f(env, instruction_set);
+}
+
+void UnloadNativeBridge() {
+ static auto f = GET_FUNC_PTR(UnloadNativeBridge);
+ return f();
+}
+
+bool NativeBridgeAvailable() {
+ static auto f = GET_FUNC_PTR(NativeBridgeAvailable);
+ return f();
+}
+
+bool NativeBridgeInitialized() {
+ static auto f = GET_FUNC_PTR(NativeBridgeInitialized);
+ return f();
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLoadLibrary);
+ return f(libpath, flag);
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetTrampoline);
+ return f(handle, name, shorty, len);
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+ static auto f = GET_FUNC_PTR(NativeBridgeIsSupported);
+ return f(libpath);
+}
+
+uint32_t NativeBridgeGetVersion() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetVersion);
+ return f();
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetSignalHandler);
+ return f(signal);
+}
+
+bool NativeBridgeError() {
+ static auto f = GET_FUNC_PTR(NativeBridgeError);
+ return f();
+}
+
+bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename) {
+ static auto f = GET_FUNC_PTR(NativeBridgeNameAcceptable);
+ return f(native_bridge_library_filename);
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+ static auto f = GET_FUNC_PTR(NativeBridgeUnloadLibrary);
+ return f(handle);
+}
+
+const char* NativeBridgeGetError() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetError);
+ return f();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+ static auto f = GET_FUNC_PTR(NativeBridgeIsPathSupported);
+ return f(path);
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ static auto f = GET_FUNC_PTR(NativeBridgeInitAnonymousNamespace);
+ return f(public_ns_sonames, anon_ns_library_path);
+}
+
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+ const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns) {
+ static auto f = GET_FUNC_PTR(NativeBridgeCreateNamespace);
+ return f(name, ld_library_path, default_library_path, type, permitted_when_isolated_path,
+ parent_ns);
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to,
+ const char* shared_libs_sonames) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLinkNamespaces);
+ return f(from, to, shared_libs_sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLoadLibraryExt);
+ return f(libpath, flag, ns);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetVendorNamespace);
+ return f();
+}
+
+#undef GET_FUNC_PTR
+
+} // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 744a4a8..2bb8467 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,7 +23,7 @@
"-Wextra",
"-Werror",
],
- header_libs: ["libnativebridge-dummy-headers"],
+ header_libs: ["libnativebridge-headers"],
cppflags: ["-fvisibility=protected"],
}
@@ -46,9 +46,8 @@
}
// Build the unit tests.
-cc_test {
- name: "libnativebridge-tests",
- host_supported: true,
+cc_defaults {
+ name: "libnativebridge-tests-defaults",
test_per_src: true,
cflags: [
@@ -81,12 +80,24 @@
shared_libs: [
"liblog",
- "libnativebridge",
"libnativebridge-dummy",
],
header_libs: ["libbase_headers"],
}
+cc_test {
+ name: "libnativebridge-tests",
+ defaults: ["libnativebridge-tests-defaults"],
+ host_supported: true,
+ shared_libs: ["libnativebridge"],
+}
+
+cc_test {
+ name: "libnativebridge-lazy-tests",
+ defaults: ["libnativebridge-tests-defaults"],
+ shared_libs: ["libnativebridge_lazy"],
+}
+
// Build the test for the C API.
cc_test {
name: "libnativebridge-api-tests",
@@ -95,5 +106,5 @@
srcs: [
"NativeBridgeApi.c",
],
- header_libs: ["libnativebridge-dummy-headers"],
+ header_libs: ["libnativebridge-headers"],
}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 1ec21e9..66cb49f 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -1,16 +1,7 @@
// Shared library for target
// ========================================================
-cc_library {
- name: "libnativeloader",
- host_supported: true,
- srcs: ["native_loader.cpp"],
- shared_libs: [
- "libnativehelper",
- "liblog",
- "libcutils",
- "libnativebridge",
- "libbase",
- ],
+cc_defaults {
+ name: "libnativeloader-defaults",
cflags: [
"-Werror",
"-Wall",
@@ -18,7 +9,28 @@
cppflags: [
"-fvisibility=hidden",
],
- export_include_dirs: ["include"],
+ header_libs: ["libnativeloader-headers"],
+ export_header_lib_headers: ["libnativeloader-headers"],
+}
+
+cc_library {
+ name: "libnativeloader",
+ defaults: ["libnativeloader-defaults"],
+ host_supported: true,
+ srcs: ["native_loader.cpp"],
+ shared_libs: [
+ "libnativehelper",
+ "liblog",
+ "libnativebridge",
+ "libbase",
+ ],
+ target: {
+ android: {
+ shared_libs: [
+ "libdl_android",
+ ],
+ },
+ },
required: [
"llndk.libraries.txt",
"vndksp.libraries.txt",
@@ -29,9 +41,25 @@
},
}
+// TODO(b/124250621) eliminate the need for this library
+cc_library {
+ name: "libnativeloader_lazy",
+ defaults: ["libnativeloader-defaults"],
+ host_supported: false,
+ srcs: ["native_loader_lazy.cpp"],
+ required: ["libnativeloader"],
+}
+
+cc_library_headers {
+ name: "libnativeloader-headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+// TODO(jiyong) Remove this when its use in the internal master is
+// switched to libnativeloader-headers
cc_library_headers {
name: "libnativeloader-dummy-headers",
-
host_supported: true,
export_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 5394d7e..5cc0857 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -21,7 +21,6 @@
#ifdef __ANDROID__
#define LOG_TAG "libnativeloader"
#include "nativeloader/dlext_namespaces.h"
-#include "cutils/properties.h"
#include "log/log.h"
#endif
#include <dirent.h>
@@ -32,6 +31,7 @@
#include <list>
#include <memory>
#include <mutex>
+#include <regex>
#include <string>
#include <vector>
@@ -116,6 +116,8 @@
static constexpr const char* kVndkNamespaceName = "vndk";
+static constexpr const char* kDefaultNamespaceName = "default";
+static constexpr const char* kPlatformNamespaceName = "platform";
static constexpr const char* kRuntimeNamespaceName = "runtime";
// classloader-namespace is a linker namespace that is created for the loaded
@@ -137,10 +139,32 @@
static constexpr const char* kApexPath = "/apex/";
+#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() {
- char debuggable[PROP_VALUE_MAX];
- property_get("ro.debuggable", debuggable, "0");
- return std::string(debuggable) == "1";
+ bool debuggable = false;
+#ifdef __BIONIC__
+ debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+#endif
+ return debuggable;
}
static std::string vndk_version_str() {
@@ -170,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.
@@ -179,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.
@@ -225,31 +251,51 @@
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);
+ if (vndk_ns == nullptr) {
+ ALOGW("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.
@@ -265,8 +311,19 @@
NativeLoaderNamespace native_loader_ns;
if (!is_native_bridge) {
- android_namespace_t* android_parent_ns =
- parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
+ android_namespace_t* android_parent_ns;
+ if (parent_ns != nullptr) {
+ android_parent_ns = parent_ns->get_android_ns();
+ } else {
+ // Fall back to the platform namespace if no parent is found. It is
+ // called "default" for binaries in /system and "platform" for those in
+ // the Runtime APEX. Try "platform" first since "default" always exists.
+ android_parent_ns = android_get_exported_namespace(kPlatformNamespaceName);
+ if (android_parent_ns == nullptr) {
+ android_parent_ns = android_get_exported_namespace(kDefaultNamespaceName);
+ }
+ }
+
android_namespace_t* ns = android_create_namespace(namespace_name,
nullptr,
library_path.c_str(),
@@ -315,8 +372,16 @@
native_loader_ns = NativeLoaderNamespace(ns);
} else {
- native_bridge_namespace_t* native_bridge_parent_namespace =
- parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
+ native_bridge_namespace_t* native_bridge_parent_namespace;
+ if (parent_ns != nullptr) {
+ native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
+ } else {
+ native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
+ if (native_bridge_parent_namespace == nullptr) {
+ native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
+ }
+ }
+
native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
nullptr,
library_path.c_str(),
@@ -408,6 +473,14 @@
}
}
+ // Remove the public libs in the runtime namespace.
+ // These libs are listed in public.android.txt, but we don't want the rest of android
+ // in default namespace to dlopen the libs.
+ // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+ // Unfortunately, it does not have stable C symbols, and default namespace should only use
+ // stable symbols in libandroidicu.so. http://b/120786417
+ removePublicLibsIfExistsInRuntimeApex(sonames);
+
// android_init_namespaces() expects all the public libraries
// to be loaded so that they can be found by soname alone.
//
@@ -502,6 +575,27 @@
}
}
+ /**
+ * Remove the public libs in runtime namespace
+ */
+ void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+ for (const std::string& lib_name : kRuntimePublicLibraries) {
+ std::string path(kRuntimeApexLibPath);
+ path.append("/").append(lib_name);
+
+ struct stat s;
+ // Do nothing if the path in /apex does not exist.
+ // Runtime APEX must be mounted since libnativeloader is in the same APEX
+ if (stat(path.c_str(), &s) != 0) {
+ continue;
+ }
+
+ auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+ if (it != sonames.end()) {
+ sonames.erase(it);
+ }
+ }
+ }
bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
const std::function<bool(const std::string& /* soname */,
@@ -603,6 +697,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_;
@@ -633,31 +749,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;
}
@@ -722,8 +827,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
new file mode 100644
index 0000000..2eb1203
--- /dev/null
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "nativeloader/native_loader.h"
+#define LOG_TAG "nativeloader"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+ static void* handle = dlopen("libnativeloader.so", RTLD_NOW);
+ LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror());
+ return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+ auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+ LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+ return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+} // namespace
+
+void InitializeNativeLoader() {
+ static auto f = GET_FUNC_PTR(InitializeNativeLoader);
+ return f();
+}
+
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+ 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, dex_path, library_path,
+ permitted_path);
+}
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+ jobject class_loader, const char* caller_location, jstring library_path,
+ bool* needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(OpenNativeLibrary);
+ return f(env, target_sdk_version, path, class_loader, caller_location, library_path,
+ needs_native_bridge, error_msg);
+}
+
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(CloseNativeLibrary);
+ return f(handle, needs_native_bridge, error_msg);
+}
+
+void NativeLoaderFreeErrorMessage(char* msg) {
+ static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage);
+ return f(msg);
+}
+
+struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader);
+ return f(env, class_loader);
+}
+
+struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader);
+ return f(env, class_loader);
+}
+
+void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path,
+ bool* needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace);
+ return f(ns, path, needs_native_bridge, error_msg);
+}
+
+void ResetNativeLoader() {
+ static auto f = GET_FUNC_PTR(ResetNativeLoader);
+ return f();
+}
+
+#undef GET_FUNC_PTR
+
+} // namespace android
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index 1464e39..4d5c53d 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -78,5 +78,5 @@
srcs: [
"api_test.c",
],
- header_libs: ["libnativeloader-dummy-headers"],
+ header_libs: ["libnativeloader-headers"],
}
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index 8bcc1e2..3cb6b9a 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -54,6 +54,7 @@
gid_list gids;
void *private_data;
bool profileable_from_shell;
+ long version_code;
};
/**
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 4ce2363..edc533c 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -239,6 +239,15 @@
pkg_info->profileable_from_shell = (bool)tmp;
}
+ cur = strsep(&next, " \t\r\n");
+ if (cur) {
+ tmp = strtoul(cur, &endptr, 10);
+ if (*endptr != '\0') {
+ errmsg = "Could not convert field \"versionCode\" to integer value";
+ goto err;
+ }
+ pkg_info->version_code = tmp;
+ }
rc = callback(pkg_info, userdata);
if (rc == false) {
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 7f39e9b..4217a89 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -107,7 +107,7 @@
// inline ARM implementations
inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
-inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
GGLfixed result, t;
if (__builtin_constant_p(shift)) {
asm("smull %[lo], %[hi], %[x], %[y] \n"
@@ -130,7 +130,8 @@
}
inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
-inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a,
+ int shift) {
GGLfixed result, t;
if (__builtin_constant_p(shift)) {
asm("smull %[lo], %[hi], %[x], %[y] \n"
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index d97f09f..78a02e5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -31,6 +31,7 @@
},
shared_libs: [
"libbase",
+ "libcgrouprc",
"libjsoncpp",
],
// for cutils/android_filesystem_config.h
@@ -45,5 +46,6 @@
cflags: [
"-Wall",
"-Werror",
+ "-Wexit-time-destructors",
],
}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index cd8ef94..6cd6b6e 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -44,210 +44,42 @@
using android::base::StringPrintf;
using android::base::unique_fd;
-static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
-
static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
-static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
- const std::string& gid) {
- if (mode == 0) {
- mode = 0755;
- }
-
- if (mkdir(path.c_str(), mode) != 0) {
- /* chmod in case the directory already exists */
- if (errno == EEXIST) {
- if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
- // /acct is a special case when the directory already exists
- // TODO: check if file mode is already what we want instead of using EROFS
- if (errno != EROFS) {
- PLOG(ERROR) << "fchmodat() failed for " << path;
- return false;
- }
- }
- } else {
- PLOG(ERROR) << "mkdir() failed for " << path;
- return false;
- }
- }
-
- if (uid.empty()) {
- return true;
- }
-
- passwd* uid_pwd = getpwnam(uid.c_str());
- if (!uid_pwd) {
- PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
- return false;
- }
-
- uid_t pw_uid = uid_pwd->pw_uid;
- gid_t gr_gid = -1;
- if (!gid.empty()) {
- group* gid_pwd = getgrnam(gid.c_str());
- if (!gid_pwd) {
- PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
- return false;
- }
- gr_gid = gid_pwd->gr_gid;
- }
-
- if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
- PLOG(ERROR) << "lchown() failed for " << path;
- return false;
- }
-
- /* chown may have cleared S_ISUID and S_ISGID, chmod again */
- if (mode & (S_ISUID | S_ISGID)) {
- if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
- PLOG(ERROR) << "fchmodat() failed for " << path;
- return false;
- }
- }
-
- return true;
+uint32_t CgroupController::version() const {
+ CHECK(HasValue());
+ return ACgroupController_getVersion(controller_);
}
-static bool ReadDescriptors(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;
- return false;
- }
-
- Json::Reader reader;
- Json::Value root;
- if (!reader.parse(json_doc, root)) {
- LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
- return false;
- }
-
- 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(),
- std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
- cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString())));
- }
-
- Json::Value cgroups2 = root["Cgroups2"];
- descriptors->emplace(std::make_pair(
- 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())));
-
- return true;
+const char* CgroupController::name() const {
+ CHECK(HasValue());
+ return ACgroupController_getName(controller_);
}
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
-static bool SetupCgroup(const CgroupDescriptor& descriptor) {
- const CgroupController* controller = descriptor.controller();
-
- // 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";
- return false;
- }
-
- int result;
- if (controller->version() == 2) {
- result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
- nullptr);
- } else {
- // Unfortunately historically cpuset controller was mounted using a mount command
- // different from all other controllers. This results in controller attributes not
- // to be prepended with controller name. For example this way instead of
- // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
- // the system currently expects.
- if (!strcmp(controller->name(), "cpuset")) {
- // mount cpuset none /dev/cpuset nodev noexec nosuid
- result = mount("none", controller->path(), controller->name(),
- MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
- } else {
- // mount cgroup none <path> nodev noexec nosuid <controller>
- result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
- controller->name());
- }
- }
-
- if (result < 0) {
- PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
- return false;
- }
-
- return true;
+const char* CgroupController::path() const {
+ CHECK(HasValue());
+ return ACgroupController_getPath(controller_);
}
-#else
-
-// Stubs for non-Android targets.
-static bool SetupCgroup(const CgroupDescriptor&) {
- return false;
+bool CgroupController::HasValue() const {
+ return controller_ != nullptr;
}
-#endif
+std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
+ std::string tasks_path = path();
-static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
- std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CgroupMap::CGROUPS_RC_FILE);
- unique_fd fd(TEMP_FAILURE_RETRY(open(cgroup_rc_path.c_str(),
- O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
- S_IRUSR | S_IRGRP | S_IROTH)));
- if (fd < 0) {
- PLOG(ERROR) << "open() failed for " << cgroup_rc_path;
- return false;
+ if (!rel_path.empty()) {
+ tasks_path += "/" + rel_path;
}
-
- CgroupFile fl;
- fl.version_ = CgroupFile::FILE_CURR_VERSION;
- fl.controller_count_ = descriptors.size();
- int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
- if (ret < 0) {
- PLOG(ERROR) << "write() failed for " << cgroup_rc_path;
- return false;
- }
-
- for (const auto& [name, descriptor] : descriptors) {
- ret = TEMP_FAILURE_RETRY(write(fd, descriptor.controller(), sizeof(CgroupController)));
- if (ret < 0) {
- PLOG(ERROR) << "write() failed for " << cgroup_rc_path;
- return false;
- }
- }
-
- return true;
+ return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
}
-CgroupController::CgroupController(uint32_t version, const std::string& name,
- const std::string& path) {
- version_ = version;
- strncpy(name_, name.c_str(), sizeof(name_) - 1);
- name_[sizeof(name_) - 1] = '\0';
- strncpy(path_, path.c_str(), sizeof(path_) - 1);
- path_[sizeof(path_) - 1] = '\0';
-}
-
-std::string CgroupController::GetTasksFilePath(const std::string& path) const {
- std::string tasks_path = path_;
-
- if (!path.empty()) {
- tasks_path += "/" + path;
- }
- return (version_ == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
-}
-
-std::string CgroupController::GetProcsFilePath(const std::string& path, uid_t uid,
+std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
pid_t pid) const {
- std::string proc_path(path_);
- proc_path.append("/").append(path);
+ std::string proc_path(path());
+ proc_path.append("/").append(rel_path);
proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
@@ -258,7 +90,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;
}
@@ -268,7 +100,7 @@
return true;
}
- std::string cg_tag = StringPrintf(":%s:", name_);
+ std::string cg_tag = StringPrintf(":%s:", name());
size_t start_pos = content.find(cg_tag);
if (start_pos == std::string::npos) {
return false;
@@ -285,137 +117,60 @@
return true;
}
-CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
- const std::string& path, mode_t mode, const std::string& uid,
- const std::string& gid)
- : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
-
-CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
+CgroupMap::CgroupMap() {
if (!LoadRcFile()) {
- PLOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
- }
-}
-
-CgroupMap::~CgroupMap() {
- if (cg_file_data_) {
- munmap(cg_file_data_, cg_file_size_);
- cg_file_data_ = nullptr;
- cg_file_size_ = 0;
+ LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
}
}
CgroupMap& CgroupMap::GetInstance() {
- static CgroupMap instance;
- return instance;
+ // Deliberately leak this object to avoid a race between destruction on
+ // process exit and concurrent access from another thread.
+ static auto* instance = new CgroupMap;
+ return *instance;
}
bool CgroupMap::LoadRcFile() {
- struct stat sb;
-
- if (cg_file_data_) {
- // Data already initialized
- return true;
+ if (!loaded_) {
+ loaded_ = (ACgroupFile_getVersion() != 0);
}
-
- std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CGROUPS_RC_FILE);
- unique_fd fd(TEMP_FAILURE_RETRY(open(cgroup_rc_path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- PLOG(ERROR) << "open() failed for " << cgroup_rc_path;
- return false;
- }
-
- if (fstat(fd, &sb) < 0) {
- PLOG(ERROR) << "fstat() failed for " << cgroup_rc_path;
- return false;
- }
-
- cg_file_size_ = sb.st_size;
- if (cg_file_size_ < sizeof(CgroupFile)) {
- PLOG(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) {
- 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";
- return false;
- }
-
- return true;
+ return loaded_;
}
-void CgroupMap::Print() {
- LOG(INFO) << "File version = " << cg_file_data_->version_;
- LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
+void CgroupMap::Print() const {
+ if (!loaded_) {
+ LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
+ return;
+ }
+ LOG(INFO) << "File version = " << ACgroupFile_getVersion();
+ LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
LOG(INFO) << "Mounted cgroups:";
- CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
- for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
- LOG(INFO) << "\t" << controller->name() << " ver " << controller->version() << " path "
- << controller->path();
+
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+ << ACgroupController_getVersion(controller) << " path "
+ << ACgroupController_getPath(controller);
}
}
-bool CgroupMap::SetupCgroups() {
- std::map<std::string, CgroupDescriptor> descriptors;
-
- // load cgroups.json file
- if (!ReadDescriptors(&descriptors)) {
- PLOG(ERROR) << "Failed to load cgroup description file";
- return false;
+CgroupController CgroupMap::FindController(const std::string& name) const {
+ if (!loaded_) {
+ LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
+ return CgroupController(nullptr);
}
- // setup cgroups
- for (const auto& [name, descriptor] : descriptors) {
- if (!SetupCgroup(descriptor)) {
- // issue a warning and proceed with the next cgroup
- // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
- LOG(WARNING) << "Failed to setup " << name << " cgroup";
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ if (name == ACgroupController_getName(controller)) {
+ return CgroupController(controller);
}
}
- // 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";
- return false;
- }
-
- // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
- // process memory. This optimizes performance, memory usage
- // and limits infrormation shared with unprivileged processes
- // to the minimum subset of information from cgroups.json
- if (!WriteRcFile(descriptors)) {
- LOG(ERROR) << "Failed to write " << CGROUPS_RC_FILE << " file";
- return false;
- }
-
- 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";
- return false;
- }
-
- return true;
-}
-
-const CgroupController* CgroupMap::FindController(const std::string& name) const {
- if (!cg_file_data_) {
- return nullptr;
- }
-
- // skip the file header to get to the first controller
- CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
- for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
- if (name == controller->name()) {
- return controller;
- }
- }
-
- return nullptr;
+ return CgroupController(nullptr);
}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index ba2caf7..d765e60 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -20,77 +20,43 @@
#include <sys/types.h>
#include <map>
+#include <memory>
#include <mutex>
#include <string>
+#include <vector>
-// Minimal controller description to be mmapped into process address space
+#include <android/cgrouprc.h>
+
+// Convenient wrapper of an ACgroupController pointer.
class CgroupController {
public:
- CgroupController() {}
- CgroupController(uint32_t version, const std::string& name, const std::string& path);
+ // Does not own controller
+ explicit CgroupController(const ACgroupController* controller) : controller_(controller) {}
- uint32_t version() const { return version_; }
- const char* name() const { return name_; }
- const char* path() const { return path_; }
+ uint32_t version() const;
+ const char* name() const;
+ const char* path() const;
+
+ bool HasValue() const;
std::string GetTasksFilePath(const std::string& path) const;
std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;
bool GetTaskGroup(int tid, std::string* group) const;
-
private:
- static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
- static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
-
- uint32_t version_;
- char name_[CGROUP_NAME_BUF_SZ];
- char path_[CGROUP_PATH_BUF_SZ];
-};
-
-// Complete controller description for mounting cgroups
-class CgroupDescriptor {
- public:
- CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
- mode_t mode, const std::string& uid, const std::string& gid);
-
- const CgroupController* controller() const { return &controller_; }
- mode_t mode() const { return mode_; }
- std::string uid() const { return uid_; }
- std::string gid() const { return gid_; }
-
- private:
- CgroupController controller_;
- mode_t mode_;
- std::string uid_;
- std::string gid_;
-};
-
-struct CgroupFile {
- static constexpr uint32_t FILE_VERSION_1 = 1;
- static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
-
- uint32_t version_;
- uint32_t controller_count_;
- CgroupController controllers_[];
+ const ACgroupController* controller_ = nullptr;
};
class CgroupMap {
public:
- static constexpr const char* CGROUPS_RC_FILE = "cgroup.rc";
-
// Selinux policy ensures only init process can successfully use this function
static bool SetupCgroups();
static CgroupMap& GetInstance();
-
- const CgroupController* FindController(const std::string& name) const;
+ CgroupController FindController(const std::string& name) const;
private:
- struct CgroupFile* cg_file_data_;
- size_t cg_file_size_;
-
+ bool loaded_ = false;
CgroupMap();
- ~CgroupMap();
-
bool LoadRcFile();
- void Print();
+ void Print() const;
};
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
new file mode 100644
index 0000000..6848620
--- /dev/null
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -0,0 +1,61 @@
+// 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.
+
+cc_library {
+ name: "libcgrouprc",
+ host_supported: true,
+ recovery_available: true,
+ // Do not ever mark this as vendor_available; otherwise, vendor modules
+ // that links to the static library will behave unexpectedly. All on-device
+ // modules should use libprocessgroup which links to the LL-NDK library
+ // defined below. The static library is built for tests.
+ vendor_available: false,
+ srcs: [
+ "cgroup_controller.cpp",
+ "cgroup_file.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ header_libs: [
+ "libprocessgroup_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ static_libs: [
+ "libcgrouprc_format",
+ ],
+ stubs: {
+ symbol_file: "libcgrouprc.map.txt",
+ versions: ["29"],
+ },
+ target: {
+ linux: {
+ version_script: "libcgrouprc.map.txt",
+ },
+ },
+}
+
+llndk_library {
+ name: "libcgrouprc",
+ symbol_file: "libcgrouprc.map.txt",
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
new file mode 100644
index 0000000..d064d31
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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/logging.h>
+#include <android/cgrouprc.h>
+
+#include "cgrouprc_internal.h"
+
+// All ACgroupController_* functions implicitly convert the pointer back
+// to the original CgroupController pointer before invoking the member functions.
+
+uint32_t ACgroupController_getVersion(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->version();
+}
+
+const char* ACgroupController_getName(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->name();
+}
+
+const char* ACgroupController_getPath(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->path();
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_file.cpp b/libprocessgroup/cgrouprc/cgroup_file.cpp
new file mode 100644
index 0000000..e26d841
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_file.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 <sys/mman.h>
+#include <sys/stat.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <processgroup/processgroup.h>
+
+#include "cgrouprc_internal.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using android::cgrouprc::format::CgroupController;
+using android::cgrouprc::format::CgroupFile;
+
+static CgroupFile* LoadRcFile() {
+ struct stat sb;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ if (fstat(fd, &sb) < 0) {
+ PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ size_t file_size = sb.st_size;
+ if (file_size < sizeof(CgroupFile)) {
+ LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (file_data == MAP_FAILED) {
+ PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+ LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
+ munmap(file_data, file_size);
+ return nullptr;
+ }
+
+ auto expected = sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController);
+ if (file_size != expected) {
+ LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size, expected " << expected
+ << ", actual " << file_size;
+ munmap(file_data, file_size);
+ return nullptr;
+ }
+
+ return file_data;
+}
+
+static CgroupFile* GetInstance() {
+ // Deliberately leak this object (not munmap) to avoid a race between destruction on
+ // process exit and concurrent access from another thread.
+ static auto* file = LoadRcFile();
+ return file;
+}
+
+uint32_t ACgroupFile_getVersion() {
+ auto file = GetInstance();
+ if (file == nullptr) return 0;
+ return file->version_;
+}
+
+uint32_t ACgroupFile_getControllerCount() {
+ auto file = GetInstance();
+ if (file == nullptr) return 0;
+ return file->controller_count_;
+}
+
+const ACgroupController* ACgroupFile_getController(uint32_t index) {
+ auto file = GetInstance();
+ if (file == nullptr) return nullptr;
+ CHECK(index < file->controller_count_);
+ // Although the object is not actually an ACgroupController object, all ACgroupController_*
+ // functions implicitly convert ACgroupController* back to CgroupController* before invoking
+ // member functions.
+ return static_cast<ACgroupController*>(&file->controllers_[index]);
+}
diff --git a/libprocessgroup/cgrouprc/cgrouprc_internal.h b/libprocessgroup/cgrouprc/cgrouprc_internal.h
new file mode 100644
index 0000000..cd02f03
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <android/cgrouprc.h>
+
+#include <processgroup/format/cgroup_controller.h>
+#include <processgroup/format/cgroup_file.h>
+
+struct ACgroupController : android::cgrouprc::format::CgroupController {};
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
new file mode 100644
index 0000000..4edd239
--- /dev/null
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -0,0 +1,84 @@
+/*
+ * 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>
+
+__BEGIN_DECLS
+
+// For host builds, __INTRODUCED_IN is not defined.
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(x)
+#endif
+
+struct ACgroupController;
+typedef struct ACgroupController ACgroupController;
+
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+// ACgroupFile
+
+/**
+ * Returns file version. See android::cgrouprc::format::CgroupFile for a list of valid versions
+ * for the file.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getVersion() __INTRODUCED_IN(29);
+
+/**
+ * Returns the number of controllers.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getControllerCount() __INTRODUCED_IN(29);
+
+/**
+ * Returns the controller at the given index.
+ * Returnss nullptr if the given index exceeds getControllerCount().
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) const ACgroupController* ACgroupFile_getController(
+ uint32_t index) __INTRODUCED_IN(29);
+
+// ACgroupController
+
+/**
+ * Returns the version of the given controller.
+ * If the given controller is null, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the name of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getName(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the path of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getPath(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+__END_DECLS
+
+#endif
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
new file mode 100644
index 0000000..91df392
--- /dev/null
+++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
@@ -0,0 +1,11 @@
+LIBCGROUPRC { # introduced=29
+ global:
+ ACgroupFile_getVersion;
+ ACgroupFile_getControllerCount;
+ ACgroupFile_getController;
+ ACgroupController_getVersion;
+ ACgroupController_getName;
+ ACgroupController_getPath;
+ local:
+ *;
+};
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
new file mode 100644
index 0000000..dfbeed7
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+cc_library_static {
+ name: "libcgrouprc_format",
+ host_supported: true,
+ recovery_available: true,
+ srcs: [
+ "cgroup_controller.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
new file mode 100644
index 0000000..877eed8
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+CgroupController::CgroupController(uint32_t version, const std::string& name,
+ const std::string& path) {
+ // strlcpy isn't available on host. Although there is an implementation
+ // in licutils, libcutils itself depends on libcgrouprc_format, causing
+ // a circular dependency.
+ version_ = version;
+ strncpy(name_, name.c_str(), sizeof(name_) - 1);
+ name_[sizeof(name_) - 1] = '\0';
+ strncpy(path_, path.c_str(), sizeof(path_) - 1);
+ path_[sizeof(path_) - 1] = '\0';
+}
+
+uint32_t CgroupController::version() const {
+ return version_;
+}
+
+const char* CgroupController::name() const {
+ return name_;
+}
+
+const char* CgroupController::path() const {
+ return path_;
+}
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
new file mode 100644
index 0000000..64c7532
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <string>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+// Minimal controller description to be mmapped into process address space
+struct CgroupController {
+ public:
+ CgroupController() {}
+ CgroupController(uint32_t version, const std::string& name, const std::string& path);
+
+ uint32_t version() const;
+ const char* name() const;
+ const char* path() const;
+
+ private:
+ static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
+ static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
+
+ uint32_t version_;
+ char name_[CGROUP_NAME_BUF_SZ];
+ char path_[CGROUP_PATH_BUF_SZ];
+};
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
new file mode 100644
index 0000000..f1678a1
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+struct CgroupFile {
+ uint32_t version_;
+ uint32_t controller_count_;
+ CgroupController controllers_[];
+
+ static constexpr uint32_t FILE_VERSION_1 = 1;
+ static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
+};
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 6f973b8..86e6035 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,9 +24,8 @@
__BEGIN_DECLS
static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
-static constexpr const char* CGROUPS_RC_DIR = "/dev/cgroup_info";
+static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
-bool CgroupSetupCgroups();
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index e9dec12..abe63dd 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -55,19 +55,15 @@
#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-bool CgroupSetupCgroups() {
- return CgroupMap::SetupCgroups();
-}
-
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
- const CgroupController* controller = CgroupMap::GetInstance().FindController(cgroup_name);
+ auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
- if (controller == nullptr) {
+ if (!controller.HasValue()) {
return false;
}
if (path) {
- *path = controller->path();
+ *path = controller.path();
}
return true;
@@ -111,7 +107,7 @@
static bool isMemoryCgroupSupported() {
std::string cgroup_name;
- static bool memcg_supported = (CgroupMap::GetInstance().FindController("memory") != nullptr);
+ static bool memcg_supported = CgroupMap::GetInstance().FindController("memory").HasValue();
return memcg_supported;
}
@@ -170,8 +166,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 +182,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() {
@@ -200,7 +203,7 @@
cgroups.push_back(path);
}
if (CgroupGetControllerPath("memory", &path)) {
- cgroups.push_back(path);
+ cgroups.push_back(path + "/apps");
}
for (std::string cgroup_root_path : cgroups) {
@@ -219,9 +222,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 +257,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 +305,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 +315,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";
}
}
@@ -317,6 +329,7 @@
CgroupGetControllerPath("cpuacct", &cpuacct_path);
CgroupGetControllerPath("memory", &memory_path);
+ memory_path += "/apps";
const char* cgroup =
(!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
@@ -380,6 +393,7 @@
std::string cgroup;
if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
CgroupGetControllerPath("memory", &cgroup);
+ cgroup += "/apps";
} else {
CgroupGetControllerPath("cpuacct", &cgroup);
}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
new file mode 100644
index 0000000..e05a690
--- /dev/null
+++ b/libprocessgroup/profiles/Android.bp
@@ -0,0 +1,108 @@
+// 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.
+
+prebuilt_etc {
+ name: "cgroups.json",
+ src: "cgroups.json",
+}
+
+prebuilt_etc {
+ name: "cgroups.recovery.json",
+ filename: "cgroups.json",
+ recovery: true,
+ src: "cgroups.recovery.json",
+}
+
+prebuilt_etc {
+ name: "task_profiles.json",
+ 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",
+ ],
+ proto: {
+ type: "full",
+ export_proto_headers: true,
+ },
+}
+
+cc_test_host {
+ name: "libprocessgroup_proto_test",
+ defaults: ["libprocessgroup_test_defaults"],
+ srcs: [
+ "test.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "liblog",
+ "libjsoncpp",
+ "libjsonpbverify",
+ "libjsonpbparse",
+ "libprocessgroup_proto",
+ ],
+ shared_libs: [
+ "libprotobuf-cpp-full",
+ ],
+ data: [
+ "cgroups.json",
+ "cgroups.recovery.json",
+ "task_profiles.json",
+ ],
+ test_suites: [
+ "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/TEST_MAPPING b/libprocessgroup/profiles/TEST_MAPPING
new file mode 100644
index 0000000..5ff4112
--- /dev/null
+++ b/libprocessgroup/profiles/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "libprocessgroup_proto_test",
+ "host": true
+ }
+ ]
+}
diff --git a/rootdir/cgroups.json b/libprocessgroup/profiles/cgroups.json
similarity index 84%
rename from rootdir/cgroups.json
rename to libprocessgroup/profiles/cgroups.json
index aa71956..5871a63 100644
--- a/rootdir/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -1,6 +1,13 @@
{
"Cgroups": [
{
+ "Controller": "blkio",
+ "Path": "/dev/blkio",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
"Controller": "cpu",
"Path": "/dev/cpuctl",
"Mode": "0755",
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
new file mode 100644
index 0000000..f4070c5
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message Cgroups {
+ repeated Cgroup cgroups = 1 [json_name = "Cgroups"];
+ Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
+}
+
+// Next: 6
+message Cgroup {
+ string controller = 1 [json_name = "Controller"];
+ string path = 2 [json_name = "Path"];
+ string mode = 3 [json_name = "Mode"];
+ string uid = 4 [json_name = "UID"];
+ string gid = 5 [json_name = "GID"];
+}
+
+// Next: 5
+message Cgroups2 {
+ string path = 1 [json_name = "Path"];
+ string mode = 2 [json_name = "Mode"];
+ string uid = 3 [json_name = "UID"];
+ string gid = 4 [json_name = "GID"];
+}
diff --git a/libprocessgroup/profiles/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
new file mode 100644
index 0000000..f0bf5fd
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.recovery.json
@@ -0,0 +1,9 @@
+{
+ "Cgroups": [
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ }
+ ]
+}
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/rootdir/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
similarity index 60%
rename from rootdir/task_profiles.json
rename to libprocessgroup/profiles/task_profiles.json
index 5a090c5..74a39cd 100644
--- a/rootdir/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"
@@ -334,11 +334,64 @@
},
{
- "Name": "TimerSlackHigh",
- "Actions" : [
+ "Name": "LowIoPriority",
+ "Actions": [
{
- "Name" : "SetTimerSlack",
- "Params" :
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "TimerSlackHigh",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
{
"Slack": "40000000"
}
@@ -347,10 +400,10 @@
},
{
"Name": "TimerSlackNormal",
- "Actions" : [
+ "Actions": [
{
- "Name" : "SetTimerSlack",
- "Params" :
+ "Name": "SetTimerSlack",
+ "Params":
{
"Slack": "50000"
}
@@ -360,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%"
}
}
]
@@ -387,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"
}
}
@@ -409,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.proto b/libprocessgroup/profiles/task_profiles.proto
new file mode 100644
index 0000000..578f0d3
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message TaskProfiles {
+ repeated Attribute attributes = 1 [json_name = "Attributes"];
+ repeated Profile profiles = 2 [json_name = "Profiles"];
+}
+
+// Next: 4
+message Attribute {
+ string name = 1 [json_name = "Name"];
+ string controller = 2 [json_name = "Controller"];
+ string file = 3 [json_name = "File"];
+}
+
+// Next: 3
+message Profile {
+ string name = 1 [json_name = "Name"];
+ repeated Action actions = 2 [json_name = "Actions"];
+}
+
+// Next: 3
+message Action {
+ string name = 1 [json_name = "Name"];
+ map<string, string> params = 2 [json_name = "Params"];
+}
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
new file mode 100644
index 0000000..bc9aade
--- /dev/null
+++ b/libprocessgroup/profiles/test.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 namespace ::android::jsonpb;
+using ::android::base::GetExecutableDirectory;
+
+namespace android {
+namespace profiles {
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+ return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
+}
+
+// Test suite instantiations
+INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
+ ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+ MakeTestParam<Cgroups>("/cgroups.recovery.json"),
+ MakeTestParam<TaskProfiles>("/task_profiles.json")));
+INSTANTIATE_TEST_SUITE_P(, CgroupsTest,
+ ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+ MakeTestParam<Cgroups>("/cgroups.recovery.json")));
+INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest,
+ ::testing::Values(MakeTestParam<TaskProfiles>("/task_profiles.json")));
+
+} // namespace profiles
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
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 337b032..c7d0cca 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -46,20 +46,20 @@
switch (policy) {
case SP_BACKGROUND:
- return SetTaskProfiles(tid,
- {"HighEnergySaving", "ProcessCapacityLow", "TimerSlackHigh"})
+ return SetTaskProfiles(tid, {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
+ "TimerSlackHigh"})
? 0
: -1;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
- return SetTaskProfiles(tid,
- {"HighPerformance", "ProcessCapacityHigh", "TimerSlackNormal"})
+ return SetTaskProfiles(tid, {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
+ "TimerSlackNormal"})
? 0
: -1;
case SP_TOP_APP:
- return SetTaskProfiles(tid,
- {"MaxPerformance", "ProcessCapacityMax", "TimerSlackNormal"})
+ return SetTaskProfiles(tid, {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
+ "TimerSlackNormal"})
? 0
: -1;
case SP_SYSTEM:
@@ -126,15 +126,24 @@
switch (policy) {
case SP_BACKGROUND:
- return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}) ? 0 : -1;
+ return SetTaskProfiles(tid, {"HighEnergySaving", "LowIoPriority", "TimerSlackHigh"})
+ ? 0
+ : -1;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
- return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}) ? 0 : -1;
+ return SetTaskProfiles(tid, {"HighPerformance", "HighIoPriority", "TimerSlackNormal"})
+ ? 0
+ : -1;
case SP_TOP_APP:
- return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}) ? 0 : -1;
+ return SetTaskProfiles(tid, {"MaxPerformance", "MaxIoPriority", "TimerSlackNormal"})
+ ? 0
+ : -1;
case SP_RT_APP:
- return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}) ? 0 : -1;
+ return SetTaskProfiles(tid,
+ {"RealtimePerformance", "MaxIoPriority", "TimerSlackNormal"})
+ ? 0
+ : -1;
default:
return SetTaskProfiles(tid, {"TimerSlackNormal"}) ? 0 : -1;
}
@@ -143,22 +152,22 @@
}
bool cpusets_enabled() {
- static bool enabled = (CgroupMap::GetInstance().FindController("cpuset") != nullptr);
+ static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").HasValue());
return enabled;
}
bool schedboost_enabled() {
- static bool enabled = (CgroupMap::GetInstance().FindController("schedtune") != nullptr);
+ static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").HasValue());
return enabled;
}
static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
- const CgroupController* controller = CgroupMap::GetInstance().FindController(subsys);
+ auto controller = CgroupMap::GetInstance().FindController(subsys);
- if (!controller) return -1;
+ if (!controller.HasValue()) return -1;
- if (!controller->GetTaskGroup(tid, &subgroup)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ if (!controller.GetTaskGroup(tid, &subgroup)) {
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return -1;
}
return 0;
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
new file mode 100644
index 0000000..f6fc066
--- /dev/null
+++ b/libprocessgroup/setup/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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.
+//
+
+cc_library_shared {
+ name: "libprocessgroup_setup",
+ recovery_available: true,
+ srcs: [
+ "cgroup_map_write.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libjsoncpp",
+ ],
+ static_libs: [
+ "libcgrouprc_format",
+ ],
+ header_libs: [
+ "libprocessgroup_headers",
+ ],
+ export_header_lib_headers: [
+ "libprocessgroup_headers",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
new file mode 100644
index 0000000..597060e
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+
+// Complete controller description for mounting cgroups
+class CgroupDescriptor {
+ public:
+ CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
+ mode_t mode, const std::string& uid, const std::string& gid);
+
+ const format::CgroupController* controller() const { return &controller_; }
+ mode_t mode() const { return mode_; }
+ std::string uid() const { return uid_; }
+ std::string gid() const { return gid_; }
+
+ private:
+ format::CgroupController controller_;
+ mode_t mode_ = 0;
+ std::string uid_;
+ std::string gid_;
+};
+
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
new file mode 100644
index 0000000..da60948
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/format/cgroup_file.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
+
+#include "cgroup_descriptor.h"
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace cgrouprc {
+
+static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
+
+static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
+ const std::string& gid) {
+ if (mode == 0) {
+ mode = 0755;
+ }
+
+ if (mkdir(path.c_str(), mode) != 0) {
+ /* chmod in case the directory already exists */
+ if (errno == EEXIST) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ // /acct is a special case when the directory already exists
+ // TODO: check if file mode is already what we want instead of using EROFS
+ if (errno != EROFS) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
+ }
+ } else {
+ PLOG(ERROR) << "mkdir() failed for " << path;
+ return false;
+ }
+ }
+
+ if (uid.empty()) {
+ return true;
+ }
+
+ passwd* uid_pwd = getpwnam(uid.c_str());
+ if (!uid_pwd) {
+ PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
+ return false;
+ }
+
+ uid_t pw_uid = uid_pwd->pw_uid;
+ gid_t gr_gid = -1;
+ if (!gid.empty()) {
+ group* gid_pwd = getgrnam(gid.c_str());
+ if (!gid_pwd) {
+ PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
+ return false;
+ }
+ gr_gid = gid_pwd->gr_gid;
+ }
+
+ if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
+ PLOG(ERROR) << "lchown() failed for " << path;
+ return false;
+ }
+
+ /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+ if (mode & (S_ISUID | S_ISGID)) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+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(file_name, &json_doc)) {
+ PLOG(ERROR) << "Failed to read task profiles from " << file_name;
+ return false;
+ }
+
+ Json::Reader reader;
+ Json::Value root;
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
+ return false;
+ }
+
+ if (root.isMember("Cgroups")) {
+ const Json::Value& cgroups = root["Cgroups"];
+ for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+ std::string name = cgroups[i]["Controller"].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()));
+ } 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"];
+ 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()));
+ } 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;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+ const format::CgroupController* controller = descriptor.controller();
+
+ // mkdir <path> [mode] [owner] [group]
+ if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+ LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+ return false;
+ }
+
+ int result;
+ if (controller->version() == 2) {
+ result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ nullptr);
+ } else {
+ // Unfortunately historically cpuset controller was mounted using a mount command
+ // different from all other controllers. This results in controller attributes not
+ // to be prepended with controller name. For example this way instead of
+ // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
+ // the system currently expects.
+ if (!strcmp(controller->name(), "cpuset")) {
+ // mount cpuset none /dev/cpuset nodev noexec nosuid
+ result = mount("none", controller->path(), controller->name(),
+ MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
+ } else {
+ // mount cgroup none <path> nodev noexec nosuid <controller>
+ result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ controller->name());
+ }
+ }
+
+ if (result < 0) {
+ PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+ return false;
+ }
+
+ return true;
+}
+
+#else
+
+// Stubs for non-Android targets.
+static bool SetupCgroup(const CgroupDescriptor&) {
+ return false;
+}
+
+#endif
+
+static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IRGRP | S_IROTH)));
+ if (fd < 0) {
+ PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+
+ format::CgroupFile fl;
+ fl.version_ = format::CgroupFile::FILE_CURR_VERSION;
+ fl.controller_count_ = descriptors.size();
+ int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
+ if (ret < 0) {
+ PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+
+ for (const auto& [name, descriptor] : descriptors) {
+ ret = TEMP_FAILURE_RETRY(
+ write(fd, descriptor.controller(), sizeof(format::CgroupController)));
+ if (ret < 0) {
+ PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
+ const std::string& path, mode_t mode, const std::string& uid,
+ const std::string& gid)
+ : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+
+} // namespace cgrouprc
+} // namespace android
+
+bool CgroupSetup() {
+ using namespace android::cgrouprc;
+
+ std::map<std::string, CgroupDescriptor> descriptors;
+
+ if (getpid() != 1) {
+ LOG(ERROR) << "Cgroup setup can be done only by init process";
+ return false;
+ }
+
+ // Make sure we do this only one time. No need for std::call_once because
+ // init is a single-threaded process
+ if (access(CGROUPS_RC_PATH, F_OK) == 0) {
+ LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+ return true;
+ }
+
+ // load cgroups.json file
+ if (!ReadDescriptors(&descriptors)) {
+ LOG(ERROR) << "Failed to load cgroup description file";
+ return false;
+ }
+
+ // setup cgroups
+ for (const auto& [name, descriptor] : descriptors) {
+ if (!SetupCgroup(descriptor)) {
+ // issue a warning and proceed with the next cgroup
+ // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
+ LOG(WARNING) << "Failed to setup " << name << " cgroup";
+ }
+ }
+
+ // mkdir <CGROUPS_RC_DIR> 0711 system system
+ if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
+ LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
+ return false;
+ }
+
+ // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
+ // process memory. This optimizes performance, memory usage
+ // and limits infrormation shared with unprivileged processes
+ // to the minimum subset of information from cgroups.json
+ if (!WriteRcFile(descriptors)) {
+ LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
+ return false;
+ }
+
+ // chmod 0644 <CGROUPS_RC_PATH>
+ if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
+ PLOG(ERROR) << "fchmodat() failed";
+ return false;
+ }
+
+ return true;
+}
diff --git a/libprocessgroup/setup/include/processgroup/setup.h b/libprocessgroup/setup/include/processgroup/setup.h
new file mode 100644
index 0000000..6ea1979
--- /dev/null
+++ b/libprocessgroup/setup/include/processgroup/setup.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
+
+bool CgroupSetup();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 447852d..4b45c87 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -42,10 +42,11 @@
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;
- if (!controller_->GetTaskGroup(tid, &subgroup)) {
+ if (!controller()->GetTaskGroup(tid, &subgroup)) {
return false;
}
@@ -54,9 +55,10 @@
}
if (subgroup.empty()) {
- *path = StringPrintf("%s/%s", controller_->path(), file_name_.c_str());
+ *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
} else {
- *path = StringPrintf("%s/%s/%s", controller_->path(), subgroup.c_str(), file_name_.c_str());
+ *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+ file_name_.c_str());
}
return true;
}
@@ -90,6 +92,10 @@
if (sys_supports_timerslack) {
auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
if (!WriteStringToFile(std::to_string(slack_), file)) {
+ if (errno == ENOENT) {
+ // This happens when process is already dead
+ return true;
+ }
PLOG(ERROR) << "set_timerslack_ns write failed";
}
}
@@ -114,7 +120,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;
}
@@ -130,7 +136,7 @@
return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
}
-SetCgroupAction::SetCgroupAction(const CgroupController* c, const std::string& p)
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
: controller_(c), path_(p) {
#ifdef CACHE_FILE_DESCRIPTORS
// cache file descriptor only if path is app independent
@@ -140,7 +146,7 @@
return;
}
- std::string tasks_path = c->GetTasksFilePath(p.c_str());
+ std::string tasks_path = c.GetTasksFilePath(p);
if (access(tasks_path.c_str(), W_OK) != 0) {
// file is not accessible
@@ -169,7 +175,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;
}
}
@@ -182,7 +188,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;
@@ -194,27 +200,27 @@
}
// this is app-dependent path, file descriptor is not cached
- std::string procs_path = controller_->GetProcsFilePath(path_.c_str(), uid, pid);
+ 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;
}
return true;
#else
- std::string procs_path = controller_->GetProcsFilePath(path_.c_str(), uid, pid);
+ 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) {
// no permissions to access the file, ignore
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;
}
@@ -227,7 +233,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;
@@ -239,17 +245,17 @@
}
// 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_.c_str());
+ std::string tasks_path = controller()->GetTasksFilePath(path_);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
// no permissions to access the file, ignore
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;
}
@@ -279,21 +285,31 @@
}
TaskProfiles& TaskProfiles::GetInstance() {
- static TaskProfiles instance;
- return instance;
+ // Deliberately leak this object to avoid a race between destruction on
+ // process exit and concurrent access from another thread.
+ static auto* instance = new TaskProfiles;
+ return *instance;
}
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;
}
@@ -304,18 +320,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.c_str());
- if (controller) {
- attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_name);
+ auto controller = cg_map.FindController(controller_name);
+ if (controller.HasValue()) {
+ 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";
@@ -324,72 +340,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.c_str());
- if (controller) {
+ auto controller = cg_map.FindController(controller_name);
+ if (controller.HasValue()) {
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..37cc305 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -27,16 +27,16 @@
class ProfileAttribute {
public:
- ProfileAttribute(const CgroupController* controller, const std::string& file_name)
+ ProfileAttribute(const CgroupController& controller, const std::string& file_name)
: controller_(controller), file_name_(file_name) {}
- const CgroupController* controller() const { return controller_; }
+ const CgroupController* controller() const { return &controller_; }
const std::string& file_name() const { return file_name_; }
bool GetPathForTask(int tid, std::string* path) const;
private:
- const CgroupController* controller_;
+ CgroupController controller_;
std::string file_name_;
};
@@ -106,16 +106,16 @@
// Set cgroup profile element
class SetCgroupAction : public ProfileAction {
public:
- SetCgroupAction(const CgroupController* c, const std::string& p);
+ SetCgroupAction(const CgroupController& c, const std::string& p);
virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
virtual bool ExecuteForTask(int tid) const;
- const CgroupController* controller() const { return controller_; }
+ const CgroupController* controller() const { return &controller_; }
std::string path() const { return path_; }
private:
- const CgroupController* controller_;
+ CgroupController controller_;
std::string path_;
#ifdef CACHE_FILE_DESCRIPTORS
android::base::unique_fd fd_;
@@ -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/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 981241e..b6ec3cb 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -36,6 +36,7 @@
uint64_t end_addr;
uint16_t flags;
uint64_t pgoff;
+ ino_t inode;
char* next_line = content;
char* p;
@@ -124,18 +125,25 @@
return false;
}
// inode
- if (!pass_xdigit() || (*p != '\0' && !pass_space())) {
+ inode = strtoull(p, &end, 10);
+ if (end == p) {
return false;
}
+ p = end;
+
+ if (*p != '\0' && !pass_space()) {
+ return false;
+ }
+
// filename
- callback(start_addr, end_addr, flags, pgoff, p);
+ callback(start_addr, end_addr, flags, pgoff, inode, p);
}
return true;
}
-inline bool ReadMapFile(
- const std::string& map_file,
- const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadMapFile(const std::string& map_file,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
std::string content;
if (!android::base::ReadFileToString(map_file, &content)) {
return false;
@@ -143,9 +151,9 @@
return ReadMapFileContent(&content[0], callback);
}
-inline bool ReadProcessMaps(
- pid_t pid,
- const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadProcessMaps(pid_t pid,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
}
@@ -154,17 +162,18 @@
uint64_t end;
uint16_t flags;
uint64_t pgoff;
+ ino_t inode;
std::string name;
- MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
- : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
+ MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name)
+ : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
};
inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
return ReadProcessMaps(
- pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
- maps->emplace_back(start, end, flags, pgoff, name);
- });
+ pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
}
} /* namespace procinfo */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
index 04995d4..eba4fd0 100644
--- a/libprocinfo/process_map_benchmark.cpp
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -17,6 +17,7 @@
#include <procinfo/process_map.h>
#include <string.h>
+#include <sys/types.h>
#include <string>
@@ -31,9 +32,10 @@
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
for (auto _ : state) {
std::vector<android::procinfo::MapInfo> maps;
- android::procinfo::ReadMapFile(
- map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
- const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
+ android::procinfo::ReadMapFile(map_file, [&](uint64_t start, uint64_t end, uint16_t flags,
+ uint64_t pgoff, ino_t inode, const char* name) {
+ maps.emplace_back(start, end, flags, pgoff, inode, name);
+ });
CHECK_EQ(maps.size(), 2043u);
}
}
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 170a806..562d864 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -26,23 +26,27 @@
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
std::vector<android::procinfo::MapInfo> maps;
ASSERT_TRUE(android::procinfo::ReadMapFile(
- map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
- const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+ map_file,
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
ASSERT_EQ(2043u, maps.size());
ASSERT_EQ(maps[0].start, 0x12c00000ULL);
ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
ASSERT_EQ(maps[0].pgoff, 0ULL);
+ ASSERT_EQ(maps[0].inode, 10267643UL);
ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
ASSERT_EQ(maps[876].pgoff, 0ULL);
+ ASSERT_EQ(maps[876].inode, 2407UL);
ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
ASSERT_EQ(maps[1260].flags, PROT_READ);
ASSERT_EQ(maps[1260].pgoff, 0ULL);
+ ASSERT_EQ(maps[1260].inode, 10266154UL);
ASSERT_EQ(maps[1260].name,
"[anon:dalvik-classes.dex extracted in memory from "
"/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
@@ -51,8 +55,9 @@
TEST(process_map, ReadProcessMaps) {
std::vector<android::procinfo::MapInfo> maps;
ASSERT_TRUE(android::procinfo::ReadProcessMaps(
- getpid(), [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
- const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+ getpid(),
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
ASSERT_GT(maps.size(), 0u);
maps.clear();
ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index b5bc5af..845a197 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -18,15 +18,18 @@
#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
#include <log/log_event_list.h>
+#include <sys/uio.h>
#ifdef __cplusplus
extern "C" {
#endif
void reset_log_context(android_log_context ctx);
int write_to_logger(android_log_context context, log_id_t id);
-void note_log_drop(int error);
+void note_log_drop(int error, int atom_tag);
void stats_log_close();
int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+extern int (*write_to_statsd)(struct iovec* vec, size_t nr);
+
#ifdef __cplusplus
}
#endif
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 5b90361..ae12cbe 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -41,7 +41,7 @@
extern struct android_log_transport_write statsdLoggerWrite;
static int __write_to_statsd_init(struct iovec* vec, size_t nr);
-static int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
// Similar to create_android_logger(), but instead of allocation a new buffer,
// this function resets the buffer for resuse.
@@ -120,8 +120,8 @@
return retValue;
}
-void note_log_drop(int error) {
- statsdLoggerWrite.noteDrop(error);
+void note_log_drop(int error, int tag) {
+ statsdLoggerWrite.noteDrop(error, tag);
}
void stats_log_close() {
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index f5be95c..b778f92 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -47,9 +47,18 @@
#endif
#endif
+#ifndef htole64
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole64(x) (x)
+#else
+#define htole64(x) __bswap_64(x)
+#endif
+#endif
+
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int dropped = 0;
static atomic_int log_error = 0;
+static atomic_int atom_tag = 0;
void statsd_writer_init_lock() {
/*
@@ -152,9 +161,10 @@
return 1;
}
-static void statsdNoteDrop(int error) {
+static void statsdNoteDrop(int error, int tag) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
+ atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
}
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
@@ -203,12 +213,17 @@
if (sock >= 0) {
int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
if (snapshot) {
- android_log_event_int_t buffer;
+ android_log_event_long_t buffer;
header.id = LOG_ID_STATS;
// store the last log error in the tag field. This tag field is not used by statsd.
buffer.header.tag = htole32(atomic_load(&log_error));
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = htole32(snapshot);
+ buffer.payload.type = EVENT_TYPE_LONG;
+ // format:
+ // |atom_tag|dropped_count|
+ int64_t composed_long = atomic_load(&atom_tag);
+ // Send 2 int32's via an int64.
+ composed_long = ((composed_long << 32) | ((int64_t)snapshot));
+ buffer.payload.data = htole64(composed_long);
newVec[headerLength].iov_base = &buffer;
newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 4fc3f8b..fe2d37c 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -39,7 +39,7 @@
/* write log to transport, returns number of bytes propagated, or -errno */
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
/* note one log drop */
- void (*noteDrop)(int error);
+ void (*noteDrop)(int error, int tag);
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
index 645baf4..4c99361 100644
--- a/libsysutils/include/sysutils/OWNERS
+++ b/libsysutils/include/sysutils/OWNERS
@@ -1 +1,2 @@
-per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
+include ../../src/OWNERS
+
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index b07853a..523584a 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -26,7 +26,7 @@
#include <sysutils/FrameworkListener.h>
#include <sysutils/SocketClient.h>
-static const int CMD_BUF_SIZE = 1024;
+static const int CMD_BUF_SIZE = 4096;
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
index 645baf4..c65a40d 100644
--- a/libsysutils/src/OWNERS
+++ b/libsysutils/src/OWNERS
@@ -1 +1,2 @@
-per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
+per-file OWNERS,Netlink* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, satk@google.com
+
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index ded5adb..9780606 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -95,7 +95,7 @@
} else if (!mListen)
mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
- if (pipe(mCtrlPipe)) {
+ if (pipe2(mCtrlPipe, O_CLOEXEC)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index c90f5b2..b7650a1 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -95,7 +95,6 @@
"DexFiles.cpp",
],
exclude_shared_libs: [
- "libdexfile_external",
"libdexfile_support",
],
},
@@ -106,7 +105,6 @@
"DexFiles.cpp",
],
exclude_shared_libs: [
- "libdexfile_external",
"libdexfile_support",
],
},
@@ -137,7 +135,6 @@
shared_libs: [
"libbase",
- "libdexfile_external",
"libdexfile_support",
"liblog",
"liblzma",
@@ -251,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/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 668527a..802beca 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -61,6 +61,14 @@
table_encoding_ = data[3];
table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+ // If we can't perform a binary search on the entries, it's not worth
+ // using this object. The calling code will fall back to the DwarfEhFrame
+ // object in this case.
+ if (table_entry_size_ == 0) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
memory_.set_pc_offset(memory_.cur_offset());
if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
@@ -137,13 +145,13 @@
}
template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
- uint64_t total_entries) {
- CHECK(fde_count_ > 0);
- CHECK(total_entries <= fde_count_);
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
size_t first = 0;
- size_t last = total_entries;
+ size_t last = fde_count_;
while (first < last) {
size_t current = (first + last) / 2;
const FdeInfo* info = GetFdeInfoFromIndex(current);
@@ -172,87 +180,6 @@
}
template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
- CHECK(fde_count_ != 0);
- last_error_.code = DWARF_ERROR_NONE;
- last_error_.address = 0;
-
- // We can do a binary search if the pc is in the range of the elements
- // that have already been cached.
- if (!fde_info_.empty()) {
- const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
- if (pc >= info->pc) {
- *fde_offset = info->offset;
- return true;
- }
- if (pc < info->pc) {
- return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
- }
- }
-
- if (cur_entries_offset_ == 0) {
- // All entries read, or error encountered.
- return false;
- }
-
- memory_.set_data_offset(entries_data_offset_);
- memory_.set_cur_offset(cur_entries_offset_);
- memory_.set_pc_offset(0);
- cur_entries_offset_ = 0;
-
- FdeInfo* prev_info = nullptr;
- for (size_t current = fde_info_.size();
- current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
- FdeInfo* info = &fde_info_[current];
- uint64_t value;
- if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
- !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
- fde_info_.erase(current);
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
-
- // Relative encodings require adding in the load bias.
- if (IsEncodingRelative(table_encoding_)) {
- value += load_bias_;
- }
- info->pc = value;
-
- if (pc < info->pc) {
- if (prev_info == nullptr) {
- return false;
- }
- cur_entries_offset_ = memory_.cur_offset();
- *fde_offset = prev_info->offset;
- return true;
- }
- prev_info = info;
- }
-
- if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
- *fde_offset = prev_info->offset;
- return true;
- }
- return false;
-}
-
-template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
- if (fde_count_ == 0) {
- return false;
- }
-
- if (table_entry_size_ > 0) {
- // Do a binary search since the size of each table entry is fixed.
- return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
- } else {
- // Do a sequential search since each table entry size is variable.
- return GetFdeOffsetSequential(pc, fde_offset);
- }
-}
-
-template <typename AddressType>
void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
for (size_t i = 0; i < fde_count_; i++) {
const FdeInfo* info = GetFdeInfoFromIndex(i);
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index e3e9ca8..0e5eef7 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -69,10 +69,6 @@
const FdeInfo* GetFdeInfoFromIndex(size_t index);
- bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
-
- bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
-
void GetFdes(std::vector<const DwarfFde*>* fdes) override;
protected:
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 57a780e..849a31a 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -138,7 +138,7 @@
return false;
}
- if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+ if (cie->version != 1 && cie->version != 3 && cie->version != 4 && cie->version != 5) {
// Unrecognized version.
last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
return false;
@@ -155,7 +155,7 @@
cie->augmentation_string.push_back(aug_value);
} while (aug_value != '\0');
- if (cie->version == 4) {
+ if (cie->version == 4 || cie->version == 5) {
// Skip the Address Size field since we only use it for validation.
memory_.set_cur_offset(memory_.cur_offset() + 1);
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 2f5eed9..3454913 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) {
@@ -157,7 +160,7 @@
if (valid_) {
return interface_->LastErrorCode();
}
- return ERROR_NONE;
+ return ERROR_INVALID_ELF;
}
uint64_t Elf::GetLastErrorAddress() {
@@ -167,22 +170,23 @@
return 0;
}
+// The relative pc expectd by this function is relative to the start of the elf.
+bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
+ if (!valid_) {
+ return false;
+ }
+ return regs->StepIfSignalHandler(rel_pc, this, process_memory);
+}
+
// The relative pc is always relative to the start of the map from which it comes.
-bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
if (!valid_) {
return false;
}
- // The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
- if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
- *finished = false;
- return true;
- }
-
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
- return interface_->Step(adjusted_rel_pc, regs, process_memory, finished);
+ return interface_->Step(rel_pc, regs, process_memory, finished);
}
bool Elf::IsValidElf(Memory* memory) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index c1b98d9..dee8eb3 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -276,7 +276,7 @@
if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
return "";
}
- std::string build_id(hdr.n_descsz - 1, '\0');
+ std::string build_id(hdr.n_descsz, '\0');
if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
return build_id;
}
@@ -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/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 5b2fadf..5d81200 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -111,6 +111,14 @@
pc_adjustment = 0;
}
step_pc -= pc_adjustment;
+
+ bool finished = false;
+ if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
+ step_pc = rel_pc;
+ } else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished)) {
+ finished = true;
+ }
+
// Skip any locations that are within this library.
if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
// Add frame information.
@@ -124,22 +132,12 @@
}
num_frames++;
}
- if (!elf->valid()) {
- break;
- }
- if (frame_info->size() == max_frames) {
- break;
- }
+ if (finished || frame_info->size() == max_frames ||
+ (cur_pc == regs->pc() && cur_sp == regs->sp())) {
+ break;
+ }
adjust_pc = true;
- bool finished;
- if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
- break;
- }
- // pc and sp are the same, terminate the unwind.
- if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
- break;
- }
}
return num_frames != 0;
}
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 c90e383..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 {
@@ -62,39 +62,36 @@
bool Maps::Parse() {
return android::procinfo::ReadMapFile(
GetMapsFile(),
- [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
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();
}
}
@@ -102,13 +99,14 @@
std::string content(buffer_);
return android::procinfo::ReadMapFileContent(
&content[0],
- [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
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..f3d2b5e 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -89,8 +89,8 @@
#endif
}
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
- uint64_t pc_adjustment) {
+FrameData* Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc,
+ uint64_t pc_adjustment) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
@@ -100,11 +100,18 @@
frame->pc = regs_->pc() - pc_adjustment;
if (map_info == nullptr) {
- return;
+ // Nothing else to update.
+ return nullptr;
}
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;
@@ -112,12 +119,7 @@
frame->map_end = map_info->end;
frame->map_flags = map_info->flags;
frame->map_load_bias = elf->GetLoadBias();
-
- if (!resolve_names_ ||
- !elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
- frame->function_name = "";
- frame->function_offset = 0;
- }
+ return frame;
}
static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
@@ -188,6 +190,7 @@
}
}
+ FrameData* frame = nullptr;
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
@@ -204,23 +207,21 @@
}
}
- FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
+ frame = FillInFrame(map_info, elf, rel_pc, pc_adjustment);
// Once a frame is added, stop skipping frames.
initial_map_names_to_skip = nullptr;
}
adjust_pc = true;
- bool stepped;
+ bool stepped = false;
bool in_device_map = false;
- if (map_info == nullptr) {
- stepped = false;
- } else {
+ bool finished = false;
+ if (map_info != nullptr) {
if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
// Do not stop here, fall through in case we are
// in the speculative unwind path and need to remove
// some of the speculative frames.
- stepped = false;
in_device_map = true;
} else {
MapInfo* sp_info = maps_->Find(regs_->sp());
@@ -228,19 +229,37 @@
// Do not stop here, fall through in case we are
// in the speculative unwind path and need to remove
// some of the speculative frames.
- stepped = false;
in_device_map = true;
} else {
- bool finished;
- stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
- elf->GetLastError(&last_error_);
- if (stepped && finished) {
- break;
+ if (elf->StepIfSignalHandler(rel_pc, regs_, process_memory_.get())) {
+ stepped = true;
+ if (frame != nullptr) {
+ // Need to adjust the relative pc because the signal handler
+ // pc should not be adjusted.
+ frame->rel_pc = rel_pc;
+ frame->pc += pc_adjustment;
+ step_pc = rel_pc;
+ }
+ } else if (elf->Step(step_pc, regs_, process_memory_.get(), &finished)) {
+ stepped = true;
}
+ elf->GetLastError(&last_error_);
}
}
}
+ if (frame != nullptr) {
+ if (!resolve_names_ ||
+ !elf->GetFunctionName(step_pc, &frame->function_name, &frame->function_offset)) {
+ frame->function_name = "";
+ frame->function_offset = 0;
+ }
+ }
+
+ if (finished) {
+ break;
+ }
+
if (!stepped) {
if (return_address_attempt) {
// Only remove the speculative frame if there are more than two frames
@@ -278,17 +297,9 @@
}
}
-std::string Unwinder::FormatFrame(size_t frame_num) {
- if (frame_num >= frames_.size()) {
- return "";
- }
- return FormatFrame(frames_[frame_num], regs_->Is32Bit());
-}
-
-std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
+std::string Unwinder::FormatFrame(const FrameData& frame) {
std::string data;
-
- if (is32bit) {
+ if (regs_->Is32Bit()) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
} else {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
@@ -314,9 +325,24 @@
}
data += ')';
}
+
+ MapInfo* map_info = maps_->Find(frame.map_start);
+ if (map_info != nullptr && display_build_id_) {
+ std::string build_id = map_info->GetPrintableBuildID();
+ if (!build_id.empty()) {
+ data += " (BuildId: " + build_id + ')';
+ }
+ }
return data;
}
+std::string Unwinder::FormatFrame(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrame(frames_[frame_num]);
+}
+
void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
jit_debug->SetArch(arch);
jit_debug_ = jit_debug;
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..56bf318 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);
@@ -67,8 +67,9 @@
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
- bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
- bool* finished);
+ bool StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory);
+
+ bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
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/Error.h b/libunwindstack/include/unwindstack/Error.h
index 6ed0e0f..72ec454 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -29,6 +29,7 @@
ERROR_INVALID_MAP, // Unwind in an invalid map.
ERROR_MAX_FRAMES_EXCEEDED, // The number of frames exceed the total allowed.
ERROR_REPEATED_FRAME, // The last frame has the same pc/sp as the next.
+ ERROR_INVALID_ELF, // Unwind in an invalid elf.
};
struct ErrorData {
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..75be209 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -88,7 +88,7 @@
}
std::string FormatFrame(size_t frame_num);
- static std::string FormatFrame(const FrameData& frame, bool is32bit);
+ std::string FormatFrame(const FrameData& frame);
void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
@@ -100,6 +100,13 @@
// 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; }
+
+ void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
+
#if !defined(NO_LIBDEXFILE_SUPPORT)
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
#endif
@@ -111,8 +118,7 @@
Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
void FillInDexFrame();
- void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
- uint64_t pc_adjustment);
+ FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
size_t max_frames_;
Maps* maps_;
@@ -124,6 +130,8 @@
DexFiles* dex_files_ = nullptr;
#endif
bool resolve_names_ = true;
+ bool embedded_soname_ = true;
+ bool display_build_id_ = false;
ErrorData last_error_;
};
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index d620934..120bd73 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -550,6 +550,22 @@
VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
}
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version5) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
@@ -558,10 +574,10 @@
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
- SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+ SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
- SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+ SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
}
@@ -803,9 +819,10 @@
GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
- GetCieFromOffset64_version4, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
- GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
- GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
+ GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
+ GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
+ GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
+ GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 910ae36..be9e721 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -95,6 +95,13 @@
EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+ // Verify a zero table entry size fails to init.
+ this->memory_.SetData8(0x1003, 0x1);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
+ // Reset the value back to the original.
+ this->memory_.SetData8(0x1003, DW_EH_PE_sdata4);
+
// Verify a zero fde count fails to init.
this->memory_.SetData32(0x1006, 0);
ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
@@ -276,9 +283,8 @@
EXPECT_EQ(0x500U, info->offset);
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_verify) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_verify) {
this->eh_frame_->TestSetTableEntrySize(0x10);
- this->eh_frame_->TestSetFdeCount(10);
typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
for (size_t i = 0; i < 10; i++) {
@@ -288,105 +294,42 @@
}
uint64_t fde_offset;
- EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+ this->eh_frame_->TestSetFdeCount(10);
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
// Not an error, just not found.
ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
// Even number of elements.
for (size_t i = 0; i < 10; i++) {
+ SCOPED_TRACE(testing::Message() << "Failed at index " << i);
TypeParam pc = 0x1000 * (i + 1);
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
}
+
// Odd number of elements.
+ this->eh_frame_->TestSetFdeCount(9);
for (size_t i = 0; i < 9; i++) {
+ SCOPED_TRACE(testing::Message() << "Failed at index " << i);
TypeParam pc = 0x1000 * (i + 1);
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
}
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_index_fail) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_index_fail) {
this->eh_frame_->TestSetTableEntrySize(0x10);
this->eh_frame_->TestSetFdeCount(10);
uint64_t fde_offset;
- EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x1000, &fde_offset, 10));
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential) {
- this->eh_frame_->TestSetFdeCount(10);
- this->eh_frame_->TestSetEntriesDataOffset(0x100);
- this->eh_frame_->TestSetEntriesEnd(0x2000);
- this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
- this->memory_.SetData32(0x1040, 0x340);
- this->memory_.SetData32(0x1044, 0x500);
-
- this->memory_.SetData32(0x1048, 0x440);
- this->memory_.SetData32(0x104c, 0x600);
-
- // Verify that if entries is zero, that it fails.
- uint64_t fde_offset;
- ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
- this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
- EXPECT_EQ(0x500U, fde_offset);
-
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
- EXPECT_EQ(0x600U, fde_offset);
-
- // Expect that the data is cached so no more memory reads will occur.
- this->memory_.Clear();
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
- EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_last_element) {
- this->eh_frame_->TestSetFdeCount(2);
- this->eh_frame_->TestSetEntriesDataOffset(0x100);
- this->eh_frame_->TestSetEntriesEnd(0x2000);
- this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
- this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
- this->memory_.SetData32(0x1040, 0x340);
- this->memory_.SetData32(0x1044, 0x500);
-
- this->memory_.SetData32(0x1048, 0x440);
- this->memory_.SetData32(0x104c, 0x600);
-
- uint64_t fde_offset;
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
- EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_end_check) {
- this->eh_frame_->TestSetFdeCount(2);
- this->eh_frame_->TestSetEntriesDataOffset(0x100);
- this->eh_frame_->TestSetEntriesEnd(0x1048);
- this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
- this->memory_.SetData32(0x1040, 0x340);
- this->memory_.SetData32(0x1044, 0x500);
-
- this->memory_.SetData32(0x1048, 0x440);
- this->memory_.SetData32(0x104c, 0x600);
-
- uint64_t fde_offset;
- ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
- ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
}
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
@@ -397,7 +340,7 @@
ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_search) {
this->eh_frame_->TestSetTableEntrySize(16);
this->eh_frame_->TestSetFdeCount(10);
@@ -417,26 +360,6 @@
EXPECT_EQ(0x10700U, fde_offset);
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_sequential_search) {
- this->eh_frame_->TestSetFdeCount(10);
- this->eh_frame_->TestSetTableEntrySize(0);
-
- typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
- info.pc = 0x50;
- info.offset = 0x10000;
- this->eh_frame_->TestSetFdeInfo(0, info);
- info.pc = 0x150;
- info.offset = 0x10100;
- this->eh_frame_->TestSetFdeInfo(1, info);
- info.pc = 0x250;
- info.offset = 0x10200;
- this->eh_frame_->TestSetFdeInfo(2, info);
-
- uint64_t fde_offset;
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
- EXPECT_EQ(0x10100U, fde_offset);
-}
-
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
// CIE 32 information.
this->memory_.SetData32(0xf000, 0x100);
@@ -526,10 +449,8 @@
REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
- GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
- GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
- GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
- GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+ GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
+ GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_search,
GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
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..cdc927a 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) {
@@ -1197,14 +1192,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1249,24 +1246,27 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 8; // "WRONG" aligned to 4
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
memcpy(¬e_section[note_offset], "WRONG", sizeof("WRONG"));
note_offset += 8;
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section[note_offset], ¬e_header, sizeof(note_header));
note_offset += sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1311,14 +1311,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1363,14 +1365,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1415,14 +1419,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1ff2306..c432d6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -126,14 +126,18 @@
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));
+ ASSERT_FALSE(elf.StepIfSignalHandler(0, nullptr, nullptr));
+ EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
+
bool finished;
- ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
+ EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
}
TEST_F(ElfTest, elf32_invalid_machine) {
@@ -295,9 +299,8 @@
}
elf.FakeSetValid(true);
- bool finished;
- ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished));
- EXPECT_FALSE(finished);
+ ASSERT_TRUE(elf.StepIfSignalHandler(0x3000, ®s, &process_memory));
+ EXPECT_EQ(ERROR_NONE, elf.GetLastErrorCode());
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
}
@@ -309,7 +312,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 ""; }
@@ -336,7 +339,7 @@
EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1000, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, get_global_invalid_elf) {
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/RegsFake.h b/libunwindstack/tests/RegsFake.h
index d6ca9b7..207d46e 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -23,6 +23,8 @@
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
+#include "Check.h"
+
namespace unwindstack {
class RegsFake : public Regs {
@@ -47,7 +49,10 @@
void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
- bool Is32Bit() { return false; }
+ bool Is32Bit() {
+ CHECK(fake_arch_ != ARCH_UNKNOWN);
+ return fake_arch_ == ARCH_ARM || fake_arch_ == ARCH_X86 || fake_arch_ == ARCH_MIPS;
+ }
uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index b5feb38..6c64c40 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -204,6 +204,7 @@
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
+ std::unique_ptr<Regs> regs_copy(regs_->Clone());
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -223,6 +224,22 @@
EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+
+ // Display build ids now.
+ unwinder.SetRegs(regs_copy.get());
+ unwinder.SetDisplayBuildID(true);
+ unwinder.Unwind();
+
+ frame_info = DumpFrames(unwinder);
+ ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0001a9f8 libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
+ " #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
+ frame_info);
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
@@ -300,7 +317,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 +618,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"
@@ -1198,7 +1215,7 @@
" #02 pc 0032bff3 libunwindstack_test (SignalOuterFunction+2)\n"
" #03 pc 0032fed3 libunwindstack_test "
"(unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)\n"
- " #04 pc 00026528 libc.so\n"
+ " #04 pc 0002652c libc.so (__restore)\n"
" #05 pc 00000000 <unknown>\n"
" #06 pc 0032c2d9 libunwindstack_test (InnerFunction+736)\n"
" #07 pc 0032cc4f libunwindstack_test (MiddleFunction+42)\n"
@@ -1226,7 +1243,7 @@
EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
- EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xf413652cU, unwinder.frames()[4].pc);
EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
EXPECT_EQ(0U, unwinder.frames()[5].pc);
EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
@@ -1309,10 +1326,11 @@
" #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
" #01 pc 000000000005426c linker64 "
"(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
- " #02 pc 00000000000008bc vdso.so\n"
+ " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\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);
@@ -1320,7 +1338,7 @@
EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
- EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
@@ -1348,7 +1366,7 @@
" #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
" #01 pc 000000000005426c linker64 "
"(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
- " #02 pc 00000000000008bc vdso.so\n"
+ " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\n"
" #03 pc 00000000000846f4 libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
" #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x21d5000)\n"
@@ -1359,7 +1377,7 @@
EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
- EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
@@ -1372,4 +1390,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..48e038e 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);
}
}
@@ -58,7 +58,9 @@
maps_.reset(new Maps);
ElfFake* elf = new ElfFake(new MemoryFake);
- elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+ interface_fake->FakeSetBuildID("FAKE");
+ elf->FakeSetInterface(interface_fake);
AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
@@ -79,13 +81,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 +105,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 +331,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 +850,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);
@@ -1067,7 +1104,15 @@
}
// Verify format frame code.
-TEST_F(UnwinderTest, format_frame_static) {
+TEST_F(UnwinderTest, format_frame) {
+ RegsFake regs_arm(10);
+ regs_arm.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder32(10, maps_.get(), ®s_arm, process_memory_);
+
+ RegsFake regs_arm64(10);
+ regs_arm64.FakeSetArch(ARCH_ARM64);
+ Unwinder unwinder64(10, maps_.get(), ®s_arm64, process_memory_);
+
FrameData frame;
frame.num = 1;
frame.rel_pc = 0x1000;
@@ -1082,39 +1127,61 @@
frame.map_flags = PROT_READ;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (offset 0x2000) (function+100)",
- Unwinder::FormatFrame(frame, false));
+ unwinder64.FormatFrame(frame));
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (offset 0x2000) (function+100)",
- Unwinder::FormatFrame(frame, true));
+ unwinder32.FormatFrame(frame));
frame.map_elf_start_offset = 0;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)",
- Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)",
- Unwinder::FormatFrame(frame, true));
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame));
frame.function_offset = 0;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function)",
- Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", unwinder32.FormatFrame(frame));
// Verify the function name is demangled.
frame.function_name = "_ZN4funcEv";
- EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (func())",
- Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (func())", Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (func())", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (func())", unwinder32.FormatFrame(frame));
frame.function_name = "";
- EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", unwinder32.FormatFrame(frame));
frame.map_name = "";
- EXPECT_EQ(" #01 pc 0000000000001000 <anonymous:3000>", Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 <anonymous:3000>", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", unwinder32.FormatFrame(frame));
frame.map_start = 0;
frame.map_end = 0;
- EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 <unknown>", Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 <unknown>", unwinder32.FormatFrame(frame));
+}
+
+TEST_F(UnwinderTest, format_frame_build_id) {
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder(10, maps_.get(), ®s, process_memory_);
+
+ FrameData frame;
+ frame.num = 1;
+ frame.rel_pc = 0x1000;
+ frame.pc = 0x4000;
+ frame.sp = 0x1000;
+ frame.function_name = "function";
+ frame.function_offset = 100;
+ frame.map_name = "/fake/libfake.so";
+ frame.map_elf_start_offset = 0;
+ frame.map_start = 0x3000;
+ frame.map_end = 0x6000;
+ frame.map_flags = PROT_READ;
+
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder.FormatFrame(frame));
+ unwinder.SetDisplayBuildID(true);
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100) (BuildId: 46414b45)",
+ unwinder.FormatFrame(frame));
}
static std::string ArchToString(ArchEnum arch) {
@@ -1132,7 +1199,7 @@
}
// Verify format frame code.
-TEST_F(UnwinderTest, format_frame) {
+TEST_F(UnwinderTest, format_frame_by_arch) {
std::vector<Regs*> reg_list;
RegsArm* arm = new RegsArm;
arm->set_pc(0x2300);
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/libutils/Android.bp b/libutils/Android.bp
index 93aa1e6..4f194c7 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -69,6 +69,7 @@
],
shared_libs: [
+ "libcutils",
"liblog",
],
@@ -83,7 +84,6 @@
cflags: ["-fvisibility=protected"],
shared_libs: [
- "libcutils",
"libprocessgroup",
"libdl",
"libvndksupport",
@@ -98,14 +98,6 @@
exclude_shared_libs: ["libvndksupport"],
},
- host: {
- cflags: ["-DLIBUTILS_NATIVE=1"],
-
- shared: {
- enabled: false,
- },
- },
-
linux_bionic: {
enabled: true,
},
@@ -180,18 +172,23 @@
},
},
+ shared_libs: [
+ "libutils",
+ "libbacktrace",
+ ],
+
target: {
- android: {
- shared_libs: [
- "libutils",
- "libbacktrace",
- ],
- },
linux: {
srcs: [
"ProcessCallStack.cpp",
],
},
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
},
}
@@ -205,7 +202,6 @@
"LruCache_test.cpp",
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
- "Singleton_test.cpp",
"String8_test.cpp",
"StrongPointer_test.cpp",
"Unicode_test.cpp",
@@ -240,17 +236,33 @@
},
},
- required: [
- "libutils_test_singleton1",
- "libutils_test_singleton2",
- ],
-
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wthread-safety",
],
+
+ test_suites: ["device-tests"],
+}
+
+// TODO: the test infrastructure isn't yet capable of running this,
+// so it's broken out into its own test so that the main libutils_tests
+// can be in presubmit even if this can't.
+
+cc_test {
+ name: "libutils_singleton_test",
+ srcs: ["Singleton_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: ["libbase"],
+
+ required: [
+ ":libutils_test_singleton1",
+ ":libutils_test_singleton2",
+ ],
}
cc_test_library {
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index b3f943d..2d696eb 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -76,8 +76,8 @@
}
void Looper::initTLSKey() {
- int result = pthread_key_create(& gTLSKey, threadDestructor);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+ int error = pthread_key_create(&gTLSKey, threadDestructor);
+ LOG_ALWAYS_FATAL_IF(error != 0, "Could not allocate TLS key: %s", strerror(error));
}
void Looper::threadDestructor(void *st) {
@@ -399,8 +399,8 @@
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
- LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s", mWakeEventFd.get(),
- strerror(errno));
+ LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
+ mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
diff --git a/libutils/Mutex_test.cpp b/libutils/Mutex_test.cpp
index 8a1805f..79f4302 100644
--- a/libutils/Mutex_test.cpp
+++ b/libutils/Mutex_test.cpp
@@ -29,4 +29,20 @@
android::Mutex::Autolock _l(mLock);
i = 0;
modifyLockedVariable();
-}
\ No newline at end of file
+}
+
+TEST(Mutex, tryLock) {
+ if (mLock.tryLock() != 0) {
+ return;
+ }
+ mLock.unlock();
+}
+
+#if defined(__ANDROID__)
+TEST(Mutex, timedLock) {
+ if (mLock.timedLock(1) != 0) {
+ return;
+ }
+ mLock.unlock();
+}
+#endif
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/Mutex.h b/libutils/include/utils/Mutex.h
index 29c2e8c..1325bf3 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -108,7 +108,7 @@
void unlock() RELEASE();
// lock if possible; returns 0 on success, error otherwise
- status_t tryLock() TRY_ACQUIRE(true);
+ status_t tryLock() TRY_ACQUIRE(0);
#if defined(__ANDROID__)
// Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -122,7 +122,7 @@
// which is subject to NTP adjustments, and includes time during suspend,
// so a timeout may occur even though no processes could run.
// Not holding a partial wakelock may lead to a system suspend.
- status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(true);
+ status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(0);
#endif
// Manages the mutex automatically. It'll be locked when Autolock is
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/Android.bp b/libvndksupport/Android.bp
index e73b366..546c15c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -9,7 +9,15 @@
],
local_include_dirs: ["include/vndksupport"],
export_include_dirs: ["include"],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "libdl_android",
+ "liblog",
+ ],
+ version_script: "libvndksupport.map.txt",
+ stubs: {
+ symbol_file: "libvndksupport.map.txt",
+ versions: ["29"],
+ },
}
llndk_library {
diff --git a/libvndksupport/include/vndksupport/linker.h b/libvndksupport/include/vndksupport/linker.h
index f509564..5f48c39 100644
--- a/libvndksupport/include/vndksupport/linker.h
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -20,6 +20,16 @@
extern "C" {
#endif
+/*
+ * Returns whether the current process is a vendor process.
+ *
+ * Note that this is only checking what process is running and has nothing to
+ * do with what namespace the caller is loaded at. For example, a VNDK-SP
+ * library loaded by SP-HAL calling this function may still get a 'false',
+ * because it is running in a system process.
+ */
+int android_is_in_vendor_process();
+
void* android_load_sphal_library(const char* name, int flag);
int android_unload_sphal_library(void* handle);
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index 16e38da..ac9a99c 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,7 +1,8 @@
LIBVNDKSUPPORT {
global:
- android_load_sphal_library; # vndk
- android_unload_sphal_library; # vndk
+ android_is_in_vendor_process; # vndk apex
+ android_load_sphal_library; # vndk apex
+ android_unload_sphal_library; # vndk apex
local:
*;
};
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index bc5620b..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*);
@@ -45,6 +47,22 @@
return vendor_namespace;
}
+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;
+ }
+
+ // In vendor process, 'vndk' namespace is not visible, whereas in system
+ // process, it is.
+ return android_get_exported_namespace("vndk") == NULL;
+}
+
void* android_load_sphal_library(const char* name, int flag) {
struct android_namespace_t* vendor_namespace = get_vendor_namespace();
if (vendor_namespace != NULL) {
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 9538bba..bc1543b 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -38,6 +38,19 @@
],
},
},
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ "shift",
+ "integer-divide-by-zero",
+ "implicit-signed-integer-truncation",
+ // TODO: Fix crash when we enable this option
+ // "implicit-unsigned-integer-truncation",
+ // TODO: not tested yet.
+ // "implicit-integer-sign-change",
+ ],
+ },
}
cc_defaults {
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index 5fc2fb4..1714586 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -35,7 +35,7 @@
return false;
} else {
// 2-5 byte sequences.
- for (uint8_t first = byte << 1; first & 0x80; first <<= 1) {
+ for (uint8_t first = (byte & 0x7f) << 1; first & 0x80; first = (first & 0x7f) << 1) {
++i;
// Missing continuation byte..
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 6756007..cc059d8 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -17,6 +17,7 @@
#include <errno.h>
#include <error.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <getopt.h>
#include <inttypes.h>
#include <stdio.h>
@@ -52,9 +53,21 @@
static uint64_t total_compressed_length = 0;
static size_t file_count = 0;
-static bool Filter(const std::string& name) {
- if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
- if (!includes.empty() && includes.find(name) == includes.end()) return true;
+static bool ShouldInclude(const std::string& name) {
+ // Explicitly excluded?
+ if (!excludes.empty()) {
+ for (const auto& exclude : excludes) {
+ if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
+ }
+ }
+
+ // Implicitly included?
+ if (includes.empty()) return true;
+
+ // Explicitly included?
+ for (const auto& include : includes) {
+ if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
+ }
return false;
}
@@ -245,7 +258,7 @@
ZipString string;
while ((err = Next(cookie, &entry, &string)) >= 0) {
std::string name(string.name, string.name + string.name_length);
- if (!Filter(name)) ProcessOne(zah, entry, name);
+ if (ShouldInclude(name)) ProcessOne(zah, entry, name);
}
if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
@@ -260,7 +273,8 @@
printf(
"\n"
- "Extract FILEs from ZIP archive. Default is all files.\n"
+ "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+ "exclude (-x) lists use shell glob patterns.\n"
"\n"
"-d DIR Extract into DIR\n"
"-l List contents (-lq excludes archive name, -lv is verbose)\n"
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 6b9f6e1..e1ec47a 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -102,21 +102,8 @@
}
static uint32_t ComputeHash(const ZipString& name) {
-#if !defined(_WIN32)
- return std::hash<std::string_view>{}(
- std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
-#else
- // Remove this code path once the windows compiler knows how to compile the above statement.
- uint32_t hash = 0;
- uint16_t len = name.name_length;
- const uint8_t* str = name.name;
-
- while (len--) {
- hash = hash * 31 + *str++;
- }
-
- return hash;
-#endif
+ return static_cast<uint32_t>(std::hash<std::string_view>{}(
+ std::string_view(reinterpret_cast<const char*>(name.name), name.name_length)));
}
static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
@@ -278,11 +265,6 @@
if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
-#if defined(__ANDROID__)
- if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
- android_errorWriteLog(0x534e4554, "31251826");
- }
-#endif
return kInvalidOffset;
}
if (eocd->num_records == 0) {
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 981df3a..0df0fa5 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -30,9 +30,13 @@
#include "entry_name_utils-inl.h"
#include "zip_archive_common.h"
-#if !defined(powerof2)
-#define powerof2(x) ((((x)-1) & (x)) == 0)
-#endif
+#undef powerof2
+#define powerof2(x) \
+ ({ \
+ __typeof__(x) _x = (x); \
+ __typeof__(x) _x2; \
+ __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
+ })
/* Zip compression methods we support */
enum {
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index c284273..7322afb 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -257,7 +257,7 @@
std::vector<uint8_t> buffer(kBufSize);
size_t prev = 1;
for (size_t i = 0; i < kBufSize; i++) {
- buffer[i] = i + prev;
+ buffer[i] = static_cast<uint8_t>(i + prev);
prev = i;
}
diff --git a/lmkd/event.logtags b/lmkd/event.logtags
index 7c2cd18..065c6db 100644
--- a/lmkd/event.logtags
+++ b/lmkd/event.logtags
@@ -35,4 +35,4 @@
# TODO: generate ".java" and ".h" files with integer constants from this file.
# for meminfo logs
-10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
+10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index e72d159..59377dd 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -50,7 +50,7 @@
typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
/* Get LMKD packet command */
-inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+static inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
return (enum lmk_cmd)ntohl(pack[0]);
}
@@ -64,8 +64,8 @@
* For LMK_TARGET packet get target_idx-th payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
- int target_idx, struct lmk_target *target) {
+static inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet, int target_idx,
+ struct lmk_target* target) {
target->minfree = ntohl(packet[target_idx * 2 + 1]);
target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
}
@@ -74,9 +74,8 @@
* Prepare LMK_TARGET packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
- struct lmk_target *targets,
- size_t target_cnt) {
+static inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet, struct lmk_target* targets,
+ size_t target_cnt) {
int idx = 0;
packet[idx++] = htonl(LMK_TARGET);
while (target_cnt) {
@@ -99,8 +98,7 @@
* For LMK_PROCPRIO packet get its payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
- struct lmk_procprio *params) {
+static inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet, struct lmk_procprio* params) {
params->pid = (pid_t)ntohl(packet[1]);
params->uid = (uid_t)ntohl(packet[2]);
params->oomadj = ntohl(packet[3]);
@@ -110,8 +108,7 @@
* Prepare LMK_PROCPRIO packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
- struct lmk_procprio *params) {
+static inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet, struct lmk_procprio* params) {
packet[0] = htonl(LMK_PROCPRIO);
packet[1] = htonl(params->pid);
packet[2] = htonl(params->uid);
@@ -128,8 +125,8 @@
* For LMK_PROCREMOVE packet get its payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
- struct lmk_procremove *params) {
+static inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procremove* params) {
params->pid = (pid_t)ntohl(packet[1]);
}
@@ -137,8 +134,8 @@
* Prepare LMK_PROCREMOVE packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
- struct lmk_procprio *params) {
+static inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio* params) {
packet[0] = htonl(LMK_PROCREMOVE);
packet[1] = htonl(params->pid);
return 2 * sizeof(int);
@@ -148,7 +145,7 @@
* Prepare LMK_PROCPURGE packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+static inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
packet[0] = htonl(LMK_PROCPURGE);
return sizeof(int);
}
@@ -163,8 +160,8 @@
* For LMK_GETKILLCNT packet get its payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
- struct lmk_getkillcnt *params) {
+static inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
+ struct lmk_getkillcnt* params) {
params->min_oomadj = ntohl(packet[1]);
params->max_oomadj = ntohl(packet[2]);
}
@@ -173,8 +170,8 @@
* Prepare LMK_GETKILLCNT packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
- struct lmk_getkillcnt *params) {
+static inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
+ struct lmk_getkillcnt* params) {
packet[0] = htonl(LMK_GETKILLCNT);
packet[1] = htonl(params->min_oomadj);
packet[2] = htonl(params->max_oomadj);
@@ -185,7 +182,7 @@
* Prepare LMK_GETKILLCNT reply packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
+static inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
packet[0] = htonl(LMK_GETKILLCNT);
packet[1] = htonl(kill_cnt);
return 2 * sizeof(int);
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 562e578..2de7378 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 10
/* 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) {
@@ -1694,16 +1699,6 @@
static unsigned long report_skip_count = 0;
if (!use_minfree_levels) {
- /* If pressure level is less than critical and enough free swap then ignore */
- if (level < VMPRESS_LEVEL_CRITICAL &&
- mi.field.free_swap > low_pressure_mem.max_nr_free_pages) {
- if (debug_process_killing) {
- ALOGI("Ignoring pressure since %" PRId64
- " swap pages are available ",
- mi.field.free_swap);
- }
- return;
- }
/* Free up enough memory to downgrate the memory pressure to low level */
if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
if (debug_process_killing) {
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/logcat/event.logtags b/logcat/event.logtags
index da8d2d4..3a1d36f 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -116,8 +116,8 @@
# audio
# 61000 - 61199 reserved for audioserver
-# 0 for screen off, 1 for screen on, 2 for key-guard done
-70000 screen_toggled (screen_state|1|5)
+# com.android.server.policy
+# 70000 - 70199 reserved for PhoneWindowManager and other policies
# aggregation service
70200 aggregation (aggregation time|2|3)
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index d5c40be..b32b437 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -33,6 +33,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <gtest/gtest.h>
#include <log/event_tag_map.h>
#include <log/log.h>
@@ -1747,3 +1748,13 @@
EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
#endif
}
+
+TEST(logcat, invalid_buffer) {
+ FILE* fp = popen("logcat -b foo 2>&1", "r");
+ ASSERT_NE(nullptr, fp);
+ std::string output;
+ ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &output));
+ pclose(fp);
+
+ ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
+}
diff --git a/logd/Android.bp b/logd/Android.bp
index 360f2fe..9b86258 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -80,6 +80,24 @@
cflags: ["-Werror"],
}
+cc_binary {
+ name: "auditctl",
+
+ srcs: ["auditctl.cpp"],
+
+ static_libs: [
+ "liblogd",
+ ],
+
+ shared_libs: ["libbase"],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wconversion"
+ ],
+}
prebuilt_etc {
name: "logtagd.rc",
diff --git a/logd/auditctl.cpp b/logd/auditctl.cpp
new file mode 100644
index 0000000..98bb02d
--- /dev/null
+++ b/logd/auditctl.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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/parseint.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "libaudit.h"
+
+static void usage(const char* cmdline) {
+ fprintf(stderr, "Usage: %s [-r rate]\n", cmdline);
+}
+
+static void do_update_rate(uint32_t rate) {
+ int fd = audit_open();
+ if (fd == -1) {
+ error(EXIT_FAILURE, errno, "Unable to open audit socket");
+ }
+ int result = audit_rate_limit(fd, rate);
+ close(fd);
+ if (result < 0) {
+ fprintf(stderr, "Can't update audit rate limit: %d\n", result);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(int argc, char* argv[]) {
+ uint32_t rate = 0;
+ bool update_rate = false;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "r:")) != -1) {
+ switch (opt) {
+ case 'r':
+ if (!android::base::ParseUint<uint32_t>(optarg, &rate)) {
+ error(EXIT_FAILURE, errno, "Invalid Rate");
+ }
+ update_rate = true;
+ break;
+ default: /* '?' */
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // In the future, we may add other options to auditctl
+ // so this if statement will expand.
+ // if (!update_rate && !update_backlog && !update_whatever) ...
+ if (!update_rate) {
+ fprintf(stderr, "Nothing to do\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (update_rate) {
+ do_update_rate(rate);
+ }
+
+ return 0;
+}
diff --git a/logd/libaudit.c b/logd/libaudit.c
index 9d9a857..f452c71 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.c
@@ -160,8 +160,7 @@
* and the the mask set to AUDIT_STATUS_PID
*/
status.pid = pid;
- status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
- status.rate_limit = AUDIT_RATE_LIMIT; /* audit entries per second */
+ status.mask = AUDIT_STATUS_PID;
/* Let the kernel know this pid will be registering for audit events */
rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@@ -188,6 +187,14 @@
return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
}
+int audit_rate_limit(int fd, uint32_t limit) {
+ struct audit_status status;
+ memset(&status, 0, sizeof(status));
+ status.mask = AUDIT_STATUS_RATE_LIMIT;
+ status.rate_limit = limit; /* audit entries per second */
+ return audit_send(fd, AUDIT_SET, &status, sizeof(status));
+}
+
int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
ssize_t len;
int flags;
diff --git a/logd/libaudit.h b/logd/libaudit.h
index 2a93ea3..b4a92a8 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -89,8 +89,17 @@
*/
extern int audit_setup(int fd, pid_t pid);
-/* Max audit messages per second */
-#define AUDIT_RATE_LIMIT 5
+/**
+ * Throttle kernel messages at the provided rate
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param rate
+ * The rate, in messages per second, above which the kernel
+ * should drop audit messages.
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
+extern int audit_rate_limit(int fd, uint32_t limit);
__END_DECLS
diff --git a/logd/logd.rc b/logd/logd.rc
index c740ecf..438419a 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -16,8 +16,19 @@
group logd
writepid /dev/cpuset/system-background/tasks
+# Limit SELinux denial generation to 5/second
+service logd-auditctl /system/bin/auditctl -r 5
+ oneshot
+ disabled
+ user logd
+ group logd
+ capabilities AUDIT_CONTROL
+
on fs
write /dev/event-log-tags "# content owned by logd
"
chown logd logd /dev/event-log-tags
chmod 0644 /dev/event-log-tags
+
+on property:sys.boot_completed=1
+ start logd-auditctl
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 7d7a22f..447b067 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -39,7 +39,6 @@
#endif
#include "../LogReader.h" // pickup LOGD_SNDTIMEO
-#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
@@ -1065,145 +1064,3 @@
TEST(logd, multiple_test_10) {
__android_log_btwrite_multiple__helper(10);
}
-
-#ifdef __ANDROID__
-// returns violating pid
-static pid_t sepolicy_rate(unsigned rate, unsigned num) {
- pid_t pid = fork();
-
- if (pid) {
- siginfo_t info = {};
- if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return -1;
- if (info.si_status) return -1;
- return pid;
- }
-
- // We may have DAC, but let's not have MAC
- if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
- int save_errno = errno;
- security_context_t context;
- getcon(&context);
- if (strcmp(context, "u:r:shell:s0")) {
- fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
- context, strerror(save_errno));
- freecon(context);
- _exit(-1);
- // NOTREACHED
- return -1;
- }
- }
-
- // The key here is we are root, but we are in u:r:shell:s0,
- // and the directory does not provide us DAC access
- // (eg: 0700 system system) so we trigger the pair dac_override
- // and dac_read_search on every try to get past the message
- // de-duper. We will also rotate the file name in the directory
- // as another measure.
- static const char file[] = "/data/drm/cannot_access_directory_%u";
- static const unsigned avc_requests_per_access = 2;
-
- rate /= avc_requests_per_access;
- useconds_t usec;
- if (rate == 0) {
- rate = 1;
- usec = 2000000;
- } else {
- usec = (1000000 + (rate / 2)) / rate;
- }
- num = (num + (avc_requests_per_access / 2)) / avc_requests_per_access;
-
- if (usec < 2) usec = 2;
-
- while (num > 0) {
- if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
- _exit(-1);
- // NOTREACHED
- return -1;
- }
- usleep(usec);
- --num;
- }
- _exit(0);
- // NOTREACHED
- return -1;
-}
-
-static constexpr int background_period = 10;
-
-static int count_avc(pid_t pid) {
- int count = 0;
-
- // pid=-1 skip as pid is in error
- if (pid == (pid_t)-1) return count;
-
- // pid=0 means we want to report the background count of avc: activities
- struct logger_list* logger_list =
- pid ? android_logger_list_alloc(
- ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)
- : android_logger_list_alloc_time(
- ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- log_time(android_log_clockid()) -
- log_time(background_period, 0),
- 0);
- if (!logger_list) return count;
- struct logger* logger = android_logger_open(logger_list, LOG_ID_EVENTS);
- if (!logger) {
- android_logger_list_close(logger_list);
- return count;
- }
- for (;;) {
- log_msg log_msg;
-
- if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
- if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
- (log_msg.id() != LOG_ID_EVENTS))
- continue;
-
- char* eventData = log_msg.msg();
- if (!eventData) continue;
-
- uint32_t tag = get4LE(eventData);
- if (tag != AUDITD_LOG_TAG) continue;
-
- if (eventData[4] != EVENT_TYPE_STRING) continue;
-
- // int len = get4LE(eventData + 4 + 1);
- log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
- const char* cp = strstr(eventData + 4 + 1 + 4, "): avc: denied");
- if (!cp) continue;
-
- ++count;
- }
-
- android_logger_list_close(logger_list);
-
- return count;
-}
-#endif
-
-TEST(logd, sepolicy_rate_limiter) {
-#ifdef __ANDROID__
- int background_selinux_activity_too_high = count_avc(0);
- if (background_selinux_activity_too_high > 2) {
- GTEST_LOG_(ERROR) << "Too much background selinux activity "
- << background_selinux_activity_too_high * 60 /
- background_period
- << "/minute on the device, this test\n"
- << "can not measure the functionality of the "
- << "sepolicy rate limiter. Expect test to\n"
- << "fail as this device is in a bad state, "
- << "but is not strictly a unit test failure.";
- }
-
- static const int rate = AUDIT_RATE_LIMIT;
- static const int duration = 2;
- // Two seconds of sustained denials. Depending on the overlap in the time
- // window that the kernel is considering vs what this test is considering,
- // allow some additional denials to prevent a flaky test.
- EXPECT_LE(count_avc(sepolicy_rate(rate, rate * duration)),
- rate * duration + rate);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
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 cbbc710..462ae8b 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_REQUIRED_MODULES := fsverity_init
# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
# Since init.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
@@ -24,48 +25,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
-
-# End of runtime APEX compatibilty.
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# cgroups.json
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := cgroups.json
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# task_profiles.json
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := task_profiles.json
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-
include $(BUILD_PREBUILT)
#######################################
@@ -99,6 +58,15 @@
endif
#######################################
+# fsverity_init
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= fsverity_init
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := fsverity_init.sh
+include $(BUILD_PREBUILT)
+
+#######################################
# init.environ.rc
include $(CLEAR_VARS)
@@ -121,7 +89,7 @@
EXPORT_GLOBAL_GCOV_OPTIONS :=
ifeq ($(NATIVE_COVERAGE),true)
- EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+ EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
endif
# Put it here instead of in init.rc module definition,
@@ -195,6 +163,7 @@
@echo "Generate: $< -> $@"
@mkdir -p $(dir $@)
$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
+ $(hide) sed -i -e 's?%DEX2OATBOOTCLASSPATH%?$(PRODUCT_DEX2OAT_BOOTCLASSPATH)?g' $@
$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
@@ -245,6 +214,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
@@ -368,3 +376,5 @@
$(hide) echo -n > $@
$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
echo $(lib).so >> $@;)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
new file mode 100644
index 0000000..5dc019c
--- /dev/null
+++ b/rootdir/avb/Android.mk
@@ -0,0 +1,46 @@
+LOCAL_PATH:= $(call my-dir)
+
+#######################################
+# q-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := q-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# r-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# s-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
diff --git a/rootdir/avb/q-gsi.avbpubkey b/rootdir/avb/q-gsi.avbpubkey
new file mode 100644
index 0000000..5ed7543
--- /dev/null
+++ b/rootdir/avb/q-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/r-gsi.avbpubkey b/rootdir/avb/r-gsi.avbpubkey
new file mode 100644
index 0000000..2609b30
--- /dev/null
+++ b/rootdir/avb/r-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-gsi.avbpubkey b/rootdir/avb/s-gsi.avbpubkey
new file mode 100644
index 0000000..9065fb8
--- /dev/null
+++ b/rootdir/avb/s-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
new file mode 100644
index 0000000..e4d3d5e
--- /dev/null
+++ b/rootdir/etc/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBionicTestCases"
+ }
+ ]
+}
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 48ca998..8792671 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -14,6 +14,10 @@
# Except for /postinstall, where only /system and /product are searched
dir.postinstall = /postinstall
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.legacy = /data
+
[legacy]
namespace.default.isolated = false
@@ -40,7 +44,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.
@@ -49,11 +53,14 @@
# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
# libart.
namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libdexfile_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -72,8 +79,10 @@
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
-# when it exists.
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -87,12 +96,25 @@
namespace.media.search.paths = /apex/com.android.media/${LIB}
namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+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 += libcgrouprc.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
@@ -106,10 +128,11 @@
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
-namespace.conscrypt.link.runtime.shared_libs = libjavacore.so
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
namespace.conscrypt.link.default.shared_libs = libc.so
namespace.conscrypt.link.default.shared_libs += libm.so
namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
###############################################################################
# "resolv" APEX namespace
@@ -123,9 +146,12 @@
namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
namespace.resolv.links = default
namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
###############################################################################
# Namespace config for binaries under /postinstall.
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 7aa097d..b486411 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -20,16 +20,19 @@
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
-dir.system = /data/nativetest
-dir.system = /data/nativetest64
-dir.system = /data/benchmarktest
-dir.system = /data/benchmarktest64
-# TODO(b/123864775): Ensure tests are run from one of the directories above and
-# remove this.
-dir.system = /data/local/tmp
+dir.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
dir.postinstall = /postinstall
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
[system]
additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
@@ -64,6 +67,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
@@ -76,7 +82,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}
@@ -96,6 +102,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
@@ -109,13 +118,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.
@@ -123,11 +132,14 @@
# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
# libart.
namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libdexfile_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -146,8 +158,10 @@
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
-# when it exists.
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -161,6 +175,8 @@
namespace.media.search.paths = /apex/com.android.media/${LIB}
namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+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
@@ -180,10 +196,11 @@
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
-namespace.conscrypt.link.runtime.shared_libs = libjavacore.so
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
namespace.conscrypt.link.default.shared_libs = libc.so
namespace.conscrypt.link.default.shared_libs += libm.so
namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
###############################################################################
# "resolv" APEX namespace
@@ -197,9 +214,12 @@
namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
namespace.resolv.links = default
namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
###############################################################################
# "sphal" namespace
@@ -223,6 +243,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}
@@ -235,17 +256,19 @@
namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
###############################################################################
# "rs" namespace
#
@@ -265,6 +288,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
@@ -310,6 +334,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
@@ -354,7 +380,7 @@
# (LL-NDK only) access.
###############################################################################
[vendor]
-additional.namespaces = runtime,system,vndk
+additional.namespaces = runtime,system,vndk%VNDK_IN_SYSTEM_NS%
###############################################################################
# "default" namespace
@@ -377,6 +403,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
@@ -394,8 +421,9 @@
namespace.default.asan.permitted.paths += /data/asan/vendor
namespace.default.asan.permitted.paths += /vendor
-namespace.default.links = system,vndk
+namespace.default.links = system,vndk%VNDK_IN_SYSTEM_NS%
namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
namespace.default.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
@@ -408,9 +436,10 @@
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = system
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
-# when it exists.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.system.allow_all_shared_libs = true
###############################################################################
@@ -443,14 +472,18 @@
# When these NDK libs are required inside this namespace, then it is redirected
# to the system namespace. This is possible since their ABI is stable across
-# Android releases.
-namespace.vndk.links = system,default
+# Android releases. The links here should be identical to that of the
+# 'vndk_in_system' namespace, except for the link between 'vndk' and
+# 'vndk_in_system'.
+namespace.vndk.links = system,default%VNDK_IN_SYSTEM_NS%
namespace.vndk.link.system.shared_libs = %LLNDK_LIBRARIES%
namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.vndk.link.default.allow_all_shared_libs = true
+namespace.vndk.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
###############################################################################
# "system" namespace
#
@@ -475,6 +508,160 @@
namespace.system.link.runtime.shared_libs += libnativebridge.so
namespace.system.link.runtime.shared_libs += libnativehelper.so
namespace.system.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.system.link.runtime.shared_libs += libandroidicu.so
+
+###############################################################################
+# "vndk_in_system" namespace
+#
+# This namespace is where no-vendor-variant VNDK libraries are loaded for a
+# vendor process. Note that we do not simply export these libraries from
+# "system" namespace, because in some case both the core variant and the
+# vendor variant of a VNDK library may be loaded. In such case, we do not
+# want to eliminate double-loading because doing so means the global states
+# of the library would be shared.
+#
+# Only the no-vendor-variant VNDK libraries are whitelisted in this namespace.
+# This is to ensure that we do not load libraries needed by no-vendor-variant
+# VNDK libraries into vndk_in_system namespace.
+###############################################################################
+namespace.vndk_in_system.isolated = true
+namespace.vndk_in_system.visible = true
+
+# The search paths here should be kept the same as that of the 'system'
+# namespace.
+namespace.vndk_in_system.search.paths = /system/${LIB}
+namespace.vndk_in_system.search.paths += /%PRODUCT%/${LIB}
+namespace.vndk_in_system.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.vndk_in_system.asan.search.paths = /data/asan/system/${LIB}
+namespace.vndk_in_system.asan.search.paths += /system/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/product/${LIB}
+namespace.vndk_in_system.asan.search.paths += /%PRODUCT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.vndk_in_system.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.vndk_in_system.whitelisted = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
+# The links here should be identical to that of the 'vndk' namespace, with the
+# following exception:
+# 1. 'vndk_in_system' needs to be freely linked back to 'vndk'.
+# 2. 'vndk_in_system' does not need to link to 'default', as any library that
+# requires anything vendor would not be a vndk_in_system library.
+namespace.vndk_in_system.links = vndk,system
+
+namespace.vndk_in_system.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.vndk_in_system.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk_in_system.link.vndk.allow_all_shared_libs = true
+
+
+###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv
+
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths = /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime,resolv
+namespace.default.visible = true
+
+namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+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 += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.link.default.shared_libs = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+
###############################################################################
# Namespace config for binaries under /postinstall.
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 1904445..12007dc 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -20,13 +20,19 @@
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
-dir.system = /data/nativetest
-dir.system = /data/nativetest64
-dir.system = /data/benchmarktest
-dir.system = /data/benchmarktest64
+dir.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
dir.postinstall = /postinstall
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
[system]
additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
@@ -59,7 +65,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.
@@ -67,11 +73,14 @@
# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
# libart.
namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libart.so:libartd.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libdexfile_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -91,8 +100,10 @@
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
-# when it exists.
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -106,6 +117,8 @@
namespace.media.search.paths = /apex/com.android.media/${LIB}
namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+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
@@ -125,10 +138,11 @@
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
-namespace.conscrypt.link.runtime.shared_libs = libjavacore.so
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
namespace.conscrypt.link.default.shared_libs = libc.so
namespace.conscrypt.link.default.shared_libs += libm.so
namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
###############################################################################
# "resolv" APEX namespace
@@ -142,9 +156,12 @@
namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
namespace.resolv.links = default
namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
###############################################################################
# "sphal" namespace
@@ -168,6 +185,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}
@@ -180,17 +198,19 @@
namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
###############################################################################
# "rs" namespace
#
@@ -210,6 +230,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
@@ -255,6 +276,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
@@ -305,11 +327,11 @@
namespace.default.search.paths += /vendor/${LIB}/vndk-sp
# Access to system libraries is allowed
-namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
@@ -323,8 +345,6 @@
namespace.default.asan.search.paths += /vendor/${LIB}/vndk
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
namespace.default.asan.search.paths += /vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
-namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
@@ -333,12 +353,16 @@
namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.links = runtime
namespace.default.link.runtime.shared_libs = libdexfile_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.default.link.runtime.shared_libs += libandroidicu.so
###############################################################################
# "runtime" APEX namespace
@@ -349,12 +373,118 @@
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
-# when it exists.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv
+
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths = /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime,resolv
+namespace.default.visible = true
+
+namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+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 += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.link.default.shared_libs = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+
+###############################################################################
# Namespace config for binaries under /postinstall.
# Only default namespace is defined and default has no directories
# other than /system/lib in the search paths. This is because linker calls
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
new file mode 100644
index 0000000..4fee15f
--- /dev/null
+++ b/rootdir/fsverity_init.sh
@@ -0,0 +1,32 @@
+#!/system/bin/sh
+#
+# 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.
+#
+
+# Enforce fsverity signature checking
+echo 1 > /proc/sys/fs/verity/require_signatures
+
+# Load all keys
+for cert in /product/etc/security/fsverity/*.der; do
+ /system/bin/mini-keyctl padd asymmetric fsv_product .fs-verity < "$cert" ||
+ log -p e -t fsverity_init "Failed to load $cert"
+done
+
+DEBUGGABLE=$(getprop ro.debuggable)
+if [ $DEBUGGABLE != "1" ]; then
+ # Prevent future key links to .fs-verity keyring
+ /system/bin/mini-keyctl restrict_keyring .fs-verity ||
+ log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+fi
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index d10f7c1..455c9a8 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,14 +1,16 @@
# set up the global environment
-on init
+on early-init
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
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%
+ export DEX2OATBOOTCLASSPATH %DEX2OATBOOTCLASSPATH%
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
%EXPORT_GLOBAL_GCOV_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 57032bc..e94b8e2 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
@@ -84,6 +83,15 @@
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
+ # Create blkio tuning nodes
+ mkdir /dev/blkio/background
+ chown system system /dev/blkio
+ chown system system /dev/blkio/background
+ chown system system /dev/blkio/tasks
+ chown system system /dev/blkio/background/tasks
+ chmod 0664 /dev/blkio/tasks
+ chmod 0664 /dev/blkio/background/tasks
+
restorecon_recursive /mnt
mount configfs none /config nodev noexec nosuid
@@ -214,6 +222,9 @@
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
+ # make the PSI monitor accessible to others
+ chown system system /proc/pressure/memory
+ chmod 0664 /proc/pressure/memory
# qtaguid will limit access to specific data based on group memberships.
# net_bw_acct grants impersonation of socket owners.
@@ -269,18 +280,14 @@
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
+ # Allow system processes to read / write power state.
+ chown system system /sys/power/state
+ chown system system /sys/power/wakeup_count
+ chmod 0660 /sys/power/state
# 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
@@ -388,7 +395,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.
@@ -416,8 +426,20 @@
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/fsverity_init
+
+ # 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
@@ -534,12 +556,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/staging 0750 system system
-
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc
mkdir /data/nfc/param 0770 nfc nfc
@@ -572,6 +588,10 @@
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.
@@ -580,17 +600,6 @@
# 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
- # 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
-
# If there is no post-fs-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won't work.
@@ -648,6 +657,12 @@
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio 5
+ # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs
+ # to avoid power consumption when system becomes mostly idle. Be careful
+ # to make it too large, since it may bring userdata loss, if they
+ # are not aware of using fsync()/sync() to prepare sudden power-cut.
+ write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
+
# Permissions for System Server and daemons.
chown radio system /sys/android_power/state
chown radio system /sys/android_power/request_state
@@ -655,11 +670,8 @@
chown radio system /sys/android_power/acquire_partial_wake_lock
chown radio system /sys/android_power/release_wake_lock
chown system system /sys/power/autosleep
- chown system system /sys/power/state
- chown system system /sys/power/wakeup_count
chown radio wakelock /sys/power/wake_lock
chown radio wakelock /sys/power/wake_unlock
- chmod 0660 /sys/power/state
chmod 0660 /sys/power/wake_lock
chmod 0660 /sys/power/wake_unlock
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/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 852e234..f62c3df 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -23,6 +23,11 @@
lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
libz_is_llndk := $(strip $(libz_is_llndk))
+my_vndk_use_core_variant := $(TARGET_VNDK_USE_CORE_VARIANT)
+ifeq ($(lib_list_from_prebuilts),true)
+my_vndk_use_core_variant := false
+endif
+
compatibility_check_script := \
$(LOCAL_PATH)/ld_config_backward_compatibility_check.py
intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
@@ -35,6 +40,9 @@
vndksp_libraries_file := $(library_lists_dir)/vndksp.libraries.$(vndk_version).txt
vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.txt
vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.txt
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file := $(library_lists_dir)/vndk_using_core_variant.libraries.$(vndk_version).txt
+endif
sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
@@ -68,6 +76,9 @@
$(eval $(call write-libs-to-file,$(vndksp_libraries_list),$(vndksp_libraries_file)))
$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),$(vndkcore_libraries_file)))
$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),$(vndkprivate_libraries_file)))
+ifeq ($(my_vndk_use_core_variant),true)
+$(eval $(call write-libs-to-file,$(VNDK_USING_CORE_VARIANT_LIBRARIES),$(vndk_using_core_variant_libraries_file)))
+endif
endif # ifneq ($(lib_list_from_prebuilts),true)
# Given a file with a list of libs, filter-out the VNDK private libraries
@@ -94,6 +105,10 @@
ifeq ($(check_backward_compatibility),true)
deps += $(compatibility_check_script)
endif
+ifeq ($(my_vndk_use_core_variant),true)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE := $(vndk_using_core_variant_libraries_file)
+deps += $(vndk_using_core_variant_libraries_file)
+endif
$(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
@echo "Generate: $< -> $@"
@@ -109,6 +124,20 @@
$(call private-filter-out-private-libs,$(PRIVATE_VNDK_CORE_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)
$(hide) sed -i.bak -e "s?%VNDK_CORE_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)?g" $@
+ifeq ($(my_vndk_use_core_variant),true)
+ $(call private-filter-out-private-libs,$(PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)
+ $(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%?,vndk_in_system?g" $@
+ $(hide) sed -i.bak -e "s?%VNDK_USING_CORE_VARIANT_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)?g" $@
+else
+ $(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%??g" $@
+ # Unlike LLNDK or VNDK-SP, VNDK_USING_CORE_VARIANT_LIBRARIES can be nothing
+ # if TARGET_VNDK_USE_CORE_VARIANT is not set. In this case, we need to remove
+ # the entire line in the linker config so that we are not left with a line
+ # like:
+ # namespace.vndk.link.vndk_in_system.shared_libs =
+ $(hide) sed -i.bak -e 's?^.*= %VNDK_USING_CORE_VARIANT_LIBRARIES%$$??' $@
+endif
+
$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | \
xargs -n 1 -I privatelib bash -c "(grep privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk" && \
@@ -146,3 +175,10 @@
llndk_libraries_list :=
vndksp_libraries_list :=
write-libs-to-file :=
+
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file :=
+vndk_using_core_variant_libraries_list :=
+endif
+
+my_vndk_use_core_variant :=
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/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index f01a8c7..3bc3883 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -10,6 +10,7 @@
phony {
name: "shell_and_utilities_system",
required: [
+ "auditctl",
"awk",
"bzip2",
"grep",
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f08cf93..5289976 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -7,6 +7,8 @@
"-Wno-unused-const-variable",
"-D_FILE_OFFSET_BITS=64",
"-DWITHOUT_NLS",
+ "-DWITHOUT_BZ2",
+ "-DWITHOUT_GZIP",
],
}
@@ -54,13 +56,6 @@
defaults: ["toolbox_binary_defaults"],
}
-// We only want 'r' on userdebug and eng builds.
-cc_binary {
- name: "r",
- defaults: ["toolbox_defaults"],
- srcs: ["r.c"],
-}
-
// We build BSD grep separately (but see http://b/111849261).
cc_defaults {
name: "grep_common",
diff --git a/toolbox/r.c b/toolbox/r.c
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#if __LP64__
-#define strtoptr strtoull
-#else
-#define strtoptr strtoul
-#endif
-
-static int usage()
-{
- fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- if(argc < 2) return usage();
-
- int width = 4;
- if(!strcmp(argv[1], "-b")) {
- width = 1;
- argc--;
- argv++;
- } else if(!strcmp(argv[1], "-s")) {
- width = 2;
- argc--;
- argv++;
- }
-
- if(argc < 2) return usage();
- uintptr_t addr = strtoptr(argv[1], 0, 16);
-
- uintptr_t endaddr = 0;
- char* end = strchr(argv[1], '-');
- if (end)
- endaddr = strtoptr(end + 1, 0, 16);
-
- if (!endaddr)
- endaddr = addr + width - 1;
-
- if (endaddr <= addr) {
- fprintf(stderr, "end address <= start address\n");
- return -1;
- }
-
- bool set = false;
- uint32_t value = 0;
- if(argc > 2) {
- set = true;
- value = strtoul(argv[2], 0, 16);
- }
-
- int fd = open("/dev/mem", O_RDWR | O_SYNC);
- if(fd < 0) {
- fprintf(stderr,"cannot open /dev/mem\n");
- return -1;
- }
-
- off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
- size_t mmap_size = endaddr - mmap_start + 1;
- mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
- void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, mmap_start);
-
- if(page == MAP_FAILED){
- fprintf(stderr,"cannot mmap region\n");
- return -1;
- }
-
- while (addr <= endaddr) {
- switch(width){
- case 4: {
- uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
- break;
- }
- case 2: {
- uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
- break;
- }
- case 1: {
- uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
- break;
- }
- }
- addr += width;
- }
- return 0;
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
index cf4a0fa..428bf58 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/file.c
+++ b/toolbox/upstream-netbsd/usr.bin/grep/file.c
@@ -1,4 +1,4 @@
-/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */
+/* $NetBSD: file.c,v 1.10 2018/08/12 09:03:21 christos Exp $ */
/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */
/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */
@@ -35,15 +35,12 @@
#endif
#include <sys/cdefs.h>
-__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
+__RCSID("$NetBSD: file.c,v 1.10 2018/08/12 09:03:21 christos Exp $");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -53,21 +50,20 @@
#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
#include "grep.h"
#define MAXBUFSIZ (32 * 1024)
#define LNBUFBUMP 80
-#ifndef __ANDROID__
+#ifndef WITHOUT_GZIP
static gzFile gzbufdesc;
+#endif
+#ifndef WITHOUT_BZ2
static BZFILE* bzbufdesc;
#endif
-static unsigned char buffer[MAXBUFSIZ];
+static unsigned char buffer[MAXBUFSIZ + 1];
static unsigned char *bufpos;
static size_t bufrem;
@@ -77,18 +73,21 @@
static inline int
grep_refill(struct file *f)
{
- ssize_t nr;
-#ifndef __ANDROID__
+ ssize_t nr = -1;
int bzerr;
-#endif
bufpos = buffer;
bufrem = 0;
-#ifndef __ANDROID__
- if (filebehave == FILE_GZIP)
+#ifndef WITHOUT_GZIP
+ if (filebehave == FILE_GZIP) {
nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
- else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
+ if (nr == -1)
+ return -1;
+ }
+#endif
+#ifndef WITHOUT_BZ2
+ if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
switch (bzerr) {
case BZ_OK:
@@ -114,9 +113,13 @@
/* Make sure we exit with an error */
nr = -1;
}
- } else
+ if (nr == -1)
+ return -1;
+ }
#endif
+ if (nr == -1) {
nr = read(f->fd, buffer, MAXBUFSIZ);
+ }
if (nr < 0)
return (-1);
@@ -125,7 +128,7 @@
return (0);
}
-static inline int
+static inline void
grep_lnbufgrow(size_t newlen)
{
@@ -133,8 +136,6 @@
lnbuf = grep_realloc(lnbuf, newlen);
lnbuflen = newlen;
}
-
- return (0);
}
char *
@@ -159,20 +160,22 @@
/* Look for a newline in the remaining part of the buffer */
if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
++p; /* advance over newline */
- ret = (char *)bufpos;
len = p - bufpos;
+ grep_lnbufgrow(len + 1);
+ memcpy(lnbuf, bufpos, len);
+ lnbuf[len] = '\0';
+ *lenp = len;
bufrem -= len;
bufpos = p;
- *lenp = len;
- return (ret);
+ return ((char *)lnbuf);
}
/* We have to copy the current buffered data to the line buffer */
for (len = bufrem, off = 0; ; len += bufrem) {
/* Make sure there is room for more data */
- if (grep_lnbufgrow(len + LNBUFBUMP))
- goto error;
+ grep_lnbufgrow(len + LNBUFBUMP);
memcpy(lnbuf + off, bufpos, len - off);
+ lnbuf[len] = '\0';
off = len;
if (grep_refill(f) != 0)
goto error;
@@ -185,9 +188,9 @@
++p;
diff = p - bufpos;
len += diff;
- if (grep_lnbufgrow(len))
- goto error;
+ grep_lnbufgrow(len + 1);
memcpy(lnbuf + off, bufpos, diff);
+ lnbuf[off + diff] = '\0';
bufrem -= diff;
bufpos = p;
break;
@@ -204,11 +207,13 @@
grep_file_init(struct file *f)
{
-#ifndef __ANDROID__
+#ifndef WITHOUT_GZIP
if (filebehave == FILE_GZIP &&
(gzbufdesc = gzdopen(f->fd, "r")) == NULL)
goto error;
+#endif
+#ifndef WITHOUT_BZ2
if (filebehave == FILE_BZIP &&
(bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
goto error;
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
index 1ea6ed3..bad2a73 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.c
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
@@ -1,4 +1,4 @@
-/* $NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $ */
+/* $NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $ */
/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */
/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
@@ -34,7 +34,7 @@
#endif
#include <sys/cdefs.h>
-__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
+__RCSID("$NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $");
#include <sys/stat.h>
#include <sys/types.h>
@@ -170,7 +170,9 @@
struct option long_options[] =
{
{"binary-files", required_argument, NULL, BIN_OPT},
+#ifndef WITHOUT_GZIP
{"decompress", no_argument, NULL, DECOMPRESS_OPT},
+#endif
{"help", no_argument, NULL, HELP_OPT},
{"mmap", no_argument, NULL, MMAP_OPT},
{"line-buffered", no_argument, NULL, LINEBUF_OPT},
@@ -197,7 +199,9 @@
{"no-filename", no_argument, NULL, 'h'},
{"with-filename", no_argument, NULL, 'H'},
{"ignore-case", no_argument, NULL, 'i'},
+#ifndef WITHOUT_BZ2
{"bz2decompress", no_argument, NULL, 'J'},
+#endif
{"files-with-matches", no_argument, NULL, 'l'},
{"files-without-match", no_argument, NULL, 'L'},
{"max-count", required_argument, NULL, 'm'},
@@ -338,6 +342,7 @@
case 'g':
grepbehave = GREP_BASIC;
break;
+#ifndef WITHOUT_GZIP
case 'z':
filebehave = FILE_GZIP;
switch(__progname[1]) {
@@ -352,6 +357,7 @@
break;
}
break;
+#endif
}
lastc = '\0';
@@ -491,9 +497,11 @@
iflag = true;
cflags |= REG_ICASE;
break;
+#ifndef WITHOUT_BZ2
case 'J':
filebehave = FILE_BZIP;
break;
+#endif
case 'L':
lflag = false;
Lflag = true;
@@ -596,9 +604,11 @@
strcasecmp("no", optarg) != 0)
errx(2, getstr(3), "--color");
break;
+#ifndef WITHOUT_GZIP
case DECOMPRESS_OPT:
filebehave = FILE_GZIP;
break;
+#endif
case LABEL_OPT:
label = optarg;
break;
@@ -679,8 +689,13 @@
}
}
- if (lbflag)
+ if (lbflag) {
+#ifdef _IOLBF
+ setvbuf(stdout, NULL, _IOLBF, 0);
+#else
setlinebuf(stdout);
+#endif
+ }
if ((aargc == 0 || aargc == 1) && !Hflag)
hflag = true;
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
index fa2a3e3..b7ef7fa 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.h
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
@@ -1,4 +1,4 @@
-/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */
+/* $NetBSD: grep.h,v 1.10 2018/08/12 09:03:21 christos Exp $ */
/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */
/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */
@@ -29,14 +29,14 @@
* SUCH DAMAGE.
*/
-#ifndef __ANDROID__
+#ifndef WITHOUT_BZ2
#include <bzlib.h>
#endif
#include <limits.h>
#include <regex.h>
#include <stdbool.h>
#include <stdio.h>
-#ifndef __ANDROID__
+#ifndef WITHOUT_GZIP
#include <zlib.h>
#endif
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
index ecd948d..a3c9e4c 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/util.c
+++ b/toolbox/upstream-netbsd/usr.bin/grep/util.c
@@ -1,4 +1,4 @@
-/* $NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $ */
+/* $NetBSD: util.c,v 1.19 2018/02/05 22:14:26 mrg Exp $ */
/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */
/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */
@@ -34,7 +34,7 @@
#endif
#include <sys/cdefs.h>
-__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
+__RCSID("$NetBSD: util.c,v 1.19 2018/02/05 22:14:26 mrg Exp $");
#include <sys/stat.h>
#include <sys/types.h>
@@ -478,9 +478,10 @@
if (color)
fprintf(stdout, "\33[%sm\33[K", color);
- fwrite(line->dat + matches[i].rm_so,
- matches[i].rm_eo - matches[i].rm_so, 1,
- stdout);
+ fwrite(line->dat + matches[i].rm_so,
+ matches[i].rm_eo - matches[i].rm_so, 1,
+ stdout);
+
if (color)
fprintf(stdout, "\33[m\33[K");
a = matches[i].rm_eo;
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);
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 0a0ecec..00e3dbc 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,8 +19,13 @@
# to pull in the baseline set of Trusty specific modules.
#
+# For gatekeeper, we include the generic -service and -impl to use legacy
+# HAL loading of gatekeeper.trusty.
+
PRODUCT_PACKAGES += \
android.hardware.keymaster@3.0-service.trusty \
+ android.hardware.gatekeeper@1.0-service \
+ android.hardware.gatekeeper@1.0-impl \
gatekeeper.trusty
PRODUCT_PROPERTY_OVERRIDES += \