Merge "Revert "Revert "adbd: switch the default USB implementation."""
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..1e085a7 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -402,6 +402,14 @@
"liblog",
],
+ product_variables: {
+ debuggable: {
+ required: [
+ "remount",
+ ],
+ },
+ },
+
target: {
android: {
srcs: [
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a5b2f7b..3c07882 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())) {
@@ -1096,10 +1119,10 @@
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);
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.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/client/adb_client.cpp b/adb/client/adb_client.cpp
index 0a09d1e..4cf3a74 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -70,46 +70,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 +147,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 +162,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,11 +210,15 @@
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());
+int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
+ // first query the adb server's version
+ unique_fd fd(_adb_connect("host:version", nullptr, error));
+
+ LOG(DEBUG) << "adb_connect: service: " << service;
if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
fprintf(stderr, "* cannot start server on remote host\n");
// error is the original network connection error
@@ -216,7 +240,7 @@
// 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) {
@@ -254,7 +278,7 @@
return 0;
}
- fd.reset(_adb_connect(service, error));
+ fd.reset(_adb_connect(service, transport, error));
if (fd == -1) {
D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
@@ -265,7 +289,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..0a73787 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -24,7 +24,10 @@
// 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();
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 2bf2924..16fa215 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -638,39 +638,43 @@
fprintf(stdout, "Created child session ID %d.\n", session_id);
session_ids.push_back(session_id);
- struct stat sb;
- if (stat(file, &sb) == -1) {
- fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
- goto finalize_multi_package_session;
+ // Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument.
+ std::vector<std::string> splits = android::base::Split(file, ":");
+
+ for (const std::string& split : splits) {
+ struct stat sb;
+ if (stat(split.c_str(), &sb) == -1) {
+ fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno));
+ goto finalize_multi_package_session;
+ }
+
+ std::string cmd = android::base::StringPrintf(
+ "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
+ static_cast<uint64_t>(sb.st_size), session_id, i,
+ android::base::Basename(split).c_str());
+
+ unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
+ if (local_fd < 0) {
+ fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno));
+ goto finalize_multi_package_session;
+ }
+
+ std::string error;
+ unique_fd remote_fd(adb_connect(cmd, &error));
+ if (remote_fd < 0) {
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+ goto finalize_multi_package_session;
+ }
+
+ copy_to_file(local_fd.get(), remote_fd.get());
+ read_status_line(remote_fd.get(), buf, sizeof(buf));
+
+ if (strncmp("Success", buf, 7)) {
+ fprintf(stderr, "adb: failed to write %s\n", split.c_str());
+ fputs(buf, stderr);
+ goto finalize_multi_package_session;
+ }
}
-
- std::string cmd =
- android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
- install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
- session_id, i, android::base::Basename(file).c_str());
-
- unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
- if (local_fd < 0) {
- fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
- goto finalize_multi_package_session;
- }
-
- std::string error;
- unique_fd remote_fd(adb_connect(cmd, &error));
- if (remote_fd < 0) {
- fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
- goto finalize_multi_package_session;
- }
-
- copy_to_file(local_fd.get(), remote_fd.get());
- read_status_line(remote_fd.get(), buf, sizeof(buf));
-
- if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "adb: failed to write %s\n", file);
- fputs(buf, stderr);
- goto finalize_multi_package_session;
- }
-
all_session_ids += android::base::StringPrintf(" %d", session_id);
}
@@ -718,6 +722,7 @@
fputs(buf, stderr);
}
+ session_ids.push_back(parent_session_id);
// try to abandon all remaining sessions
for (std::size_t i = 0; i < session_ids.size(); i++) {
service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index f70b480..3286959 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"
@@ -1031,10 +1031,11 @@
}
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;
}
@@ -1046,7 +1047,8 @@
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 +1081,9 @@
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.
+ adb_set_transport(kTransportAny, nullptr, transport_id);
+ wait_for_device("wait-for-disconnect");
return true;
}
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index b8827ef..7e470e1 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;
}
}
}
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..70deb31 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -27,6 +27,7 @@
#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>
@@ -242,10 +243,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 +292,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 +317,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 +356,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 +398,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;
@@ -448,24 +448,40 @@
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.c_str(), ×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) {
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/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..e9d9c63 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,42 +385,9 @@
}
D("subprocess parent: exec completed");
- 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.
- if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
- *error = android::base::StringPrintf(
- "failed to create socketpair to intercept data: %s", strerror(errno));
- kill(pid_, SIGKILL);
- 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";
- kill(pid_, SIGKILL);
- 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);
- kill(pid_, SIGKILL);
- return false;
- }
- }
- }
+ if (!ConnectProtocolEndpoints(error)) {
+ kill(pid_, SIGKILL);
+ return false;
}
D("subprocess parent: completed");
@@ -429,7 +398,6 @@
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());
@@ -448,34 +416,9 @@
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));
+ if (!ConnectProtocolEndpoints(error)) {
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),
@@ -486,6 +429,45 @@
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 {
+ // 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;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
Subprocess* raw = subprocess.release();
std::thread(ThreadHandler, raw).detach();
@@ -863,12 +845,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/usb.cpp b/adb/daemon/usb.cpp
index 69d1c31..a44ff43 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -29,6 +29,7 @@
#include <linux/usb/functionfs.h>
#include <sys/eventfd.h>
+#include <algorithm>
#include <array>
#include <future>
#include <memory>
@@ -56,10 +57,11 @@
// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
static std::optional<bool> gFfsAioSupported;
-static constexpr size_t kUsbReadQueueDepth = 16;
-static constexpr size_t kUsbReadSize = 16384;
+static constexpr size_t kUsbReadQueueDepth = 32;
+static constexpr size_t kUsbReadSize = 8 * PAGE_SIZE;
-static constexpr size_t kUsbWriteQueueDepth = 16;
+static constexpr size_t kUsbWriteQueueDepth = 32;
+static constexpr size_t kUsbWriteSize = 8 * PAGE_SIZE;
static const char* to_string(enum usb_functionfs_event_type type) {
switch (type) {
@@ -115,7 +117,7 @@
struct IoBlock {
bool pending;
struct iocb control;
- Block payload;
+ std::shared_ptr<Block> payload;
TransferId id() const { return TransferId::from_value(control.aio_data); }
};
@@ -207,8 +209,20 @@
std::lock_guard<std::mutex> lock(write_mutex_);
write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
if (!packet->payload.empty()) {
- write_requests_.push_back(
- CreateWriteBlock(std::move(packet->payload), next_write_id_++));
+ // The kernel attempts to allocate a contiguous block of memory for each write,
+ // which can fail if the write is large and the kernel heap is fragmented.
+ // Split large writes into smaller chunks to avoid this.
+ std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+ size_t offset = 0;
+ size_t len = payload->size();
+
+ while (len > 0) {
+ size_t write_size = std::min(kUsbWriteSize, len);
+ write_requests_.push_back(
+ CreateWriteBlock(payload, offset, write_size, next_write_id_++));
+ len -= write_size;
+ offset += write_size;
+ }
}
SubmitWrites();
return true;
@@ -246,7 +260,6 @@
// until it dies, and then report failure to the transport via HandleError, which will
// eventually result in the transport being destroyed, which will result in UsbFfsConnection
// being destroyed, which unblocks the open thread and restarts this entire process.
- static constexpr int kInterruptionSignal = SIGUSR1;
static std::once_flag handler_once;
std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
@@ -272,6 +285,7 @@
} else if (rc == 0) {
// Something in the kernel presumably went wrong.
// Close our endpoints, wait for a bit, and then try again.
+ StopWorker();
aio_context_.reset();
read_fd_.reset();
write_fd_.reset();
@@ -297,7 +311,7 @@
switch (event.type) {
case FUNCTIONFS_BIND:
- CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+ CHECK(!bound) << "received FUNCTIONFS_BIND while already bound?";
bound = true;
break;
@@ -313,28 +327,7 @@
}
}
- pthread_t worker_thread_handle = worker_thread_.native_handle();
- while (true) {
- int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
- if (rc != 0) {
- LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
- break;
- }
-
- std::this_thread::sleep_for(100ms);
-
- rc = pthread_kill(worker_thread_handle, 0);
- if (rc == 0) {
- continue;
- } else if (rc == ESRCH) {
- break;
- } else {
- LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
- }
- }
-
- worker_thread_.join();
-
+ StopWorker();
aio_context_.reset();
read_fd_.reset();
write_fd_.reset();
@@ -365,12 +358,36 @@
});
}
+ void StopWorker() {
+ pthread_t worker_thread_handle = worker_thread_.native_handle();
+ while (true) {
+ int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+ if (rc != 0) {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ break;
+ }
+
+ std::this_thread::sleep_for(100ms);
+
+ rc = pthread_kill(worker_thread_handle, 0);
+ if (rc == 0) {
+ continue;
+ } else if (rc == ESRCH) {
+ break;
+ } else {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ }
+ }
+
+ worker_thread_.join();
+ }
+
void PrepareReadBlock(IoBlock* block, uint64_t id) {
block->pending = false;
- block->payload.resize(kUsbReadSize);
+ block->payload = std::make_shared<Block>(kUsbReadSize);
block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
- block->control.aio_nbytes = block->payload.size();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
+ block->control.aio_nbytes = block->payload->size();
}
IoBlock CreateReadBlock(uint64_t id) {
@@ -421,7 +438,7 @@
uint64_t read_idx = id.id % kUsbReadQueueDepth;
IoBlock* block = &read_requests_[read_idx];
block->pending = false;
- block->payload.resize(size);
+ block->payload->resize(size);
// Notification for completed reads can be received out of order.
if (block->id().id != needed_read_id_) {
@@ -442,16 +459,16 @@
}
void ProcessRead(IoBlock* block) {
- if (!block->payload.empty()) {
+ if (!block->payload->empty()) {
if (!incoming_header_.has_value()) {
- CHECK_EQ(sizeof(amessage), block->payload.size());
+ CHECK_EQ(sizeof(amessage), block->payload->size());
amessage msg;
- memcpy(&msg, block->payload.data(), sizeof(amessage));
+ memcpy(&msg, block->payload->data(), sizeof(amessage));
LOG(DEBUG) << "USB read:" << dump_header(&msg);
incoming_header_ = msg;
} else {
size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
- Block payload = std::move(block->payload);
+ Block payload = std::move(*block->payload);
CHECK_LE(payload.size(), bytes_left);
incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
}
@@ -506,7 +523,8 @@
SubmitWrites();
}
- std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+ std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
+ size_t len, uint64_t id) {
auto block = std::make_unique<IoBlock>();
block->payload = std::move(payload);
block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
@@ -514,14 +532,20 @@
block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
block->control.aio_reqprio = 0;
block->control.aio_fildes = write_fd_.get();
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
- block->control.aio_nbytes = block->payload.size();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
+ block->control.aio_nbytes = len;
block->control.aio_offset = 0;
block->control.aio_flags = IOCB_FLAG_RESFD;
block->control.aio_resfd = worker_event_fd_.get();
return block;
}
+ std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+ std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
+ size_t len = block->size();
+ return CreateWriteBlock(std::move(block), 0, len, id);
+ }
+
void SubmitWrites() REQUIRES(write_mutex_) {
if (writes_submitted_ == kUsbWriteQueueDepth) {
return;
@@ -594,6 +618,8 @@
std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+
+ static constexpr int kInterruptionSignal = SIGUSR1;
};
void usb_init_legacy();
diff --git a/adb/services.cpp b/adb/services.cpp
index 0061f0e..80f9f79 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;
@@ -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/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..0b4e084 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
@@ -296,7 +289,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();
});
@@ -923,7 +916,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_;
@@ -1014,8 +1007,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.
diff --git a/adb/transport.h b/adb/transport.h
index 065c81f..a0174b8 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();
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/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/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/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/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/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/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 8798ad3..c08afda 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -316,7 +316,7 @@
std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
std::string line;
- for (unwindstack::MapInfo* map_info : *maps) {
+ for (auto const& map_info : *maps) {
line = " ";
if (print_fault_address_marker) {
if (addr < map_info->start) {
diff --git a/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_driver.cpp b/fastboot/fastboot_driver.cpp
index 65a5247..fea0a77 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -403,7 +403,7 @@
RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
int* dsize) {
char status[FB_RESPONSE_SZ + 1];
- auto start = std::chrono::system_clock::now();
+ auto start = std::chrono::steady_clock::now();
auto set_response = [response](std::string s) {
if (response) *response = std::move(s);
@@ -414,7 +414,7 @@
// erase response
set_response("");
- while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
+ while ((std::chrono::steady_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
int r = transport_->Read(status, FB_RESPONSE_SZ);
if (r < 0) {
error_ = ErrnoStr("Status read failed");
@@ -427,6 +427,11 @@
std::string tmp = input.substr(strlen("INFO"));
info_(tmp);
add_info(std::move(tmp));
+ // We may receive one or more INFO packets during long operations,
+ // e.g. flash/erase if they are back by slow media like NAND/NOR
+ // flash. In that case, reset the timer since it's not a real
+ // timeout.
+ start = std::chrono::steady_clock::now();
} else if (android::base::StartsWith(input, "OKAY")) {
set_response(input.substr(strlen("OKAY")));
return SUCCESS;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 974e13e..4ee9624 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -111,3 +111,35 @@
"libgsi_headers",
],
}
+
+cc_binary {
+ name: "remount",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libavb_user",
+ ],
+ shared_libs: [
+ "libbootloader_message",
+ "libbase",
+ "libcrypto",
+ "libfec",
+ "libfs_mgr",
+ ],
+ header_libs: [
+ "libcutils_headers",
+ ],
+ srcs: [
+ "fs_mgr_remount.cpp",
+ ],
+ cppflags: [
+ "-DALLOW_ADBD_DISABLE_VERITY=0",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-UALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_DISABLE_VERITY=1",
+ ],
+ },
+ },
+}
diff --git a/fs_mgr/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..272190e 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1089,7 +1089,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 +1098,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 +1320,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 +1329,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!";
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4d44fcf..2f1e41f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -307,6 +307,8 @@
} else {
entry->logical_blk_size = val;
}
+ } else if (StartsWith(flag, "avb_keys=")) { // must before the following "avb"
+ entry->avb_keys = arg;
} else if (StartsWith(flag, "avb")) {
entry->fs_mgr_flags.avb = true;
entry->vbmeta_partition = arg;
@@ -325,8 +327,6 @@
}
} else if (StartsWith(flag, "zram_backing_dev_path=")) {
entry->zram_backing_dev_path = arg;
- } else if (StartsWith(flag, "avb_key=")) {
- entry->avb_key = arg;
} else {
LWARNING << "Warning: unknown flag: " << flag;
}
@@ -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
@@ -736,7 +759,8 @@
.fs_type = "ext4",
.flags = MS_RDONLY,
.fs_options = "barrier=1",
- .avb_key = "/gsi.avbpubkey",
+ // could add more keys separated by ':'.
+ .avb_keys = "/avb/gsi.avbpubkey:",
.logical_partition_name = "system"};
system.fs_mgr_flags.wait = true;
system.fs_mgr_flags.logical = true;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 87729cd..40da36d 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,12 @@
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);
+ // 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 +247,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 +534,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) {
@@ -802,12 +776,42 @@
} // 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 +844,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 +872,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 +912,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 +922,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.
@@ -919,7 +939,8 @@
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 +967,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 +1031,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_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
new file mode 100644
index 0000000..5c4008c
--- /dev/null
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -0,0 +1,401 @@
+/*
+ * 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 try_unmount_bionic(android::fs_mgr::Fstab* mounts) {
+ static constexpr const char* kBionic = "/bionic";
+
+ auto entry = GetEntryForMountPoint(mounts, kBionic);
+ if (!entry) return;
+
+ struct statfs buf;
+ if (::statfs(kBionic, &buf) == -1) {
+ PLOG(ERROR) << "statfs of " << kBionic;
+ return;
+ }
+ if (buf.f_flags & MS_RDONLY) {
+ // /bionic is on a read-only partition; can happen for
+ // non-system-as-root-devices. Don' try to unmount.
+ return;
+ }
+ fs_mgr_set_blk_ro(entry->blk_device, false);
+ if (::mount(entry->blk_device.c_str(), entry->mount_point.c_str(), entry->fs_type.c_str(),
+ MS_REMOUNT, nullptr) == -1) {
+ PLOG(ERROR) << "remount of " << kBionic;
+ }
+}
+
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ static const char log_characters[] = "VD\0WEFF";
+ if (severity < sizeof(log_characters)) {
+ auto severity_char = log_characters[severity];
+ if (severity_char) fprintf(stderr, "%c ", severity_char);
+ }
+ fprintf(stderr, "%s\n", message);
+
+ static auto logd = android::base::LogdLogger();
+ logd(id, severity, tag, file, line, message);
+}
+
+[[noreturn]] void reboot(bool dedupe) {
+ if (dedupe) {
+ LOG(INFO) << "The device will now reboot to recovery and attempt un-deduplication.";
+ } else {
+ LOG(INFO) << "Successfully disabled verity\nrebooting device";
+ }
+ ::sync();
+ android::base::SetProperty(ANDROID_RB_PROPERTY, dedupe ? "reboot,recovery" : "reboot,remount");
+ ::sleep(60);
+ ::exit(0); // SUCCESS
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, MyLogger);
+
+ enum {
+ SUCCESS,
+ NOT_USERDEBUG,
+ BADARG,
+ NOT_ROOT,
+ NO_FSTAB,
+ UNKNOWN_PARTITION,
+ INVALID_PARTITION,
+ VERITY_PARTITION,
+ BAD_OVERLAY,
+ NO_MOUNTS,
+ REMOUNT_FAILED,
+ } retval = SUCCESS;
+
+ // If somehow this executable is delivered on a "user" build, it can
+ // not function, so providing a clear message to the caller rather than
+ // letting if fall through and provide a lot of confusing failure messages.
+ if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
+ LOG(ERROR) << "only functions on userdebug or eng builds";
+ return NOT_USERDEBUG;
+ }
+
+ const char* fstab_file = nullptr;
+ auto can_reboot = false;
+
+ struct option longopts[] = {
+ {"fstab", required_argument, nullptr, 'T'},
+ {"help", no_argument, nullptr, 'h'},
+ {"reboot", no_argument, nullptr, 'R'},
+ {0, 0, nullptr, 0},
+ };
+ for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) {
+ switch (opt) {
+ case 'R':
+ can_reboot = true;
+ break;
+ case 'T':
+ if (fstab_file) {
+ LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
+ usage(BADARG);
+ }
+ fstab_file = optarg;
+ break;
+ default:
+ LOG(ERROR) << "Bad Argument -" << char(opt);
+ usage(BADARG);
+ break;
+ case 'h':
+ usage(SUCCESS);
+ break;
+ }
+ }
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "must be run as root";
+ return NOT_ROOT;
+ }
+
+ // Read the selected fstab.
+ android::fs_mgr::Fstab fstab;
+ auto fstab_read = false;
+ if (fstab_file) {
+ fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
+ } else {
+ fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
+ }
+ if (!fstab_read || fstab.empty()) {
+ PLOG(ERROR) << "Failed to read fstab";
+ return NO_FSTAB;
+ }
+
+ // Generate the list of supported overlayfs mount points.
+ auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+ // Generate the all remountable partitions sub-list
+ android::fs_mgr::Fstab all;
+ for (auto const& entry : fstab) {
+ if (!remountable_partition(entry)) continue;
+ if (overlayfs_candidates.empty() ||
+ GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
+ (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
+ all.emplace_back(entry);
+ }
+ }
+
+ // Parse the unique list of valid partition arguments.
+ android::fs_mgr::Fstab partitions;
+ for (; argc > optind; ++optind) {
+ auto partition = std::string(argv[optind]);
+ if (partition.empty()) continue;
+ if (partition == "/") partition = "/system";
+ auto find_part = [&partition](const auto& entry) {
+ const auto mount_point = system_mount_point(entry);
+ if (partition == mount_point) return true;
+ if (partition == android::base::Basename(mount_point)) return true;
+ return false;
+ };
+ // Do we know about the partition?
+ auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
+ if (it == fstab.end()) {
+ LOG(ERROR) << "Unknown partition " << partition << ", skipping";
+ retval = UNKNOWN_PARTITION;
+ continue;
+ }
+ // Is that one covered by an existing overlayfs?
+ auto wrap = is_wrapped(overlayfs_candidates, *it);
+ if (wrap) {
+ LOG(INFO) << "partition " << partition << " covered by overlayfs for "
+ << wrap->mount_point << ", switching";
+ partition = system_mount_point(*wrap);
+ }
+ // Is it a remountable partition?
+ it = std::find_if(all.begin(), all.end(), find_part);
+ if (it == all.end()) {
+ LOG(ERROR) << "Invalid partition " << partition << ", skipping";
+ retval = INVALID_PARTITION;
+ continue;
+ }
+ if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
+ partitions.emplace_back(*it);
+ }
+ }
+
+ if (partitions.empty() && !retval) {
+ partitions = all;
+ }
+
+ // Check verity and optionally setup overlayfs backing.
+ auto reboot_later = false;
+ auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
+ for (auto it = partitions.begin(); it != partitions.end();) {
+ auto& entry = *it;
+ auto& mount_point = entry.mount_point;
+ if (fs_mgr_is_verity_enabled(entry)) {
+ retval = VERITY_PARTITION;
+ if (android::base::GetProperty("ro.boot.vbmeta.devices_state", "") != "locked") {
+ if (AvbOps* ops = avb_ops_user_new()) {
+ auto ret = avb_user_verity_set(
+ ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
+ false);
+ avb_ops_user_free(ops);
+ if (ret) {
+ LOG(WARNING) << "Disable verity for " << mount_point;
+ reboot_later = can_reboot;
+ if (reboot_later) {
+ // w/o overlayfs available, also check for dedupe
+ if (!uses_overlayfs) {
+ ++it;
+ continue;
+ }
+ reboot(false);
+ }
+ } else if (fs_mgr_set_blk_ro(entry.blk_device, false)) {
+ fec::io fh(entry.blk_device.c_str(), O_RDWR);
+ if (fh && fh.set_verity_status(false)) {
+ LOG(WARNING) << "Disable verity for " << mount_point;
+ reboot_later = can_reboot;
+ if (reboot_later && !uses_overlayfs) {
+ ++it;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ LOG(ERROR) << "Skipping " << mount_point;
+ it = partitions.erase(it);
+ continue;
+ }
+
+ auto change = false;
+ errno = 0;
+ if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change)) {
+ if (change) {
+ LOG(INFO) << "Using overlayfs for " << mount_point;
+ }
+ } else if (errno) {
+ PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+ retval = BAD_OVERLAY;
+ it = partitions.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (partitions.empty()) {
+ if (reboot_later) reboot(false);
+ LOG(WARNING) << "No partitions to remount";
+ return retval;
+ }
+
+ // Mount overlayfs.
+ errno = 0;
+ if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
+ retval = BAD_OVERLAY;
+ PLOG(ERROR) << "Can not mount overlayfs for partitions";
+ }
+
+ // Get actual mounts _after_ overlayfs has been added.
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ retval = NO_MOUNTS;
+ }
+
+ // Remount selected partitions.
+ for (auto& entry : partitions) {
+ // unlock the r/o key for the mount point device
+ if (entry.fs_mgr_flags.logical) {
+ fs_mgr_update_logical_partition(&entry);
+ }
+ auto blk_device = entry.blk_device;
+ auto mount_point = entry.mount_point;
+
+ for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+ auto& rentry = *it;
+ if (mount_point == rentry.mount_point) {
+ blk_device = rentry.blk_device;
+ break;
+ }
+ if ((mount_point == "/") && (rentry.mount_point == "/system")) {
+ blk_device = rentry.blk_device;
+ mount_point = "/system";
+ break;
+ }
+ }
+ if (blk_device == "/dev/root") {
+ auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+ if (from_fstab) blk_device = from_fstab->blk_device;
+ }
+ fs_mgr_set_blk_ro(blk_device, false);
+
+ // Now remount!
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ continue;
+ }
+ if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
+ mount_point = entry.mount_point;
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ continue;
+ }
+ }
+ PLOG(WARNING) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+ // If errno = EROFS at this point, we are dealing with r/o
+ // filesystem types like squashfs, erofs or ext4 dedupe. We will
+ // consider such a device that does not have CONFIG_OVERLAY_FS
+ // in the kernel as a misconfigured; except for ext4 dedupe.
+ if ((errno == EROFS) && can_reboot) {
+ const std::vector<std::string> msg = {"--fsck_unshare_blocks"};
+ std::string err;
+ if (write_bootloader_message(msg, &err)) reboot(true);
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ errno = EROFS;
+ }
+ retval = REMOUNT_FAILED;
+ }
+
+ if (reboot_later) reboot(false);
+
+ try_unmount_bionic(&mounts);
+
+ return retval;
+}
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..e811447 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -55,7 +55,7 @@
std::string zram_loopback_path;
uint64_t zram_loopback_size = 512 * 1024 * 1024; // 512MB by default;
std::string zram_backing_dev_path;
- std::string avb_key;
+ std::string avb_keys;
struct FsMgrFlags {
bool wait : 1;
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
index 33c3cad..7463810 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: [
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index b9b75f8..99a1a2f 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>
@@ -126,7 +127,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 +148,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 +201,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;
@@ -220,14 +230,46 @@
}
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.
+ auto offset = TEMP_FAILURE_RETRY(lseek(file_fd, file_size - 1, SEEK_SET));
+ if (offset < 0) {
+ PLOG(ERROR) << "Failed to lseek " << file_path;
+ return false;
+ }
+ if (offset != file_size - 1) {
+ LOG(ERROR) << "Seek returned wrong offset " << offset << " 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(file_size, file_size)) {
+ return false;
+ }
+ return true;
+ }
+ 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
@@ -282,9 +324,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 +361,9 @@
}
static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type == EXT4_SUPER_MAGIC) {
- // No pinning necessary for ext4. The blocks, once allocated, are expected
- // to be fixed.
+ if (fs_type != F2FS_SUPER_MAGIC) {
+ // No pinning necessary for ext4 or vfat. The blocks, once allocated,
+ // are expected to be fixed.
return true;
}
@@ -433,6 +475,44 @@
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;
+ }
+
+ uint64_t num_blocks = (s.st_size + s.st_blksize - 1) / s.st_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++;
+ } else {
+ extents->push_back(fiemap_extent{.fe_logical = block_number,
+ .fe_physical = block,
+ .fe_length = 1,
+ .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 +538,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 +575,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 +598,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..66eb9ae 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,47 @@
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, 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());
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));
+ uint32_t fs_type;
+ {
+ auto ptr = FiemapWriter::Open(testfile, gBlockSize, true);
+ ASSERT_NE(ptr, nullptr);
+ fs_type = ptr->fs_type();
+ }
+ ASSERT_EQ(unlink(testfile.c_str()), 0);
+
+ if (fs_type != MSDOS_SUPER_MAGIC) {
+ expected.push_back(0);
+ }
+ 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 +144,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) {
@@ -138,45 +172,168 @@
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(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);
}
- void TearDown() { CleanupFiles(); }
+ ASSERT_GE(extents.size(), files.size());
+}
- void CleanupFiles() {
- unlink(unaligned_file_.c_str());
- unlink(file_4k_.c_str());
- unlink(file_32k_.c_str());
+TEST_F(SplitFiemapTest, Open) {
+ {
+ auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+ ASSERT_NE(ptr, nullptr);
}
- std::string unaligned_file_;
- std::string file_4k_;
- std::string file_32k_;
- FiemapUniquePtr fptr_unaligned;
- FiemapUniquePtr fptr_4k;
- FiemapUniquePtr fptr_32k;
-};
+ auto ptr = SplitFiemap::Open(testfile);
+ ASSERT_NE(ptr, nullptr);
-TEST_F(TestExistingFile, ErrorChecks) {
- EXPECT_EQ(fptr_unaligned, nullptr);
- EXPECT_NE(fptr_4k, nullptr);
- EXPECT_NE(fptr_32k, nullptr);
+ auto extents = ptr->extents();
+ ASSERT_GE(extents.size(), 24);
+}
- 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);
+TEST_F(SplitFiemapTest, DeleteOnFail) {
+ auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 10, 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 +420,46 @@
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;
+ cerr << "Usage: <test_dir> [file_size]\n";
+ cerr << "\n";
+ cerr << "Note: test_dir must be a writable directory.\n";
exit(EXIT_FAILURE);
}
::android::base::InitLogging(argv, ::android::base::StderrLogger);
- testbdev = argv[1];
+ std::string tempdir = argv[1] + "/XXXXXX"s;
+ if (!mkdtemp(tempdir.data())) {
+ cerr << "unable to create tempdir on " << argv[1] << "\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 +467,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..07f3c10
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
@@ -0,0 +1,96 @@
+/*
+ * 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_; }
+
+ // 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..dbb67a8
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -0,0 +1,293 @@
+/*
+ * 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();
+}
+
+} // 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.h b/fs_mgr/libfs_avb/avb_ops.h
index c0f12aa..a849d94 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -27,7 +27,7 @@
#include <string>
#include <vector>
-#include <fs_avb/fs_avb.h>
+#include <fs_avb/types.h>
#include <libavb/libavb.h>
namespace android {
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 08f87b4..f4e4d4e 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,12 +95,10 @@
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;
}
@@ -164,12 +131,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 +175,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 +203,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 +312,8 @@
return footer;
}
-bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob) {
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length,
+ const std::string& expected_key_blob) {
if (expected_key_blob.empty()) { // no expectation of the key, return true.
return true;
}
@@ -360,6 +326,21 @@
return false;
}
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+ const std::vector<std::string>& allowed_key_paths) {
+ std::string allowed_key_blob;
+ if (key_blob_to_validate.empty()) {
+ LWARNING << "Failed to validate an empty key";
+ return false;
+ }
+ for (const auto& path : allowed_key_paths) {
+ if (ReadFileToString(path, &allowed_key_blob)) {
+ if (key_blob_to_validate == allowed_key_blob) return true;
+ }
+ }
+ return false;
+}
+
VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
const std::string& expected_public_key_blob,
std::string* out_public_key_data) {
@@ -383,7 +364,7 @@
<< ": Error verifying vbmeta image: failed to get public key";
return VBMetaVerifyResult::kError;
}
- if (!VerifyPublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
+ if (!ValidatePublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
<< " sign data does not match key in chain descriptor";
return VBMetaVerifyResult::kErrorVerification;
@@ -420,6 +401,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 +430,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 +496,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/builder.cpp b/fs_mgr/liblp/builder.cpp
index c39fbe7..ebd6997 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;
}
@@ -861,7 +861,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 +973,12 @@
ImportExtents(partition, metadata, source);
- if (!ValidatePartitionSizeChange(partition, 0, partition->size())) {
+ // Note: we've already increased the partition size by calling
+ // ImportExtents(). In order to figure out the size before that,
+ // we would have to iterate the extents and add up the linear
+ // segments. Instead, we just force ValidatePartitionSizeChange
+ // to check if the current configuration is acceptable.
+ if (!ValidatePartitionSizeChange(partition, partition->size(), partition->size(), true)) {
partition->RemoveExtents();
return false;
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 69724f8..81305b3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -835,3 +835,73 @@
EXPECT_EQ(exported->extents[1].target_data, 4608);
EXPECT_EQ(exported->extents[1].num_sectors, 1536);
}
+
+TEST_F(BuilderTest, UpdateSuper) {
+ // Build the on-disk metadata that we saw before flashing.
+ auto builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+
+ Partition* partition = builder->AddPartition("system_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 3608576));
+
+ partition = builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 5510144));
+
+ partition = builder->AddPartition("product_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 2048));
+
+ partition = builder->AddPartition("system_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 7955456));
+
+ partition = builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 9857024));
+
+ partition = builder->AddPartition("product_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 11378688));
+
+ auto on_disk = builder->Export();
+ ASSERT_NE(on_disk, nullptr);
+
+ // Build the super_empty from the new build.
+ builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+ ASSERT_NE(builder->AddPartition("system_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("system_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("product_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("product_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+
+ std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
+ ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 53f480f..486a71f 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -297,7 +297,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);
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index ea12e96..19737fe 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,32 @@
"-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,
+}
+
+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-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 8298bf2..bd5a4fe 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,6 +442,8 @@
fi
echo "${RED}[ FAILED ]${NORMAL} ${@}" >&2
cleanup
+ restore
+ test_duration
exit 1
}
@@ -313,8 +451,8 @@
Returns true if (regex) lval matches rval" ]
EXPECT_EQ() {
- lval="${1}"
- rval="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
@@ -359,10 +497,10 @@
Exits if (regex) lval mismatches rval" ]
check_eq() {
- left="${1}"
- right="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
- EXPECT_EQ "${left}" "${right}" ||
+ EXPECT_EQ "${lval}" "${rval}" ||
die "${@}"
}
@@ -383,25 +521,86 @@
-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
@@ -424,16 +623,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 +687,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 +724,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 +745,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 +764,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 +789,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 +829,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 +882,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"
@@ -694,26 +962,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,7 +989,10 @@
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
@@ -735,10 +1005,15 @@
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 +1052,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 +1066,27 @@
if ${is_userspace_fastboot}; then
die "overlay supposed to be minus /vendor takeover after flash vendor"
else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
- ( die "overlay supposed to be minus /vendor takeover after flash vendor" )
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
fi
fi
- B="`adb_cat /system/hello`" ||
- die "re-read /system/hello after flash vendor"
+ B="`adb_cat /system/hello`"
check_eq "${A}" "${B}" system after flash vendor
adb_root ||
die "adb root"
- B="`adb_cat /vendor/hello`" &&
- if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
- die "re-read /vendor/hello after flash vendor"
- else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
- ( die "re-read /vendor/hello after flash vendor" )
- fi
+ B="`adb_cat /vendor/hello`"
if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
- check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+ check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+ vendor content after flash vendor
else
- ( check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor )
+ (
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ restore() {
+ true
+ }
+ check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+ vendor content after flash vendor
+ )
fi
fi
@@ -820,7 +1096,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,12 +1114,10 @@
adb_sh rm /system/hello </dev/null ||
( [ -n "${L}" ] && echo "${L}" && false ) ||
die -t ${T} "cleanup hello"
-B="`adb_cat /system/hello`" &&
- die "re-read /system/hello after rm"
-check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
-B="`adb_cat /vendor/hello`" &&
- die "re-read /vendor/hello after rm"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
+B="`adb_cat /system/hello`"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /vendor/hello`"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
if [ -n "${scratch_partition}" ]; then
@@ -851,17 +1125,18 @@
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 +1148,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 +1181,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/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/init/Android.bp b/init/Android.bp
index 639d8d1..9aeb837 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -132,7 +132,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..cc514ed 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -63,8 +63,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 +94,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/builtins.cpp b/init/builtins.cpp
index 538ed00..6511d29 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1119,13 +1119,21 @@
}
static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
- if (SwitchToDefaultMountNamespace()) {
+ if (SetupRuntimeBionic()) {
return Success();
} else {
return Error() << "Failed to setup runtime bionic";
}
}
+static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) {
+ if (SwitchToDefaultMountNamespace()) {
+ return Success();
+ } else {
+ return Error() << "Failed to enter into default mount namespace";
+ }
+}
+
// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1177,6 +1185,7 @@
{"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/first_stage_init.cpp b/init/first_stage_init.cpp
index e11d897..7cf4c3f 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
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1b077bc..17cd470 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;
@@ -188,6 +189,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)
@@ -736,9 +760,26 @@
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 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;
+ return false;
+ }
+ 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 {
return true; // No need AVB, returns true to mount the partition directly.
}
@@ -754,8 +795,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..bbef1a9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -42,6 +42,7 @@
#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
#include <libavb/libavb.h>
+#include <libgsi/libgsi.h>
#include <processgroup/processgroup.h>
#include <selinux/android.h>
@@ -695,6 +696,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_namespace.cpp b/init/mount_namespace.cpp
index 413fe8f..4161df2 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>
@@ -132,9 +133,9 @@
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;
@@ -172,6 +173,11 @@
kBionicLibsMountPointDir64))
return false;
+ // /apex is also 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();
@@ -182,7 +188,7 @@
// bind-mounted. In the namespace for post-apexd processes, the bionic from
// the runtime APEX is bind-mounted.
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) {
@@ -227,8 +233,19 @@
}
}
+ LOG(INFO) << "Switched to default mount namespace";
+ return true;
+}
+
+// TODO(jiyong): remove this when /system/lib/libc.so becomes
+// a symlink to /apex/com.android.runtime/lib/bionic/libc.so
+bool SetupRuntimeBionic() {
+ if (IsRecoveryMode()) {
+ // We don't have multiple namespaces in recovery mode
+ return true;
+ }
// Bind-mount bionic from the runtime APEX since it is now available. Note
- // that in case of IsBionicUpdatable() == false, these mounts are over the
+ // that in case of IsApexUpdatable() == false, these mounts are over the
// existing existing bind mounts for the bootstrap bionic, which effectively
// becomes hidden.
if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint,
@@ -238,7 +255,7 @@
kBionicLibsMountPointDir64))
return false;
- LOG(INFO) << "Switched to default mount namespace";
+ LOG(INFO) << "Runtime bionic is set up";
return true;
}
@@ -248,7 +265,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/mount_namespace.h b/init/mount_namespace.h
index c41a449..4eef785 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -20,6 +20,7 @@
namespace init {
bool SetupMountNamespaces();
+bool SetupRuntimeBionic();
bool SwitchToDefaultMountNamespace();
bool SwitchToBootstrapMountNamespaceIfNeeded();
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 46e5e12..4bc857a 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") {
@@ -579,13 +594,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 +641,7 @@
while (isspace(*key)) key++;
}
- load_properties_from_file(fn, key);
+ load_properties_from_file(fn, key, properties);
} else {
value = strchr(key, '=');
@@ -651,12 +668,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 +689,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 +700,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 +723,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;
+ }
+ }
}
}
@@ -835,24 +868,33 @@
void property_load_boot_defaults() {
// 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);
+
+ 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 +950,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 +965,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/selinux.cpp b/init/selinux.cpp
index ee302c1..3fadfed 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -459,6 +459,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/libasyncio/Android.bp b/libasyncio/Android.bp
index 4ab439d..0fd2a3a 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
name: "libasyncio",
defaults: ["libasyncio_defaults"],
vendor_available: true,
+ double_loadable: true,
recovery_available: true,
host_supported: true,
srcs: [
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/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 9d15af2..4518891 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -55,7 +55,7 @@
}
// Iterate through the maps and fill in the backtrace_map_t structure.
- for (auto* map_info : *stack_maps_) {
+ for (const auto& map_info : *stack_maps_) {
backtrace_map_t map;
map.start = map_info->start;
map.end = map_info->end;
diff --git a/libcutils/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/liblog/Android.bp b/liblog/Android.bp
index 1d4a0a0..9b41ebe 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:
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/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/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 4e4554a..320b851 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -6,6 +6,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
srcs: ["memtrack.cpp"],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
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/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..b9f0dbf 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,21 @@
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",
+ ],
required: [
"llndk.libraries.txt",
"vndksp.libraries.txt",
@@ -29,9 +34,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/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5394d7e..043f038 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>
@@ -116,6 +115,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 +138,18 @@
static constexpr const char* kApexPath = "/apex/";
+#if defined(__LP64__)
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+#else
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+#endif
+
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() {
@@ -265,8 +274,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 +335,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 +436,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 +538,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 */,
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
new file mode 100644
index 0000000..11ecc43
--- /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, bool is_for_vendor, jstring library_path,
+ jstring permitted_path) {
+ static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
+ return f(env, target_sdk_version, class_loader, is_shared, is_for_vendor, library_path,
+ 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/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/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index cd8ef94..26d0754 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -45,6 +45,7 @@
using android::base::unique_fd;
static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
@@ -110,12 +111,13 @@
return true;
}
-static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+ std::map<std::string, CgroupDescriptor>* descriptors) {
std::vector<CgroupDescriptor> result;
std::string json_doc;
- if (!android::base::ReadFileToString(CGROUPS_DESC_FILE, &json_doc)) {
- LOG(ERROR) << "Failed to read task profiles from " << CGROUPS_DESC_FILE;
+ if (!android::base::ReadFileToString(file_name, &json_doc)) {
+ LOG(ERROR) << "Failed to read task profiles from " << file_name;
return false;
}
@@ -126,22 +128,51 @@
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())));
+ 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());
+ }
+ }
}
- 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())));
+ 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;
}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index e9dec12..8884650 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -170,8 +170,9 @@
return ret;
}
-static void RemoveUidProcessGroups(const std::string& uid_path) {
+static bool RemoveUidProcessGroups(const std::string& uid_path) {
std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
+ bool empty = true;
if (uid != NULL) {
dirent* dir;
while ((dir = readdir(uid.get())) != nullptr) {
@@ -185,9 +186,15 @@
auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ if (rmdir(path.c_str()) == -1) {
+ if (errno != EBUSY) {
+ PLOG(WARNING) << "Failed to remove " << path;
+ }
+ empty = false;
+ }
}
}
+ return empty;
}
void removeAllProcessGroups() {
@@ -200,7 +207,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 +226,14 @@
}
auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- RemoveUidProcessGroups(path);
+ if (!RemoveUidProcessGroups(path)) {
+ LOG(VERBOSE) << "Skip removing " << path;
+ continue;
+ }
LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ if (rmdir(path.c_str()) == -1 && errno != EBUSY) {
+ PLOG(WARNING) << "Failed to remove " << path;
+ }
}
}
}
@@ -249,6 +261,10 @@
auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
if (!fd) {
+ if (errno == ENOENT) {
+ // This happens when process is already dead
+ return 0;
+ }
PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
return -1;
}
@@ -293,7 +309,7 @@
LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
<< " as part of process cgroup " << initialPid;
- if (kill(-pgid, signal) == -1) {
+ if (kill(-pgid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
}
}
@@ -303,7 +319,7 @@
LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
<< initialPid;
- if (kill(pid, signal) == -1) {
+ if (kill(pid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
}
@@ -317,6 +333,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 +397,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..15d0172
--- /dev/null
+++ b/libprocessgroup/profiles/Android.bp
@@ -0,0 +1,80 @@
+// 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_library_static {
+ name: "libprocessgroup_proto",
+ host_supported: true,
+ srcs: [
+ "cgroups.proto",
+ "task_profiles.proto",
+ ],
+ proto: {
+ type: "full",
+ export_proto_headers: true,
+ },
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+}
+
+cc_test_host {
+ name: "libprocessgroup_proto_test",
+ srcs: [
+ "test.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "liblog",
+ "libjsoncpp",
+ "libjsonpbverify",
+ "libjsonpbparse",
+ "libprocessgroup_proto",
+ ],
+ shared_libs: [
+ "libprotobuf-cpp-full",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+ data: [
+ "cgroups.json",
+ "cgroups.recovery.json",
+ "task_profiles.json",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
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/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/test.cpp b/libprocessgroup/profiles/test.cpp
new file mode 100644
index 0000000..8ba14d6
--- /dev/null
+++ b/libprocessgroup/profiles/test.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 <string>
+
+#include <android-base/file.h>
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups.pb.h"
+#include "task_profiles.pb.h"
+
+using namespace ::android::jsonpb;
+using ::android::base::GetExecutableDirectory;
+using ::testing::MatchesRegex;
+
+namespace android {
+namespace profiles {
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+ return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
+}
+
+TEST(LibProcessgroupProto, EmptyMode) {
+ EXPECT_EQ(0, strtoul("", nullptr, 8))
+ << "Empty mode string cannot be silently converted to 0; this should not happen";
+}
+
+class CgroupsTest : public JsonSchemaTest {
+ public:
+ void SetUp() override {
+ JsonSchemaTest::SetUp();
+ cgroups_ = static_cast<Cgroups*>(message());
+ }
+ Cgroups* cgroups_;
+};
+
+TEST_P(CgroupsTest, CgroupRequiredFields) {
+ for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+ auto&& cgroup = cgroups_->cgroups(i);
+ EXPECT_FALSE(cgroup.controller().empty())
+ << "No controller name for cgroup #" << i << " in " << file_path_;
+ EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(CgroupsTest, Cgroup2RequiredFields) {
+ if (cgroups_->has_cgroups2()) {
+ EXPECT_FALSE(cgroups_->cgroups2().path().empty())
+ << "No path for cgroup2 in " << file_path_;
+ }
+}
+
+// "Mode" field must be in the format of "0xxx".
+static constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
+TEST_P(CgroupsTest, CgroupMode) {
+ for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+ EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
+ << "For cgroup controller #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(CgroupsTest, Cgroup2Mode) {
+ EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
+ << "For cgroups2 in " << file_path_;
+}
+
+class TaskProfilesTest : public JsonSchemaTest {
+ public:
+ void SetUp() override {
+ JsonSchemaTest::SetUp();
+ task_profiles_ = static_cast<TaskProfiles*>(message());
+ }
+ TaskProfiles* task_profiles_;
+};
+
+TEST_P(TaskProfilesTest, AttributeRequiredFields) {
+ for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
+ auto&& attribute = task_profiles_->attributes(i);
+ EXPECT_FALSE(attribute.name().empty())
+ << "No name for attribute #" << i << " in " << file_path_;
+ EXPECT_FALSE(attribute.controller().empty())
+ << "No controller for attribute #" << i << " in " << file_path_;
+ EXPECT_FALSE(attribute.file().empty())
+ << "No file for attribute #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(TaskProfilesTest, ProfileRequiredFields) {
+ for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
+ auto&& profile = task_profiles_->profiles(profile_idx);
+ EXPECT_FALSE(profile.name().empty())
+ << "No name for profile #" << profile_idx << " in " << file_path_;
+ for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
+ auto&& action = profile.actions(action_idx);
+ EXPECT_FALSE(action.name().empty())
+ << "No name for profiles[" << profile_idx << "].actions[" << action_idx
+ << "] in " << file_path_;
+ }
+ }
+}
+
+// Test suite instantiations
+
+INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
+ ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+ MakeTestParam<Cgroups>("/cgroups.recovery.json"),
+ 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/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 337b032..026c011 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;
}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 447852d..fded417 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -42,6 +42,7 @@
using android::base::WriteStringToFile;
#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
+#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
std::string subgroup;
@@ -90,6 +91,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";
}
}
@@ -194,7 +199,7 @@
}
// 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);
@@ -207,7 +212,7 @@
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
@@ -242,7 +247,7 @@
PLOG(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
@@ -284,16 +289,24 @@
}
TaskProfiles::TaskProfiles() {
- if (!Load(CgroupMap::GetInstance())) {
- LOG(ERROR) << "TaskProfiles::Load for [" << getpid() << "] failed";
+ // load system task profiles
+ if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
+ LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
+ }
+
+ // load vendor task profiles if the file exists
+ if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
+ !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
+ LOG(ERROR) << "Loading " << TASK_PROFILE_DB_VENDOR_FILE << " for [" << getpid()
+ << "] failed";
}
}
-bool TaskProfiles::Load(const CgroupMap& cg_map) {
+bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
std::string json_doc;
- if (!android::base::ReadFileToString(TASK_PROFILE_DB_FILE, &json_doc)) {
- LOG(ERROR) << "Failed to read task profiles from " << TASK_PROFILE_DB_FILE;
+ if (!android::base::ReadFileToString(file_name, &json_doc)) {
+ LOG(ERROR) << "Failed to read task profiles from " << file_name;
return false;
}
@@ -304,18 +317,18 @@
return false;
}
- Json::Value attr = root["Attributes"];
+ const Json::Value& attr = root["Attributes"];
for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {
std::string name = attr[i]["Name"].asString();
- std::string ctrlName = attr[i]["Controller"].asString();
- std::string file_name = attr[i]["File"].asString();
+ std::string controller_name = attr[i]["Controller"].asString();
+ std::string file_attr = attr[i]["File"].asString();
if (attributes_.find(name) == attributes_.end()) {
- const CgroupController* controller = cg_map.FindController(ctrlName.c_str());
+ const CgroupController* controller = cg_map.FindController(controller_name);
if (controller) {
- attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_name);
+ attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
} else {
- LOG(WARNING) << "Controller " << ctrlName << " is not found";
+ LOG(WARNING) << "Controller " << controller_name << " is not found";
}
} else {
LOG(WARNING) << "Attribute " << name << " is already defined";
@@ -324,72 +337,72 @@
std::map<std::string, std::string> params;
- Json::Value profilesVal = root["Profiles"];
- for (Json::Value::ArrayIndex i = 0; i < profilesVal.size(); ++i) {
- Json::Value profileVal = profilesVal[i];
+ const Json::Value& profiles_val = root["Profiles"];
+ for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
+ const Json::Value& profile_val = profiles_val[i];
- std::string profileName = profileVal["Name"].asString();
- Json::Value actions = profileVal["Actions"];
+ std::string profile_name = profile_val["Name"].asString();
+ const Json::Value& actions = profile_val["Actions"];
auto profile = std::make_unique<TaskProfile>();
- for (Json::Value::ArrayIndex actIdx = 0; actIdx < actions.size(); ++actIdx) {
- Json::Value actionVal = actions[actIdx];
- std::string actionName = actionVal["Name"].asString();
- Json::Value paramsVal = actionVal["Params"];
- if (actionName == "JoinCgroup") {
- std::string ctrlName = paramsVal["Controller"].asString();
- std::string path = paramsVal["Path"].asString();
+ for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
+ const Json::Value& action_val = actions[act_idx];
+ std::string action_name = action_val["Name"].asString();
+ const Json::Value& params_val = action_val["Params"];
+ if (action_name == "JoinCgroup") {
+ std::string controller_name = params_val["Controller"].asString();
+ std::string path = params_val["Path"].asString();
- const CgroupController* controller = cg_map.FindController(ctrlName.c_str());
+ const CgroupController* controller = cg_map.FindController(controller_name);
if (controller) {
profile->Add(std::make_unique<SetCgroupAction>(controller, path));
} else {
- LOG(WARNING) << "JoinCgroup: controller " << ctrlName << " is not found";
+ LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
}
- } else if (actionName == "SetTimerSlack") {
- std::string slackValue = paramsVal["Slack"].asString();
+ } else if (action_name == "SetTimerSlack") {
+ std::string slack_value = params_val["Slack"].asString();
char* end;
unsigned long slack;
- slack = strtoul(slackValue.c_str(), &end, 10);
- if (end > slackValue.c_str()) {
+ slack = strtoul(slack_value.c_str(), &end, 10);
+ if (end > slack_value.c_str()) {
profile->Add(std::make_unique<SetTimerSlackAction>(slack));
} else {
- LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slackValue;
+ LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slack_value;
}
- } else if (actionName == "SetAttribute") {
- std::string attrName = paramsVal["Name"].asString();
- std::string attrValue = paramsVal["Value"].asString();
+ } else if (action_name == "SetAttribute") {
+ std::string attr_name = params_val["Name"].asString();
+ std::string attr_value = params_val["Value"].asString();
- auto iter = attributes_.find(attrName);
+ auto iter = attributes_.find(attr_name);
if (iter != attributes_.end()) {
profile->Add(
- std::make_unique<SetAttributeAction>(iter->second.get(), attrValue));
+ std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
} else {
- LOG(WARNING) << "SetAttribute: unknown attribute: " << attrName;
+ LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
}
- } else if (actionName == "SetClamps") {
- std::string boostValue = paramsVal["Boost"].asString();
- std::string clampValue = paramsVal["Clamp"].asString();
+ } else if (action_name == "SetClamps") {
+ std::string boost_value = params_val["Boost"].asString();
+ std::string clamp_value = params_val["Clamp"].asString();
char* end;
unsigned long boost;
- boost = strtoul(boostValue.c_str(), &end, 10);
- if (end > boostValue.c_str()) {
- unsigned long clamp = strtoul(clampValue.c_str(), &end, 10);
- if (end > clampValue.c_str()) {
+ boost = strtoul(boost_value.c_str(), &end, 10);
+ if (end > boost_value.c_str()) {
+ unsigned long clamp = strtoul(clamp_value.c_str(), &end, 10);
+ if (end > clamp_value.c_str()) {
profile->Add(std::make_unique<SetClampsAction>(boost, clamp));
} else {
- LOG(WARNING) << "SetClamps: invalid parameter " << clampValue;
+ LOG(WARNING) << "SetClamps: invalid parameter " << clamp_value;
}
} else {
- LOG(WARNING) << "SetClamps: invalid parameter: " << boostValue;
+ LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
}
} else {
- LOG(WARNING) << "Unknown profile action: " << actionName;
+ LOG(WARNING) << "Unknown profile action: " << action_name;
}
}
- profiles_[profileName] = std::move(profile);
+ profiles_[profile_name] = std::move(profile);
}
return true;
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index b2e39f9..9ee81c1 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -152,5 +152,5 @@
TaskProfiles();
- bool Load(const CgroupMap& cg_map);
+ bool Load(const CgroupMap& cg_map, const std::string& file_name);
};
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15f03d0..27cda93 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -31,6 +31,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
host_supported: true,
srcs: [
"process.cpp",
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/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/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/libunwindstack/Android.bp b/libunwindstack/Android.bp
index c90f5b2..a49fd9e 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",
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/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/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/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/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/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index d88531f..2dc5118 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);
}
}
@@ -85,7 +85,7 @@
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 +98,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);
}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index fc6f305..39bf3a5 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -20,6 +20,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
host_supported: true,
srcs: ["usbhost.c"],
cflags: ["-Werror"],
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 93aa1e6..c67ff8f 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -205,7 +205,6 @@
"LruCache_test.cpp",
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
- "Singleton_test.cpp",
"String8_test.cpp",
"StrongPointer_test.cpp",
"Unicode_test.cpp",
@@ -240,17 +239,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/libvndksupport/Android.bp b/libvndksupport/Android.bp
index e73b366..bfa2508 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -10,6 +10,11 @@
local_include_dirs: ["include/vndksupport"],
export_include_dirs: ["include"],
shared_libs: ["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..821940a 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -45,6 +45,17 @@
return vendor_namespace;
}
+int android_is_in_vendor_process() {
+ 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/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..18421e8 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -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/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
index 6b5d5d0..36a5f9f 100755
--- a/mkbootimg/unpack_bootimg.py
+++ b/mkbootimg/unpack_bootimg.py
@@ -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..d2125d8 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -42,33 +42,29 @@
LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
LOCAL_POST_INSTALL_CMD += ; ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+# TODO(b/124106384): Clean up compat symlinks for ART binaries.
+ART_BINARIES= \
+ dalvikvm \
+ dalvikvm32 \
+ dalvikvm64 \
+ dex2oat \
+ dexdiag \
+ dexdump \
+ dexlist \
+ dexoptanalyzer \
+ oatdump \
+ profman \
+
+$(foreach b,$(ART_BINARIES), \
+ $(eval LOCAL_POST_INSTALL_CMD += \
+ ; ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+)
+
# End of runtime APEX compatibilty.
include $(BUILD_PREBUILT)
#######################################
-# 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)
-
-#######################################
# asan.options
ifneq ($(filter address,$(SANITIZE_TARGET)),)
@@ -195,6 +191,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' $@
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..e624b5a 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -54,6 +54,10 @@
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
@@ -87,12 +91,10 @@
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%
+namespace.media.link.default.allow_all_shared_libs = true
###############################################################################
# "conscrypt" APEX namespace
@@ -110,6 +112,7 @@
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
@@ -126,6 +129,8 @@
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..4b2dd07 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -20,13 +20,17 @@
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
+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.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.postinstall = /postinstall
@@ -64,6 +68,9 @@
namespace.default.permitted.paths += /vendor/framework
namespace.default.permitted.paths += /vendor/app
namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /system/vendor/framework
+namespace.default.permitted.paths += /system/vendor/app
+namespace.default.permitted.paths += /system/vendor/priv-app
namespace.default.permitted.paths += /odm/framework
namespace.default.permitted.paths += /odm/app
namespace.default.permitted.paths += /odm/priv-app
@@ -96,6 +103,9 @@
namespace.default.asan.permitted.paths += /vendor/framework
namespace.default.asan.permitted.paths += /vendor/app
namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /system/vendor/framework
+namespace.default.asan.permitted.paths += /system/vendor/app
+namespace.default.asan.permitted.paths += /system/vendor/priv-app
namespace.default.asan.permitted.paths += /odm/framework
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
@@ -128,6 +138,10 @@
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
@@ -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
@@ -184,6 +200,7 @@
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
@@ -200,6 +217,8 @@
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 +242,7 @@
namespace.sphal.permitted.paths = /odm/${LIB}
namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
namespace.sphal.asan.search.paths += /odm/${LIB}
@@ -265,6 +285,7 @@
namespace.rs.permitted.paths = /odm/${LIB}
namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
namespace.rs.permitted.paths += /data
namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp
@@ -310,6 +331,8 @@
namespace.vndk.permitted.paths += /odm/${LIB}/egl
namespace.vndk.permitted.paths += /vendor/${LIB}/hw
namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
@@ -377,6 +400,7 @@
namespace.default.permitted.paths = /odm
namespace.default.permitted.paths += /vendor
+namespace.default.permitted.paths += /system/vendor
#VNDK27#namespace.default.search.paths += /vendor/${LIB}/hw
#VNDK27#namespace.default.search.paths += /vendor/${LIB}/egl
@@ -408,6 +432,7 @@
# 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.
@@ -475,6 +500,116 @@
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
+
+###############################################################################
+# 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 = libart.so:libartd.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
+
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+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 = libjavacore.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..54f4c98 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -20,6 +20,13 @@
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
+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.system = /data/nativetest
dir.system = /data/nativetest64
dir.system = /data/benchmarktest
@@ -72,6 +79,10 @@
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
@@ -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
@@ -129,6 +142,7 @@
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
@@ -145,6 +159,8 @@
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 +184,7 @@
namespace.sphal.permitted.paths = /odm/${LIB}
namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
namespace.sphal.asan.search.paths += /odm/${LIB}
@@ -210,6 +227,7 @@
namespace.rs.permitted.paths = /odm/${LIB}
namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
namespace.rs.permitted.paths += /data
namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp
@@ -255,6 +273,7 @@
namespace.vndk.permitted.paths += /odm/${LIB}/egl
namespace.vndk.permitted.paths += /vendor/${LIB}/hw
namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
@@ -305,11 +324,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 +342,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 +350,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 +370,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.
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 = libart.so:libartd.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
+
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+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 = libjavacore.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 += 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/init.environ.rc.in b/rootdir/init.environ.rc.in
index d10f7c1..5d6cd2d 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,5 +1,5 @@
# set up the global environment
-on init
+on early-init
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
@@ -9,6 +9,7 @@
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..f2e7a7c 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
@@ -269,18 +277,9 @@
write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}
chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}
- # Setup APEX mount point and its security context
- mount tmpfs tmpfs /apex nodev noexec nosuid
- chmod 0755 /apex
- chown root root /apex
- restorecon /apex
-
# Start logd before any other services run to ensure we capture all of their logs.
start logd
- # Start apexd as soon as we can
- start apexd
-
# Start essential services.
start servicemanager
start hwservicemanager
@@ -388,6 +387,7 @@
restorecon_recursive /metadata
mkdir /metadata/vold
chmod 0700 /metadata/vold
+ mkdir /metadata/password_slots 0771 root system
on late-fs
# Ensure that tracefs has the correct permissions.
@@ -416,8 +416,16 @@
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
+ # 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/pkg_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 +542,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,24 +574,22 @@
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
+ # TODO(jiyong): remove setup_runtime_bionic
+ setup_runtime_bionic
+ parse_apex_configs
+
init_user0
# Set SELinux security contexts on upgrade or policy update.
restorecon --recursive --skip-ce /data
- # 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
+ # 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
# If there is no post-fs-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
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/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index e1de130..2b35819 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -99,14 +100,21 @@
static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
- mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
+ mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
+ bool use_esdfs) {
+ // Add new options at the end of the vector.
+ std::vector<std::string> new_opts_list;
+ if (multi_user) new_opts_list.push_back("multiuser,");
+ if (derive_gid) new_opts_list.push_back("derive_gid,");
+ if (default_normal) new_opts_list.push_back("default_normal,");
+ if (unshared_obb) new_opts_list.push_back("unshared_obb,");
// Try several attempts, each time with one less option, to gracefully
// handle older kernels that aren't updated yet.
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i <= new_opts_list.size(); ++i) {
std::string new_opts;
- if (multi_user && i < 3) new_opts += "multiuser,";
- if (derive_gid && i < 2) new_opts += "derive_gid,";
- if (default_normal && i < 1) new_opts += "default_normal,";
+ for (int j = 0; j < new_opts_list.size() - i; ++j) {
+ new_opts += new_opts_list[j];
+ }
auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
@@ -142,13 +150,14 @@
return true;
}
-static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
- const std::string& dest_path, uid_t fsuid, gid_t fsgid,
- bool multi_user, userid_t userid, gid_t gid, mode_t mask,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+static bool sdcardfs_setup_secondary(const std::string& default_path,
+ const std::string& source_path, const std::string& dest_path,
+ uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid,
+ gid_t gid, mode_t mask, bool derive_gid, bool default_normal,
+ bool unshared_obb, bool use_esdfs) {
if (use_esdfs) {
return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
- derive_gid, default_normal, use_esdfs);
+ derive_gid, default_normal, unshared_obb, use_esdfs);
} else {
return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
}
@@ -156,7 +165,7 @@
static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
gid_t gid, userid_t userid, bool multi_user, bool full_write,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+ bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
std::string dest_path_default = "/mnt/runtime/default/" + label;
std::string dest_path_read = "/mnt/runtime/read/" + label;
std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -167,16 +176,17 @@
// Multi-user storage is fully isolated per user, so "other"
// permissions are completely masked off.
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
- default_normal, use_esdfs) ||
+ default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
- default_normal, use_esdfs)) {
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
} else {
@@ -184,16 +194,17 @@
// the Android directories are masked off to a single user
// deep inside attr_from_stat().
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
- default_normal, use_esdfs)) {
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
}
@@ -216,7 +227,8 @@
<< " -U: specify user ID that owns device"
<< " -m: source_path is multi-user"
<< " -w: runtime write mount has full write access"
- << " -P preserve owners on the lower file system";
+ << " -P: preserve owners on the lower file system"
+ << " -o: obb dir doesn't need to be shared between users";
return 1;
}
@@ -230,6 +242,7 @@
bool full_write = false;
bool derive_gid = false;
bool default_normal = false;
+ bool unshared_obb = false;
int i;
struct rlimit rlim;
int fs_version;
@@ -238,7 +251,7 @@
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
int opt;
- while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:U:mwGio")) != -1) {
switch (opt) {
case 'u':
uid = strtoul(optarg, NULL, 10);
@@ -261,8 +274,12 @@
case 'i':
default_normal = true;
break;
+ case 'o':
+ unshared_obb = true;
+ break;
case '?':
default:
+ LOG(ERROR) << "Unknown option: '" << opt << "'";
return usage();
}
}
@@ -304,6 +321,6 @@
}
run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
- default_normal, !should_use_sdcardfs());
+ default_normal, unshared_obb, !should_use_sdcardfs());
return 1;
}
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f08cf93..7ad6f1c 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -59,6 +59,7 @@
name: "r",
defaults: ["toolbox_defaults"],
srcs: ["r.c"],
+ vendor_available: true,
}
// We build BSD grep separately (but see http://b/111849261).
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 41263e5..c61f7d0 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -17,8 +17,8 @@
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/stat.h>
@@ -34,28 +34,38 @@
#define REQ_BUFFER_SIZE 4096
static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
-static const char *ss_data_root;
-static const char *trusty_devname;
-static const char *rpmb_devname;
-static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+static const char* ss_data_root;
+static const char* trusty_devname;
+static const char* rpmb_devname;
+static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
-static const char *_sopts = "hp:d:r:";
-static const struct option _lopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"trusty_dev", required_argument, NULL, 'd'},
- {"data_path", required_argument, NULL, 'p'},
- {"rpmb_dev", required_argument, NULL, 'r'},
- {0, 0, 0, 0}
-};
+static enum dev_type dev_type = MMC_RPMB;
-static void show_usage_and_exit(int code)
-{
- ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+static enum dev_type parse_dev_type(const char* dev_type_name) {
+ if (!strcmp(dev_type_name, "mmc")) {
+ return MMC_RPMB;
+ } else if (!strcmp(dev_type_name, "virt")) {
+ return VIRT_RPMB;
+ } else {
+ return UNKNOWN_RPMB;
+ }
+}
+
+static const char* _sopts = "hp:d:r:t:";
+static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
+ {"trusty_dev", required_argument, NULL, 'd'},
+ {"data_path", required_argument, NULL, 'p'},
+ {"rpmb_dev", required_argument, NULL, 'r'},
+ {"dev_type", required_argument, NULL, 't'},
+ {0, 0, 0, 0}};
+
+static void show_usage_and_exit(int code) {
+ ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+ ALOGE("Available dev types: mmc, virt\n");
exit(code);
}
-static int drop_privs(void)
-{
+static int drop_privs(void) {
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];
@@ -95,12 +105,10 @@
return 0;
}
-static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
-{
+static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
int rc;
- if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
- (msg->cmd != STORAGE_RPMB_SEND)) {
+ if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
/*
* handling post commit messages on non rpmb commands are not
* implemented as there is no use case for this yet.
@@ -119,42 +127,42 @@
}
switch (msg->cmd) {
- case STORAGE_FILE_DELETE:
- rc = storage_file_delete(msg, req, req_len);
- break;
+ case STORAGE_FILE_DELETE:
+ rc = storage_file_delete(msg, req, req_len);
+ break;
- case STORAGE_FILE_OPEN:
- rc = storage_file_open(msg, req, req_len);
- break;
+ case STORAGE_FILE_OPEN:
+ rc = storage_file_open(msg, req, req_len);
+ break;
- case STORAGE_FILE_CLOSE:
- rc = storage_file_close(msg, req, req_len);
- break;
+ case STORAGE_FILE_CLOSE:
+ rc = storage_file_close(msg, req, req_len);
+ break;
- case STORAGE_FILE_WRITE:
- rc = storage_file_write(msg, req, req_len);
- break;
+ case STORAGE_FILE_WRITE:
+ rc = storage_file_write(msg, req, req_len);
+ break;
- case STORAGE_FILE_READ:
- rc = storage_file_read(msg, req, req_len);
- break;
+ case STORAGE_FILE_READ:
+ rc = storage_file_read(msg, req, req_len);
+ break;
- case STORAGE_FILE_GET_SIZE:
- rc = storage_file_get_size(msg, req, req_len);
- break;
+ case STORAGE_FILE_GET_SIZE:
+ rc = storage_file_get_size(msg, req, req_len);
+ break;
- case STORAGE_FILE_SET_SIZE:
- rc = storage_file_set_size(msg, req, req_len);
- break;
+ case STORAGE_FILE_SET_SIZE:
+ rc = storage_file_set_size(msg, req, req_len);
+ break;
- case STORAGE_RPMB_SEND:
- rc = rpmb_send(msg, req, req_len);
- break;
+ case STORAGE_RPMB_SEND:
+ rc = rpmb_send(msg, req, req_len);
+ break;
- default:
- ALOGE("unhandled command 0x%x\n", msg->cmd);
- msg->result = STORAGE_ERR_UNIMPLEMENTED;
- rc = 1;
+ default:
+ ALOGE("unhandled command 0x%x\n", msg->cmd);
+ msg->result = STORAGE_ERR_UNIMPLEMENTED;
+ rc = 1;
}
if (rc > 0) {
@@ -164,58 +172,58 @@
return rc;
}
-static int proxy_loop(void)
-{
+static int proxy_loop(void) {
ssize_t rc;
struct storage_msg msg;
/* enter main message handling loop */
while (true) {
-
/* get incoming message */
rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
- if (rc < 0)
- return rc;
+ if (rc < 0) return rc;
/* handle request */
req_buffer[rc] = 0; /* force zero termination */
rc = handle_req(&msg, req_buffer, rc);
- if (rc)
- return rc;
+ if (rc) return rc;
}
return 0;
}
-static void parse_args(int argc, char *argv[])
-{
+static void parse_args(int argc, char* argv[]) {
int opt;
int oidx = 0;
while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
switch (opt) {
+ case 'd':
+ trusty_devname = strdup(optarg);
+ break;
- case 'd':
- trusty_devname = strdup(optarg);
- break;
+ case 'p':
+ ss_data_root = strdup(optarg);
+ break;
- case 'p':
- ss_data_root = strdup(optarg);
- break;
+ case 'r':
+ rpmb_devname = strdup(optarg);
+ break;
- case 'r':
- rpmb_devname = strdup(optarg);
- break;
+ case 't':
+ dev_type = parse_dev_type(optarg);
+ if (dev_type == UNKNOWN_RPMB) {
+ ALOGE("Unrecognized dev type: %s\n", optarg);
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+ break;
- default:
- ALOGE("unrecognized option (%c):\n", opt);
- show_usage_and_exit(EXIT_FAILURE);
+ default:
+ ALOGE("unrecognized option (%c):\n", opt);
+ show_usage_and_exit(EXIT_FAILURE);
}
}
- if (ss_data_root == NULL ||
- trusty_devname == NULL ||
- rpmb_devname == NULL) {
+ if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {
ALOGE("missing required argument(s)\n");
show_usage_and_exit(EXIT_FAILURE);
}
@@ -226,31 +234,26 @@
ALOGI("rpmb dev: %s\n", rpmb_devname);
}
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
int rc;
/* drop privileges */
- if (drop_privs() < 0)
- return EXIT_FAILURE;
+ if (drop_privs() < 0) return EXIT_FAILURE;
/* parse arguments */
parse_args(argc, argv);
/* initialize secure storage directory */
rc = storage_init(ss_data_root);
- if (rc < 0)
- return EXIT_FAILURE;
+ if (rc < 0) return EXIT_FAILURE;
/* open rpmb device */
- rc = rpmb_open(rpmb_devname);
- if (rc < 0)
- return EXIT_FAILURE;
+ rc = rpmb_open(rpmb_devname, dev_type);
+ if (rc < 0) return EXIT_FAILURE;
/* connect to Trusty secure storage server */
rc = ipc_connect(trusty_devname, ss_srv_name);
- if (rc < 0)
- return EXIT_FAILURE;
+ if (rc < 0) return EXIT_FAILURE;
/* enter main loop */
rc = proxy_loop();
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 9c79105..29827e2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -51,17 +51,16 @@
static int rpmb_fd = -1;
static uint8_t read_buf[4096];
+static enum dev_type dev_type = UNKNOWN_RPMB;
#ifdef RPMB_DEBUG
-static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
-{
+static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
size_t i;
printf("%s @%p [%zu]", prefix, buf, size);
for (i = 0; i < size; i++) {
- if (i && i % 32 == 0)
- printf("\n%*s", (int) strlen(prefix), "");
+ if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
printf(" %02x", buf[i]);
}
printf("\n");
@@ -70,41 +69,16 @@
#endif
-
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
-{
- int rc;
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
struct mmc_ioc_cmd cmd_buf[3];
} mmc = {};
- struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
- const struct storage_rpmb_send_req *req = r;
+ struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
+ int rc;
- if (req_len < sizeof(*req)) {
- ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
- req_len, sizeof(*req));
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
- size_t expected_len =
- sizeof(*req) + req->reliable_write_size + req->write_size;
- if (req_len != expected_len) {
- ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
- req_len, expected_len);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
- const uint8_t *write_buf = req->payload;
+ const uint8_t* write_buf = req->payload;
if (req->reliable_write_size) {
- if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_RELW;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -121,12 +95,6 @@
}
if (req->write_size) {
- if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid write size %u\n", req->write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_W;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -143,17 +111,9 @@
}
if (req->read_size) {
- if (req->read_size % MMC_BLOCK_SIZE != 0 ||
- req->read_size > sizeof(read_buf)) {
- ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_R;
cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
- cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
- cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;
cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
mmc_ioc_cmd_set_data((*cmd), read_buf);
#ifdef RPMB_DEBUG
@@ -163,15 +123,97 @@
cmd++;
}
- rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+ rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
if (rc < 0) {
ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ }
+ return rc;
+}
+
+static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
+ size_t payload_size) {
+ int rc;
+ uint16_t res_count = read_size / MMC_BLOCK_SIZE;
+ uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;
+ rc = write(rpmb_fd, &res_count, sizeof(res_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, payload, payload_size);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = read(rpmb_fd, read_buf, read_size);
+ return rc;
+}
+
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+ int rc;
+ const struct storage_rpmb_send_req* req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
+ if (req_len != expected_len) {
+ ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid write size %u\n", req->write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
+ ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (dev_type == MMC_RPMB) {
+ rc = send_mmc_rpmb_req(rpmb_fd, req);
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else if (dev_type == VIRT_RPMB) {
+ size_t payload_size = req->reliable_write_size + req->write_size;
+ rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
+ if (rc < 0) {
+ ALOGE("send_virt_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ if (rc != req->read_size) {
+ ALOGE("send_virt_rpmb_req got incomplete response: "
+ "(size %d, expected %d)\n",
+ rc, req->read_size);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else {
+ ALOGE("Unsupported dev_type\n");
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
#ifdef RPMB_DEBUG
- if (req->read_size)
- print_buf("response: ", read_buf, req->read_size);
+ if (req->read_size) print_buf("response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
@@ -188,24 +230,20 @@
return ipc_respond(msg, NULL, 0);
}
-
-int rpmb_open(const char *rpmb_devname)
-{
+int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
int rc;
+ dev_type = open_dev_type;
rc = open(rpmb_devname, O_RDWR, 0);
if (rc < 0) {
- ALOGE("unable (%d) to open rpmb device '%s': %s\n",
- errno, rpmb_devname, strerror(errno));
+ ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
return rc;
}
rpmb_fd = rc;
return 0;
}
-void rpmb_close(void)
-{
+void rpmb_close(void) {
close(rpmb_fd);
rpmb_fd = -1;
}
-
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 85cff44..4c330c9 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,8 @@
#include <stdint.h>
#include <trusty/interface/storage.h>
-int rpmb_open(const char *rpmb_devname);
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+
+int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
void rpmb_close(void);