Revert "adb: daemon: Assign valid fd to usb_handle ep0 file descriptor"
am: ba4684c2b2
Change-Id: I030ce911424f7315cd3b9c4f299b4232ee81e9aa
diff --git a/CleanSpec.mk b/CleanSpec.mk
index ebe5f4a..6f6481f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -80,3 +80,5 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/charger)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin/charger)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 716378b..66d0c92 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -28,6 +28,13 @@
"name": "memunreachable_test"
},
{
+ "name": "memunreachable_unit_test"
+ },
+ {
+ "name": "memunreachable_unit_test",
+ "host": true
+ },
+ {
"name": "memunreachable_binder_test"
},
{
diff --git a/adb/Android.bp b/adb/Android.bp
index 01e00dd..114eb2a 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -26,6 +26,7 @@
"-Wvla",
"-DADB_HOST=1", // overridden by adbd_defaults
"-DALLOW_ADBD_ROOT=0", // overridden by adbd_defaults
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
],
cpp_std: "experimental",
@@ -59,6 +60,9 @@
// MinGW hides some things behind _POSIX_SOURCE.
"-D_POSIX_SOURCE",
+ // libusb uses __stdcall on a variadic function, which gets ignored.
+ "-Wno-ignored-attributes",
+
// Not supported yet.
"-Wno-thread-safety",
],
@@ -506,6 +510,52 @@
}
cc_binary {
+ name: "static_adbd",
+ defaults: ["adbd_defaults", "host_adbd_supported"],
+
+ recovery_available: false,
+ static_executable: true,
+ host_supported: false,
+
+ srcs: [
+ "daemon/main.cpp",
+ ],
+
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-Wno-deprecated-declarations",
+ ],
+
+ strip: {
+ keep_symbols: true,
+ },
+
+ static_libs: [
+ "libadbd",
+ "libadbd_services",
+ "libasyncio",
+ "libavb_user",
+ "libbase",
+ "libbootloader_message",
+ "libcap",
+ "libcrypto",
+ "libcrypto_utils",
+ "libcutils",
+ "libdiagnose_usb",
+ "libext4_utils",
+ "libfec",
+ "libfec_rs",
+ "libfs_mgr",
+ "liblog",
+ "liblp",
+ "libmdnssd",
+ "libminijail",
+ "libselinux",
+ "libsquashfs_utils",
+ ],
+}
+
+cc_binary {
name: "abb",
defaults: ["adbd_defaults"],
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 050ba49..d5e7be1 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1052,9 +1052,9 @@
// 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:")) {
+ if (android::base::ConsumePrefix(&service, "tport:")) {
legacy = false;
- if (ConsumePrefix(&service, "serial:")) {
+ if (android::base::ConsumePrefix(&service, "serial:")) {
serial_storage = service;
serial = serial_storage.c_str();
} else if (service == "usb") {
@@ -1068,7 +1068,7 @@
// Selection by id is unimplemented, since you obviously already know the transport id
// you're connecting to.
} else {
- if (ConsumePrefix(&service, "transport-id:")) {
+ if (android::base::ConsumePrefix(&service, "transport-id:")) {
if (!ParseUint(&transport_id, service)) {
SendFail(reply_fd, "invalid transport id");
return HostRequestResult::Handled;
@@ -1079,7 +1079,7 @@
type = kTransportLocal;
} else if (service == "transport-any") {
type = kTransportAny;
- } else if (ConsumePrefix(&service, "transport:")) {
+ } else if (android::base::ConsumePrefix(&service, "transport:")) {
serial_storage = service;
serial = serial_storage.c_str();
}
@@ -1220,7 +1220,7 @@
}
// Indicates a new emulator instance has started.
- if (ConsumePrefix(&service, "emulator:")) {
+ if (android::base::ConsumePrefix(&service, "emulator:")) {
unsigned int port;
if (!ParseUint(&port, service)) {
LOG(ERROR) << "received invalid port for emulator: " << service;
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index f5cdcb5..bdb8efa 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, std::string_view s) {
+bool SendProtocolString(borrowed_fd fd, std::string_view s) {
unsigned int length = s.size();
if (length > MAX_PAYLOAD - 4) {
errno = EMSGSIZE;
@@ -47,7 +47,7 @@
return WriteFdExactly(fd, str);
}
-bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error) {
char buf[5];
if (!ReadFdExactly(fd, buf, 4)) {
*error = perror_str("protocol fault (couldn't read status length)");
@@ -65,57 +65,57 @@
return true;
}
-bool SendOkay(int fd) {
+bool SendOkay(borrowed_fd fd) {
return WriteFdExactly(fd, "OKAY", 4);
}
-bool SendFail(int fd, std::string_view reason) {
+bool SendFail(borrowed_fd fd, std::string_view reason) {
return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
}
-bool ReadFdExactly(int fd, void* buf, size_t len) {
+bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len) {
char* p = reinterpret_cast<char*>(buf);
size_t len0 = len;
- D("readx: fd=%d wanted=%zu", fd, len);
+ D("readx: fd=%d wanted=%zu", fd.get(), len);
while (len > 0) {
int r = adb_read(fd, p, len);
if (r > 0) {
len -= r;
p += r;
} else if (r == -1) {
- D("readx: fd=%d error %d: %s", fd, errno, strerror(errno));
+ D("readx: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
return false;
} else {
- D("readx: fd=%d disconnected", fd);
+ D("readx: fd=%d disconnected", fd.get());
errno = 0;
return false;
}
}
- VLOG(RWX) << "readx: fd=" << fd << " wanted=" << len0 << " got=" << (len0 - len)
- << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
+ VLOG(RWX) << "readx: fd=" << fd.get() << " wanted=" << len0 << " got=" << (len0 - len) << " "
+ << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
return true;
}
-bool WriteFdExactly(int fd, const void* buf, size_t len) {
+bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len) {
const char* p = reinterpret_cast<const char*>(buf);
int r;
- VLOG(RWX) << "writex: fd=" << fd << " len=" << len
- << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
+ VLOG(RWX) << "writex: fd=" << fd.get() << " len=" << len << " "
+ << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
while (len > 0) {
r = adb_write(fd, p, len);
if (r == -1) {
- D("writex: fd=%d error %d: %s", fd, errno, strerror(errno));
+ D("writex: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
if (errno == EAGAIN) {
std::this_thread::yield();
continue;
} else if (errno == EPIPE) {
- D("writex: fd=%d disconnected", fd);
+ D("writex: fd=%d disconnected", fd.get());
errno = 0;
return false;
} else {
@@ -129,15 +129,15 @@
return true;
}
-bool WriteFdExactly(int fd, const char* str) {
+bool WriteFdExactly(borrowed_fd fd, const char* str) {
return WriteFdExactly(fd, str, strlen(str));
}
-bool WriteFdExactly(int fd, const std::string& str) {
+bool WriteFdExactly(borrowed_fd fd, const std::string& str) {
return WriteFdExactly(fd, str.c_str(), str.size());
}
-bool WriteFdFmt(int fd, const char* fmt, ...) {
+bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) {
std::string str;
va_list ap;
@@ -148,7 +148,7 @@
return WriteFdExactly(fd, str);
}
-bool ReadOrderlyShutdown(int fd) {
+bool ReadOrderlyShutdown(borrowed_fd fd) {
char buf[16];
// Only call this function if you're sure that the peer does
@@ -178,7 +178,7 @@
// data. We don't repeatedly call adb_read() until we get zero because
// we don't know how long that would take, but we do know that the
// caller wants to close the socket soon.
- VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+ VLOG(RWX) << "ReadOrderlyShutdown(" << fd.get() << ") unexpectedly read "
<< dump_hex(buf, result);
// Shutdown the socket to prevent the caller from reading or writing to
// it which doesn't make sense if we just read and discarded some data.
diff --git a/adb/adb_io.h b/adb/adb_io.h
index d6e65d8..9628946 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -25,16 +25,16 @@
#include "adb_unique_fd.h"
// Sends the protocol "OKAY" message.
-bool SendOkay(int fd);
+bool SendOkay(borrowed_fd fd);
// Sends the protocol "FAIL" message, with the given failure reason.
-bool SendFail(int fd, std::string_view reason);
+bool SendFail(borrowed_fd fd, std::string_view reason);
// Writes a protocol-format string; a four hex digit length followed by the string data.
-bool SendProtocolString(int fd, std::string_view s);
+bool SendProtocolString(borrowed_fd 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);
+bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error);
// Reads exactly len bytes from fd into buf.
//
@@ -42,7 +42,7 @@
// were read. If EOF was found, errno will be set to 0.
//
// If this function fails, the contents of buf are undefined.
-bool ReadFdExactly(int fd, void* buf, size_t len);
+bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len);
// Given a client socket, wait for orderly/graceful shutdown. Call this:
//
@@ -60,19 +60,19 @@
// connect()s from the client to fail with WSAEADDRINUSE on Windows.
// Returns true if it is sure that orderly/graceful shutdown has occurred with
// no additional data read from the server.
-bool ReadOrderlyShutdown(int fd);
+bool ReadOrderlyShutdown(borrowed_fd fd);
// Writes exactly len bytes from buf to fd.
//
// Returns false if there is an error or if the fd was closed before the write
// completed. If the other end of the fd (such as in a socket, pipe, or fifo),
// is closed, errno will be set to 0.
-bool WriteFdExactly(int fd, const void* buf, size_t len);
+bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len);
// Same as above, but for strings.
-bool WriteFdExactly(int fd, const char* s);
-bool WriteFdExactly(int fd, const std::string& s);
+bool WriteFdExactly(borrowed_fd fd, const char* s);
+bool WriteFdExactly(borrowed_fd fd, const std::string& s);
// Same as above, but formats the string to send.
-bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
#endif /* ADB_IO_H */
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index d47213d..b6c910a 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -32,6 +32,8 @@
using unique_fd = android::base::unique_fd;
#endif
+using android::base::borrowed_fd;
+
template <typename T>
int adb_close(const android::base::unique_fd_impl<T>&)
__attribute__((__unavailable__("adb_close called on unique_fd")));
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 9791769..cf5fbc8 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -234,15 +234,15 @@
#if !defined(_WIN32)
// Windows version provided in sysdeps_win32.cpp
-bool set_file_block_mode(int fd, bool block) {
- int flags = fcntl(fd, F_GETFL, 0);
+bool set_file_block_mode(borrowed_fd fd, bool block) {
+ int flags = fcntl(fd.get(), F_GETFL, 0);
if (flags == -1) {
- PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
+ PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd.get();
return false;
}
flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
- if (fcntl(fd, F_SETFL, flags) != 0) {
- PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
+ if (fcntl(fd.get(), F_SETFL, flags) != 0) {
+ PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd.get() << ", flags " << flags;
return false;
}
return true;
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 5800a62..faad03d 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -26,6 +26,7 @@
#include <android-base/macros.h>
#include "adb.h"
+#include "adb_unique_fd.h"
void close_stdin();
@@ -51,7 +52,7 @@
[[noreturn]] void error_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
[[noreturn]] void perror_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
-bool set_file_block_mode(int fd, bool block);
+bool set_file_block_mode(borrowed_fd fd, bool block);
// Given forward/reverse targets, returns true if they look sane. If an error is found, fills
// |error| and returns false.
@@ -141,11 +142,3 @@
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 bd676c2..cdca3aa 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -149,13 +149,13 @@
TEST(adb_utils, set_file_block_mode) {
unique_fd fd(adb_open("/dev/null", O_RDWR | O_APPEND));
ASSERT_GE(fd, 0);
- int flags = fcntl(fd, F_GETFL, 0);
+ int flags = fcntl(fd.get(), F_GETFL, 0);
ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
ASSERT_TRUE(set_file_block_mode(fd, false));
- int new_flags = fcntl(fd, F_GETFL, 0);
+ int new_flags = fcntl(fd.get(), F_GETFL, 0);
ASSERT_EQ(flags | O_NONBLOCK, new_flags);
ASSERT_TRUE(set_file_block_mode(fd, true));
- new_flags = fcntl(fd, F_GETFL, 0);
+ new_flags = fcntl(fd.get(), F_GETFL, 0);
ASSERT_EQ(flags, new_flags);
}
#endif
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 5a7bc8d..7e408a8 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -128,7 +128,7 @@
return result;
}
-bool adb_status(int fd, std::string* error) {
+bool adb_status(borrowed_fd fd, std::string* error) {
char buf[5];
if (!ReadFdExactly(fd, buf, 4)) {
*error = perror_str("protocol fault (couldn't read status)");
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index 8d32c93..fe1e584 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -16,13 +16,14 @@
#pragma once
-#include "adb.h"
-#include "sysdeps.h"
-#include "transport.h"
-
#include <optional>
#include <string>
+#include "adb.h"
+#include "adb_unique_fd.h"
+#include "sysdeps.h"
+#include "transport.h"
+
// Explicitly check the adb server version.
// All of the commands below do this implicitly.
// Only the first invocation of this function will check the server version.
@@ -64,7 +65,7 @@
// Reads a standard adb status response (OKAY|FAIL) and returns true in the
// event of OKAY, false in the event of FAIL or protocol error.
-bool adb_status(int fd, std::string* _Nonnull error);
+bool adb_status(borrowed_fd fd, std::string* _Nonnull error);
// Create a host command corresponding to selected transport type/serial.
std::string format_host_command(const char* _Nonnull command);
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 3eee426..ed6a9a8 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -53,6 +53,25 @@
*new std::map<std::string, std::shared_ptr<RSA>>;
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
+static std::string get_user_info() {
+ std::string hostname;
+ if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+ char buf[64];
+ if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
+#endif
+ if (hostname.empty()) hostname = "unknown";
+
+ std::string username;
+ if (getenv("LOGNAME")) username = getenv("LOGNAME");
+#if !defined(_WIN32)
+ if (username.empty() && getlogin()) username = getlogin();
+#endif
+ if (username.empty()) hostname = "unknown";
+
+ return " " + username + "@" + hostname;
+}
+
static bool calculate_public_key(std::string* out, RSA* private_key) {
uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
@@ -70,6 +89,7 @@
size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
sizeof(binary_key_data));
out->resize(actual_length);
+ out->append(get_user_info());
return true;
}
@@ -79,6 +99,7 @@
mode_t old_mask;
FILE *f = nullptr;
int ret = 0;
+ std::string pubkey;
EVP_PKEY* pkey = EVP_PKEY_new();
BIGNUM* exponent = BN_new();
@@ -92,6 +113,11 @@
RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
EVP_PKEY_set1_RSA(pkey, rsa);
+ if (!calculate_public_key(&pubkey, rsa)) {
+ LOG(ERROR) << "failed to calculate public key";
+ goto out;
+ }
+
old_mask = umask(077);
f = fopen(file.c_str(), "w");
@@ -104,7 +130,12 @@
umask(old_mask);
if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
- D("Failed to write key");
+ LOG(ERROR) << "Failed to write key";
+ goto out;
+ }
+
+ if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
+ PLOG(ERROR) << "failed to write public key";
goto out;
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index e2a17c5..7211f72 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -170,9 +170,6 @@
" remove this app package from the device\n"
" '-k': keep the data and cache directories\n"
"\n"
- "backup/restore:\n"
- " to show usage run \"adb shell bu help\"\n"
- "\n"
"debugging:\n"
" bugreport [PATH]\n"
" write bugreport to given PATH [default=bugreport.zip];\n"
@@ -262,7 +259,7 @@
// stdout/stderr are routed independently and the remote exit code will be
// returned.
// if |callback| is non-null, stdout/stderr output will be handled by it.
-int read_and_dump(int fd, bool use_shell_protocol = false,
+int read_and_dump(borrowed_fd fd, bool use_shell_protocol = false,
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
int exit_code = 0;
if (fd < 0) return exit_code;
@@ -305,9 +302,9 @@
}
length = protocol->data_length();
} else {
- D("read_and_dump(): pre adb_read(fd=%d)", fd);
+ D("read_and_dump(): pre adb_read(fd=%d)", fd.get());
length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
- D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
+ D("read_and_dump(): post adb_read(fd=%d): length=%d", fd.get(), length);
if (length <= 0) {
break;
}
@@ -887,7 +884,7 @@
return -1;
}
fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
- return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
+ return adb_sideload_legacy(filename, package_fd.get(), static_cast<int>(sb.st_size));
}
int opt = SIDELOAD_HOST_BLOCK_SIZE;
@@ -1142,7 +1139,7 @@
// If we were using a specific transport ID, there's nothing we can wait for.
if (previous_id == 0) {
adb_set_transport(previous_type, previous_serial, 0);
- wait_for_device("wait-for-device", 3000ms);
+ wait_for_device("wait-for-device", 6000ms);
}
return true;
@@ -1207,14 +1204,14 @@
return send_shell_command(cmd);
}
-static void write_zeros(int bytes, int fd) {
+static void write_zeros(int bytes, borrowed_fd fd) {
int old_stdin_mode = -1;
int old_stdout_mode = -1;
std::vector<char> buf(bytes);
- D("write_zeros(%d) -> %d", bytes, fd);
+ D("write_zeros(%d) -> %d", bytes, fd.get());
- stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+ stdinout_raw_prologue(-1, fd.get(), old_stdin_mode, old_stdout_mode);
if (fd == STDOUT_FILENO) {
fwrite(buf.data(), 1, bytes, stdout);
@@ -1223,12 +1220,14 @@
adb_write(fd, buf.data(), bytes);
}
- stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+ stdinout_raw_prologue(-1, fd.get(), old_stdin_mode, old_stdout_mode);
D("write_zeros() finished");
}
static int backup(int argc, const char** argv) {
+ fprintf(stdout, "WARNING: adb backup is deprecated and may be removed in a future release\n");
+
const char* filename = "backup.ab";
/* find, extract, and use any -f argument */
@@ -1278,6 +1277,8 @@
}
static int restore(int argc, const char** argv) {
+ fprintf(stdout, "WARNING: adb restore is deprecated and may be removed in a future release\n");
+
if (argc != 2) error_exit("restore requires an argument");
const char* filename = argv[1];
@@ -1668,17 +1669,29 @@
return 0;
}
} else if (!strcmp(argv[0], "rescue")) {
+ // adb rescue getprop
// adb rescue getprop <prop>
// adb rescue install <filename>
// adb rescue wipe userdata
- if (argc != 3) error_exit("rescue requires two arguments");
+ if (argc < 2) error_exit("rescue requires at least one argument");
if (!strcmp(argv[1], "getprop")) {
- return adb_connect_command(android::base::StringPrintf("rescue-getprop:%s", argv[2]));
+ if (argc == 2) {
+ return adb_connect_command("rescue-getprop:");
+ }
+ if (argc == 3) {
+ return adb_connect_command(
+ android::base::StringPrintf("rescue-getprop:%s", argv[2]));
+ }
+ error_exit("invalid rescue getprop arguments");
} else if (!strcmp(argv[1], "install")) {
+ if (argc != 3) error_exit("rescue install requires two arguments");
if (adb_sideload_install(argv[2], true /* rescue_mode */) != 0) {
return 1;
}
- } else if (!strcmp(argv[1], "wipe") && !strcmp(argv[2], "userdata")) {
+ } else if (!strcmp(argv[1], "wipe")) {
+ if (argc != 3 || strcmp(argv[2], "userdata") != 0) {
+ error_exit("invalid rescue wipe arguments");
+ }
return adb_wipe_devices();
} else {
error_exit("invalid rescue argument");
@@ -1810,7 +1823,7 @@
if (argc < 2) error_exit("install-multiple requires an argument");
return install_multiple_app(argc, argv);
} else if (!strcmp(argv[0], "install-multi-package")) {
- if (argc < 3) error_exit("install-multi-package requires an argument");
+ if (argc < 2) error_exit("install-multi-package requires an argument");
return install_multi_package(argc, argv);
} else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) error_exit("uninstall requires an argument");
diff --git a/adb/client/console.cpp b/adb/client/console.cpp
index 1dbb6e2..d10f4de 100644
--- a/adb/client/console.cpp
+++ b/adb/client/console.cpp
@@ -71,7 +71,7 @@
return -1;
}
- int port;
+ int port = -1;
size_t emulator_count = 0;
for (const auto& device : android::base::Split(devices, "\n")) {
if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index eeac41a..425438e 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -17,6 +17,7 @@
#include <sys/wait.h>
#include <android-base/cmsg.h>
+#include <android-base/strings.h>
#include <cmd.h>
#include "adb.h"
@@ -28,11 +29,11 @@
class AdbFdTextOutput : public android::TextOutput {
public:
- explicit AdbFdTextOutput(int fd) : mFD(fd) {}
+ explicit AdbFdTextOutput(borrowed_fd fd) : fd_(fd) {}
private:
android::status_t print(const char* txt, size_t len) override {
- return WriteFdExactly(mFD, txt, len) ? android::OK : -errno;
+ return WriteFdExactly(fd_, txt, len) ? android::OK : -errno;
}
void moveIndent(int delta) override { /*not implemented*/
}
@@ -43,7 +44,7 @@
}
private:
- int mFD;
+ borrowed_fd fd_;
};
std::vector<std::string_view> parseCmdArgs(std::string_view args) {
@@ -67,10 +68,11 @@
} // namespace
-static int execCmd(std::string_view args, int in, int out, int err) {
+static int execCmd(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err) {
AdbFdTextOutput oin(out);
AdbFdTextOutput oerr(err);
- return cmdMain(parseCmdArgs(args), oin, oerr, in, out, err, RunMode::kLibrary);
+ return cmdMain(parseCmdArgs(args), oin, oerr, in.get(), out.get(), err.get(),
+ RunMode::kLibrary);
}
int main(int argc, char* const argv[]) {
@@ -87,9 +89,9 @@
std::string_view name = data;
auto protocol = SubprocessProtocol::kShell;
- if (ConsumePrefix(&name, "abb:")) {
+ if (android::base::ConsumePrefix(&name, "abb:")) {
protocol = SubprocessProtocol::kShell;
- } else if (ConsumePrefix(&name, "abb_exec:")) {
+ } else if (android::base::ConsumePrefix(&name, "abb_exec:")) {
protocol = SubprocessProtocol::kNone;
} else {
LOG(FATAL) << "Unknown command prefix for abb: " << data;
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index e82a51f..0e70d47 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -229,17 +229,13 @@
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) {
+ int rc;
syncmsg msg;
__android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
unique_fd fd(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
- if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) <
- 0) {
- D("[ Failed to fadvise: %d ]", errno);
- }
-
if (fd < 0 && errno == ENOENT) {
if (!secure_mkdirs(Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
@@ -270,6 +266,12 @@
fchmod(fd.get(), mode);
}
+ rc = posix_fadvise(fd.get(), 0, 0,
+ POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
+ if (rc != 0) {
+ D("[ Failed to fadvise: %s ]", strerror(rc));
+ }
+
while (true) {
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
@@ -464,8 +466,9 @@
return false;
}
- if (posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
- D("[ Failed to fadvise: %d ]", errno);
+ int rc = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+ if (rc != 0) {
+ D("[ Failed to fadvise: %s ]", strerror(rc));
}
syncmsg msg;
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 66bfc0d..cd9b669 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -325,12 +325,12 @@
addrlen = pathlen + sizeof(addr.sun_family);
- if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
+ if (bind(s.get(), reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
D("could not bind vm debug control socket: %d: %s", errno, strerror(errno));
return -1;
}
- if (listen(s, 4) < 0) {
+ if (listen(s.get(), 4) < 0) {
D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
return -1;
}
diff --git a/adb/daemon/reboot_service.cpp b/adb/daemon/reboot_service.cpp
index a5a11b8..13398af 100644
--- a/adb/daemon/reboot_service.cpp
+++ b/adb/daemon/reboot_service.cpp
@@ -58,7 +58,7 @@
sockaddr_un addr = {.sun_family = AF_UNIX};
strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
- if (connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+ if (connect(sock.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
PLOG(ERROR) << "Couldn't connect to recovery socket";
return;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index b0cc450..e6f4499 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -223,13 +223,13 @@
return create_jdwp_service_socket();
} else if (name == "track-jdwp") {
return create_jdwp_tracker_service_socket();
- } else if (ConsumePrefix(&name, "sink:")) {
+ } else if (android::base::ConsumePrefix(&name, "sink:")) {
uint64_t byte_count = 0;
if (!ParseUint(&byte_count, name)) {
return nullptr;
}
return new SinkSocket(byte_count);
- } else if (ConsumePrefix(&name, "source:")) {
+ } else if (android::base::ConsumePrefix(&name, "source:")) {
uint64_t byte_count = 0;
if (!ParseUint(&byte_count, name)) {
return nullptr;
@@ -250,11 +250,11 @@
#if defined(__ANDROID__)
if (name.starts_with("framebuffer:")) {
return create_service_thread("fb", framebuffer_service);
- } else if (ConsumePrefix(&name, "remount:")) {
+ } else if (android::base::ConsumePrefix(&name, "remount:")) {
std::string arg(name);
return create_service_thread("remount",
std::bind(remount_service, std::placeholders::_1, arg));
- } else if (ConsumePrefix(&name, "reboot:")) {
+ } else if (android::base::ConsumePrefix(&name, "reboot:")) {
std::string arg(name);
return create_service_thread("reboot",
std::bind(reboot_service, std::placeholders::_1, arg));
@@ -262,7 +262,7 @@
return create_service_thread("root", restart_root_service);
} else if (name.starts_with("unroot:")) {
return create_service_thread("unroot", restart_unroot_service);
- } else if (ConsumePrefix(&name, "backup:")) {
+ } else if (android::base::ConsumePrefix(&name, "backup:")) {
std::string cmd = "/system/bin/bu backup ";
cmd += name;
return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
@@ -275,7 +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 (ConsumePrefix(&name, "tcpip:")) {
+ } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
std::string str(name);
int port;
@@ -289,22 +289,22 @@
}
#endif
- if (ConsumePrefix(&name, "dev:")) {
+ if (android::base::ConsumePrefix(&name, "dev:")) {
return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
- } else if (ConsumePrefix(&name, "jdwp:")) {
+ } else if (android::base::ConsumePrefix(&name, "jdwp:")) {
pid_t pid;
if (!ParseUint(&pid, name)) {
return unique_fd{};
}
return create_jdwp_connection_fd(pid);
- } else if (ConsumePrefix(&name, "shell")) {
+ } else if (android::base::ConsumePrefix(&name, "shell")) {
return ShellService(name, transport);
- } else if (ConsumePrefix(&name, "exec:")) {
+ } else if (android::base::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 (ConsumePrefix(&name, "reverse:")) {
+ } else if (android::base::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 889229f..4fbccdb 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -58,7 +58,7 @@
}
int OFF = 0;
- bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
+ bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
return result;
}
@@ -111,8 +111,11 @@
WriteFdFmt(fd, "%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
}
} else if (errno) {
- WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
- mount_point, strerror(errno));
+ int expected_errno = enable ? EBUSY : ENOENT;
+ if (errno != expected_errno) {
+ WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n",
+ enable ? "teardown" : "setup", mount_point, strerror(errno));
+ }
}
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
return true;
@@ -194,7 +197,7 @@
}
if (!android::base::GetBoolProperty("ro.secure", false)) {
- overlayfs_setup(fd, enable);
+ overlayfs_setup(fd.get(), enable);
WriteFdExactly(fd.get(), "verity not enabled - ENG build\n");
return;
}
@@ -239,7 +242,7 @@
}
}
}
- if (!any_changed) any_changed = overlayfs_setup(fd, enable);
+ if (!any_changed) any_changed = overlayfs_setup(fd.get(), enable);
if (any_changed) {
WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 3c8f393..de97068 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -114,7 +114,7 @@
namespace {
// Reads from |fd| until close or failure.
-std::string ReadAll(int fd) {
+std::string ReadAll(borrowed_fd fd) {
char buffer[512];
std::string received;
@@ -317,9 +317,10 @@
child_stdinout_sfd.reset(OpenPtyChildFd(pts_name, &child_error_sfd));
}
- dup2(child_stdinout_sfd, STDIN_FILENO);
- dup2(child_stdinout_sfd, STDOUT_FILENO);
- dup2(child_stderr_sfd != -1 ? child_stderr_sfd : child_stdinout_sfd, STDERR_FILENO);
+ dup2(child_stdinout_sfd.get(), STDIN_FILENO);
+ dup2(child_stdinout_sfd.get(), STDOUT_FILENO);
+ dup2(child_stderr_sfd != -1 ? child_stderr_sfd.get() : child_stdinout_sfd.get(),
+ STDERR_FILENO);
// exec doesn't trigger destructors, close the FDs manually.
stdinout_sfd_.reset(-1);
@@ -415,7 +416,7 @@
}
} else {
// Raw protocol doesn't support multiple output streams, so combine stdout and stderr.
- child_stderr_sfd.reset(dup(child_stdinout_sfd));
+ child_stderr_sfd.reset(dup(child_stdinout_sfd.get()));
}
D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
@@ -537,7 +538,7 @@
FD_ZERO(&master_write_set);
for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
if (*sfd != -1) {
- FD_SET(*sfd, &master_read_set);
+ FD_SET(sfd->get(), &master_read_set);
}
}
@@ -547,8 +548,8 @@
unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
if (dead_sfd) {
D("closing FD %d", dead_sfd->get());
- FD_CLR(*dead_sfd, &master_read_set);
- FD_CLR(*dead_sfd, &master_write_set);
+ FD_CLR(dead_sfd->get(), &master_read_set);
+ FD_CLR(dead_sfd->get(), &master_write_set);
if (dead_sfd == &protocol_sfd_) {
// Using SIGHUP is a decent general way to indicate that the
// controlling process is going away. If specific signals are
@@ -573,7 +574,7 @@
namespace {
inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
- return sfd != -1 && FD_ISSET(sfd, set);
+ return sfd != -1 && FD_ISSET(sfd.get(), set);
}
} // namespace
@@ -581,7 +582,8 @@
unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
fd_set* master_write_set_ptr) {
fd_set read_set, write_set;
- int select_n = std::max(std::max(protocol_sfd_, stdinout_sfd_), stderr_sfd_) + 1;
+ int select_n =
+ std::max(std::max(protocol_sfd_.get(), stdinout_sfd_.get()), stderr_sfd_.get()) + 1;
unique_fd* dead_sfd = nullptr;
// Keep calling select() and passing data until an FD closes/errors.
@@ -614,8 +616,8 @@
dead_sfd = PassInput();
// If we didn't finish writing, block on stdin write.
if (input_bytes_left_) {
- FD_CLR(protocol_sfd_, master_read_set_ptr);
- FD_SET(stdinout_sfd_, master_write_set_ptr);
+ FD_CLR(protocol_sfd_.get(), master_read_set_ptr);
+ FD_SET(stdinout_sfd_.get(), master_write_set_ptr);
}
}
@@ -624,8 +626,8 @@
dead_sfd = PassInput();
// If we finished writing, go back to blocking on protocol read.
if (!input_bytes_left_) {
- FD_SET(protocol_sfd_, master_read_set_ptr);
- FD_CLR(stdinout_sfd_, master_write_set_ptr);
+ FD_SET(protocol_sfd_.get(), master_read_set_ptr);
+ FD_CLR(stdinout_sfd_.get(), master_write_set_ptr);
}
}
} // while (!dead_sfd)
@@ -639,7 +641,7 @@
if (!input_->Read()) {
// Read() uses ReadFdExactly() which sets errno to 0 on EOF.
if (errno != 0) {
- PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+ PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_.get();
}
return &protocol_sfd_;
}
@@ -655,7 +657,7 @@
ws.ws_col = cols;
ws.ws_xpixel = x_pixels;
ws.ws_ypixel = y_pixels;
- ioctl(stdinout_sfd_, TIOCSWINSZ, &ws);
+ ioctl(stdinout_sfd_.get(), TIOCSWINSZ, &ws);
}
break;
case ShellProtocol::kIdStdin:
@@ -666,8 +668,7 @@
if (adb_shutdown(stdinout_sfd_, SHUT_WR) == 0) {
return nullptr;
}
- PLOG(ERROR) << "failed to shutdown writes to FD "
- << stdinout_sfd_;
+ PLOG(ERROR) << "failed to shutdown writes to FD " << stdinout_sfd_.get();
return &stdinout_sfd_;
} else {
// PTYs can't close just input, so rather than close the
@@ -688,7 +689,7 @@
int bytes = adb_write(stdinout_sfd_, input_->data() + index, input_bytes_left_);
if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
if (bytes < 0) {
- PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_;
+ PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_.get();
}
// stdin is done, mark this packet as finished and we'll just start
// dumping any further data received from the protocol FD.
@@ -708,14 +709,14 @@
// read() returns EIO if a PTY closes; don't report this as an error,
// it just means the subprocess completed.
if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
- PLOG(ERROR) << "error reading output FD " << *sfd;
+ PLOG(ERROR) << "error reading output FD " << sfd->get();
}
return sfd;
}
if (bytes > 0 && !output_->Write(id, bytes)) {
if (errno != 0) {
- PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+ PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_.get();
}
return &protocol_sfd_;
}
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 3abd958..030228c 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -48,7 +48,7 @@
// Sets up in/out and error streams to emulate shell-like behavior.
//
// 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);
+using Command = int(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err);
unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
// Create a pipe containing the error.
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
index dc79d12..cdd8dbe 100644
--- a/adb/daemon/shell_service_test.cpp
+++ b/adb/daemon/shell_service_test.cpp
@@ -77,7 +77,7 @@
namespace {
// Reads raw data from |fd| until it closes or errors.
-std::string ReadRaw(int fd) {
+std::string ReadRaw(borrowed_fd fd) {
char buffer[1024];
char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
@@ -93,12 +93,12 @@
// Reads shell protocol data from |fd| until it closes or errors. Fills
// |stdout| and |stderr| with their respective data, and returns the exit code
// read from the protocol or -1 if an exit code packet was not received.
-int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
+int ReadShellProtocol(borrowed_fd fd, std::string* stdout, std::string* stderr) {
int exit_code = -1;
stdout->clear();
stderr->clear();
- auto protocol = std::make_unique<ShellProtocol>(fd);
+ auto protocol = std::make_unique<ShellProtocol>(fd.get());
while (protocol->Read()) {
switch (protocol->id()) {
case ShellProtocol::kIdStdout:
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index 07b4ba8..338d776 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -37,9 +37,12 @@
// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+#define USB_EXT_PROP_UNICODE 1
+
#define cpu_to_le16(x) htole16(x)
#define cpu_to_le32(x) htole32(x)
+// clang-format off
struct func_desc {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio source;
@@ -64,6 +67,34 @@
struct func_desc fs_descs, hs_descs;
} __attribute__((packed));
+template <size_t PropertyNameLength, size_t PropertyDataLength>
+struct usb_os_desc_ext_prop {
+ uint32_t dwSize = sizeof(*this);
+ uint32_t dwPropertyDataType = cpu_to_le32(USB_EXT_PROP_UNICODE);
+
+ // Property name and value are transmitted as UTF-16, but the kernel only
+ // accepts ASCII values and performs the conversion for us.
+ uint16_t wPropertyNameLength = cpu_to_le16(PropertyNameLength);
+ char bPropertyName[PropertyNameLength];
+
+ uint32_t dwPropertyDataLength = cpu_to_le32(PropertyDataLength);
+ char bProperty[PropertyDataLength];
+} __attribute__((packed));
+
+using usb_os_desc_guid_t = usb_os_desc_ext_prop<20, 39>;
+usb_os_desc_guid_t os_desc_guid = {
+ .bPropertyName = "DeviceInterfaceGUID",
+ .bProperty = "{64379D6C-D531-4BED-BBEC-5A16FC07D6BC}",
+};
+
+struct usb_ext_prop_values {
+ usb_os_desc_guid_t guid;
+} __attribute__((packed));
+
+usb_ext_prop_values os_prop_values = {
+ .guid = os_desc_guid,
+};
+
struct desc_v2 {
struct usb_functionfs_descs_head_v2 header;
// The rest of the structure depends on the flags in the header.
@@ -75,9 +106,10 @@
struct ss_func_desc ss_descs;
struct usb_os_desc_header os_header;
struct usb_ext_compat_desc os_desc;
+ struct usb_os_desc_header os_prop_header;
+ struct usb_ext_prop_values os_prop_values;
} __attribute__((packed));
-// clang-format off
static struct func_desc fs_descriptors = {
.intf = {
.bLength = sizeof(fs_descriptors.intf),
@@ -172,13 +204,13 @@
struct usb_ext_compat_desc os_desc_compat = {
.bFirstInterfaceNumber = 0,
.Reserved1 = cpu_to_le32(1),
- .CompatibleID = {0},
+ .CompatibleID = { 'W', 'I', 'N', 'U', 'S', 'B', '\0', '\0'},
.SubCompatibleID = {0},
.Reserved2 = {0},
};
static struct usb_os_desc_header os_desc_header = {
- .interface = cpu_to_le32(1),
+ .interface = cpu_to_le32(0),
.dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
.bcdVersion = cpu_to_le32(1),
.wIndex = cpu_to_le32(4),
@@ -186,6 +218,14 @@
.Reserved = cpu_to_le32(0),
};
+static struct usb_os_desc_header os_prop_header = {
+ .interface = cpu_to_le32(0),
+ .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_prop_values)),
+ .bcdVersion = cpu_to_le32(1),
+ .wIndex = cpu_to_le32(5),
+ .wCount = cpu_to_le16(1),
+};
+
#define STR_INTERFACE_ "ADB Interface"
static const struct {
@@ -221,12 +261,14 @@
v2_descriptor.fs_count = 3;
v2_descriptor.hs_count = 3;
v2_descriptor.ss_count = 5;
- v2_descriptor.os_count = 1;
+ v2_descriptor.os_count = 2;
v2_descriptor.fs_descs = fs_descriptors;
v2_descriptor.hs_descs = hs_descriptors;
v2_descriptor.ss_descs = ss_descriptors;
v2_descriptor.os_header = os_desc_header;
v2_descriptor.os_desc = os_desc_compat;
+ v2_descriptor.os_prop_header = os_prop_header;
+ v2_descriptor.os_prop_values = os_prop_values;
if (out_control->get() < 0) { // might have already done this before
LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
diff --git a/adb/fastdeploy/deployagent/deployagent.sh b/adb/fastdeploy/deployagent/deployagent.sh
index 4f17eb7..91576ca 100755
--- a/adb/fastdeploy/deployagent/deployagent.sh
+++ b/adb/fastdeploy/deployagent/deployagent.sh
@@ -1,7 +1,4 @@
-# Script to start "deployagent" on the device, which has a very rudimentary
-# shell.
-#
+#!/system/bin/sh
base=/data/local/tmp
export CLASSPATH=$base/deployagent.jar
exec app_process $base com.android.fastdeploy.DeployAgent "$@"
-
diff --git a/adb/services.cpp b/adb/services.cpp
index 335ffc4..6185aa6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -100,7 +100,7 @@
ConnectionState state;
};
-static void wait_for_state(int fd, state_info* sinfo) {
+static void wait_for_state(unique_fd fd, state_info* sinfo) {
D("wait_for_state %d", sinfo->state);
while (true) {
@@ -122,7 +122,7 @@
}
if (!is_ambiguous) {
- adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+ adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
int rc = adb_poll(&pfd, 1, 100);
if (rc < 0) {
SendFail(fd, error);
@@ -140,7 +140,6 @@
}
}
- adb_close(fd);
D("wait_for_state is done");
}
@@ -203,7 +202,7 @@
return create_device_tracker(false);
} else if (name == "track-devices-l") {
return create_device_tracker(true);
- } else if (ConsumePrefix(&name, "wait-for-")) {
+ } else if (android::base::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));
@@ -213,11 +212,11 @@
sinfo->serial = serial;
sinfo->transport_id = transport_id;
- if (ConsumePrefix(&name, "local")) {
+ if (android::base::ConsumePrefix(&name, "local")) {
sinfo->transport_type = kTransportLocal;
- } else if (ConsumePrefix(&name, "usb")) {
+ } else if (android::base::ConsumePrefix(&name, "usb")) {
sinfo->transport_type = kTransportUsb;
- } else if (ConsumePrefix(&name, "any")) {
+ } else if (android::base::ConsumePrefix(&name, "any")) {
sinfo->transport_type = kTransportAny;
} else {
return nullptr;
@@ -241,11 +240,10 @@
return nullptr;
}
- unique_fd fd = create_service_thread("wait", [sinfo](int fd) {
- wait_for_state(fd, sinfo.get());
- });
+ unique_fd fd = create_service_thread(
+ "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
return create_local_socket(std::move(fd));
- } else if (ConsumePrefix(&name, "connect:")) {
+ } else if (android::base::ConsumePrefix(&name, "connect:")) {
std::string host(name);
unique_fd fd = create_service_thread(
"connect", std::bind(connect_service, std::placeholders::_1, host));
diff --git a/adb/shell_protocol.h b/adb/shell_protocol.h
index 2c82689..4aab813 100644
--- a/adb/shell_protocol.h
+++ b/adb/shell_protocol.h
@@ -21,6 +21,7 @@
#include <android-base/macros.h>
#include "adb.h"
+#include "adb_unique_fd.h"
// Class to send and receive shell protocol packets.
//
@@ -60,7 +61,7 @@
// should be dynamically allocated on the heap instead.
//
// |fd| is an open file descriptor to be used to send or receive packets.
- explicit ShellProtocol(int fd);
+ explicit ShellProtocol(borrowed_fd fd);
virtual ~ShellProtocol();
// Returns a pointer to the data buffer.
@@ -103,7 +104,7 @@
kHeaderSize = sizeof(Id) + sizeof(length_t)
};
- int fd_;
+ borrowed_fd fd_;
char buffer_[kBufferSize];
size_t data_length_ = 0, bytes_left_ = 0;
diff --git a/adb/shell_service_protocol.cpp b/adb/shell_service_protocol.cpp
index 13b66ec..95afaff 100644
--- a/adb/shell_service_protocol.cpp
+++ b/adb/shell_service_protocol.cpp
@@ -22,7 +22,7 @@
#include "adb_io.h"
-ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
+ShellProtocol::ShellProtocol(borrowed_fd fd) : fd_(fd) {
buffer_[0] = kIdInvalid;
}
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index de4fff9..1333724 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -314,14 +314,14 @@
addr.svm_port = port == 0 ? VMADDR_PORT_ANY : port;
addr.svm_cid = VMADDR_CID_ANY;
socklen_t addr_len = sizeof(addr);
- if (bind(serverfd, reinterpret_cast<struct sockaddr*>(&addr), addr_len)) {
+ if (bind(serverfd.get(), reinterpret_cast<struct sockaddr*>(&addr), addr_len)) {
return -1;
}
- if (listen(serverfd, 4)) {
+ if (listen(serverfd.get(), 4)) {
return -1;
}
if (serverfd >= 0 && resolved_port) {
- if (getsockname(serverfd, reinterpret_cast<sockaddr*>(&addr), &addr_len) == 0) {
+ if (getsockname(serverfd.get(), reinterpret_cast<sockaddr*>(&addr), &addr_len) == 0) {
*resolved_port = addr.svm_port;
} else {
return -1;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8a2bf9a..75993b3 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -31,6 +31,8 @@
#include <string>
#include <vector>
+#include <android-base/strings.h>
+
#if !ADB_HOST
#include <android-base/properties.h>
#include <log/log_properties.h>
@@ -755,26 +757,26 @@
#if ADB_HOST
service = std::string_view(s->smart_socket_data).substr(4);
- if (ConsumePrefix(&service, "host-serial:")) {
+ if (android::base::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 (ConsumePrefix(&service, "host-transport-id:")) {
+ } else if (android::base::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 (!ConsumePrefix(&service, ":")) {
+ if (!android::base::ConsumePrefix(&service, ":")) {
LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
return -1;
}
- } else if (ConsumePrefix(&service, "host-usb:")) {
+ } else if (android::base::ConsumePrefix(&service, "host-usb:")) {
type = kTransportUsb;
- } else if (ConsumePrefix(&service, "host-local:")) {
+ } else if (android::base::ConsumePrefix(&service, "host-local:")) {
type = kTransportLocal;
- } else if (ConsumePrefix(&service, "host:")) {
+ } else if (android::base::ConsumePrefix(&service, "host:")) {
type = kTransportAny;
} else {
service = std::string_view{};
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 15247e7..78abba5 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -36,6 +36,7 @@
#include <android-base/unique_fd.h>
#include <android-base/utf8.h>
+#include "adb_unique_fd.h"
#include "sysdeps/errno.h"
#include "sysdeps/network.h"
#include "sysdeps/stat.h"
@@ -76,42 +77,40 @@
extern int adb_thread_setname(const std::string& name);
-static __inline__ void close_on_exec(int fd)
-{
+static __inline__ void close_on_exec(borrowed_fd fd) {
/* nothing really */
}
-extern int adb_unlink(const char* path);
-#undef unlink
-#define unlink ___xxx_unlink
+extern int adb_unlink(const char* path);
+#undef unlink
+#define unlink ___xxx_unlink
extern int adb_mkdir(const std::string& path, int mode);
-#undef mkdir
-#define mkdir ___xxx_mkdir
+#undef mkdir
+#define mkdir ___xxx_mkdir
// See the comments for the !defined(_WIN32) versions of adb_*().
extern int adb_open(const char* path, int options);
extern int adb_creat(const char* path, int mode);
-extern int adb_read(int fd, void* buf, int len);
-extern int adb_write(int fd, const void* buf, int len);
-extern int64_t adb_lseek(int fd, int64_t pos, int where);
-extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
+extern int adb_read(borrowed_fd fd, void* buf, int len);
+extern int adb_write(borrowed_fd fd, const void* buf, int len);
+extern int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where);
+extern int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR);
extern int adb_close(int fd);
extern int adb_register_socket(SOCKET s);
// See the comments for the !defined(_WIN32) version of unix_close().
-static __inline__ int unix_close(int fd)
-{
+static __inline__ int unix_close(int fd) {
return close(fd);
}
-#undef close
-#define close ____xxx_close
+#undef close
+#define close ____xxx_close
// Like unix_read(), but may return EINTR.
-extern int unix_read_interruptible(int fd, void* buf, size_t len);
+extern int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len);
// See the comments for the !defined(_WIN32) version of unix_read().
-static __inline__ int unix_read(int fd, void* buf, size_t len) {
+static __inline__ int unix_read(borrowed_fd fd, void* buf, size_t len) {
return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
}
@@ -119,23 +118,21 @@
#define read ___xxx_read
// See the comments for the !defined(_WIN32) version of unix_write().
-static __inline__ int unix_write(int fd, const void* buf, size_t len)
-{
- return write(fd, buf, len);
+static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
+ return write(fd.get(), buf, len);
}
#undef write
#define write ___xxx_write
// See the comments for the !defined(_WIN32) version of unix_lseek().
-static __inline__ int unix_lseek(int fd, int pos, int where) {
- return lseek(fd, pos, where);
+static __inline__ int unix_lseek(borrowed_fd fd, int pos, int where) {
+ return lseek(fd.get(), pos, where);
}
#undef lseek
#define lseek ___xxx_lseek
// See the comments for the !defined(_WIN32) version of adb_open_mode().
-static __inline__ int adb_open_mode(const char* path, int options, int mode)
-{
+static __inline__ int adb_open_mode(const char* path, int options, int mode) {
return adb_open(path, options);
}
@@ -152,7 +149,7 @@
// with |fd| must have GENERIC_READ access (which console FDs have by default).
// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
// calling this function is unreliable and should not be used.
-int unix_isatty(int fd);
+int unix_isatty(borrowed_fd fd);
#define isatty ___xxx_isatty
int network_inaddr_any_server(int port, int type, std::string* error);
@@ -168,20 +165,21 @@
int network_connect(const std::string& host, int port, int type, int timeout,
std::string* error);
-extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
+extern int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen);
#undef accept
#define accept ___xxx_accept
// Returns the local port number of a bound socket, or -1 on failure.
-int adb_socket_get_local_port(int fd);
+int adb_socket_get_local_port(borrowed_fd fd);
-extern int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen);
+extern int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+ socklen_t optlen);
#undef setsockopt
#define setsockopt ___xxx_setsockopt
-extern int adb_socketpair( int sv[2] );
+extern int adb_socketpair(int sv[2]);
struct adb_pollfd {
int fd;
@@ -214,8 +212,7 @@
extern int adb_fputc(int ch, FILE* stream);
extern int adb_putchar(int ch);
extern int adb_puts(const char* buf);
-extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
- FILE* stream);
+extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
extern FILE* adb_fopen(const char* f, const char* m);
@@ -344,9 +341,8 @@
return c == '/';
}
-static __inline__ void close_on_exec(int fd)
-{
- fcntl( fd, F_SETFD, FD_CLOEXEC );
+static __inline__ void close_on_exec(borrowed_fd fd) {
+ fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
}
// Open a file and return a file descriptor that may be used with unix_read(),
@@ -374,12 +370,10 @@
// Similar to the two-argument adb_open(), but takes a mode parameter for file
// creation. See adb_open() for more info.
-static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
-{
- return TEMP_FAILURE_RETRY( open( pathname, options, mode ) );
+static __inline__ int adb_open_mode(const char* pathname, int options, int mode) {
+ return TEMP_FAILURE_RETRY(open(pathname, options, mode));
}
-
// Open a file and return a file descriptor that may be used with adb_read(),
// adb_write(), adb_close(), but not unix_read(), unix_write(), unix_close().
//
@@ -387,23 +381,21 @@
// sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
// and its CR/LF translation. The returned file descriptor should be used with
// adb_read(), adb_write(), adb_close(), etc.
-static __inline__ int adb_open( const char* pathname, int options )
-{
- int fd = TEMP_FAILURE_RETRY( open( pathname, options ) );
- if (fd < 0)
- return -1;
- close_on_exec( fd );
+static __inline__ int adb_open(const char* pathname, int options) {
+ int fd = TEMP_FAILURE_RETRY(open(pathname, options));
+ if (fd < 0) return -1;
+ close_on_exec(fd);
return fd;
}
-#undef open
-#define open ___xxx_open
+#undef open
+#define open ___xxx_open
-static __inline__ int adb_shutdown(int fd, int direction = SHUT_RDWR) {
- return shutdown(fd, direction);
+static __inline__ int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR) {
+ return shutdown(fd.get(), direction);
}
-#undef shutdown
-#define shutdown ____xxx_shutdown
+#undef shutdown
+#define shutdown ____xxx_shutdown
// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
// not designed to take a file descriptor from unix_open(). See the comments
@@ -411,81 +403,76 @@
__inline__ int adb_close(int fd) {
return close(fd);
}
-#undef close
-#define close ____xxx_close
+#undef close
+#define close ____xxx_close
// On Windows, ADB has an indirection layer for file descriptors. If we get a
// Win32 SOCKET object from an external library, we have to map it in to that
// indirection layer, which this does.
-__inline__ int adb_register_socket(int s) {
+__inline__ int adb_register_socket(int s) {
return s;
}
-static __inline__ int adb_read(int fd, void* buf, size_t len)
-{
- return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
+static __inline__ int adb_read(borrowed_fd fd, void* buf, size_t len) {
+ return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
}
// Like unix_read(), but does not handle EINTR.
-static __inline__ int unix_read_interruptible(int fd, void* buf, size_t len) {
- return read(fd, buf, len);
+static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
+ return read(fd.get(), buf, len);
}
-#undef read
-#define read ___xxx_read
+#undef read
+#define read ___xxx_read
-static __inline__ int adb_write(int fd, const void* buf, size_t len)
-{
- return TEMP_FAILURE_RETRY( write( fd, buf, len ) );
+static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
+ return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
}
#undef write
#define write ___xxx_write
-static __inline__ int64_t adb_lseek(int fd, int64_t pos, int where) {
+static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
#if defined(__APPLE__)
- return lseek(fd, pos, where);
+ return lseek(fd.get(), pos, where);
#else
- return lseek64(fd, pos, where);
+ return lseek64(fd.get(), pos, where);
#endif
}
#undef lseek
#define lseek ___xxx_lseek
-static __inline__ int adb_unlink(const char* path)
-{
- return unlink(path);
+static __inline__ int adb_unlink(const char* path) {
+ return unlink(path);
}
-#undef unlink
-#define unlink ___xxx_unlink
+#undef unlink
+#define unlink ___xxx_unlink
-static __inline__ int adb_creat(const char* path, int mode)
-{
- int fd = TEMP_FAILURE_RETRY( creat( path, mode ) );
+static __inline__ int adb_creat(const char* path, int mode) {
+ int fd = TEMP_FAILURE_RETRY(creat(path, mode));
- if ( fd < 0 )
- return -1;
+ if (fd < 0) return -1;
close_on_exec(fd);
return fd;
}
-#undef creat
-#define creat ___xxx_creat
+#undef creat
+#define creat ___xxx_creat
-static __inline__ int unix_isatty(int fd) {
- return isatty(fd);
+static __inline__ int unix_isatty(borrowed_fd fd) {
+ return isatty(fd.get());
}
-#define isatty ___xxx_isatty
+#define isatty ___xxx_isatty
// Helper for network_* functions.
inline int _fd_set_error_str(int fd, std::string* error) {
- if (fd == -1) {
- *error = strerror(errno);
- }
- return fd;
+ if (fd == -1) {
+ *error = strerror(errno);
+ }
+ return fd;
}
inline int network_inaddr_any_server(int port, int type, std::string* error) {
- return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
+ return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}
inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
@@ -498,22 +485,21 @@
int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
-static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
-{
+static __inline__ int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
+ socklen_t* addrlen) {
int fd;
- fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) );
- if (fd >= 0)
- close_on_exec(fd);
+ fd = TEMP_FAILURE_RETRY(accept(serverfd.get(), addr, addrlen));
+ if (fd >= 0) close_on_exec(fd);
return fd;
}
-#undef accept
-#define accept ___xxx_accept
+#undef accept
+#define accept ___xxx_accept
-inline int adb_socket_get_local_port(int fd) {
- return socket_get_local_port(fd);
+inline int adb_socket_get_local_port(borrowed_fd fd) {
+ return socket_get_local_port(fd.get());
}
// Operate on a file descriptor returned from unix_open() or a well-known file
@@ -524,10 +510,10 @@
// Windows implementations (in the ifdef above and in sysdeps_win32.cpp) call
// into the C Runtime and its configurable CR/LF translation (which is settable
// via _setmode()).
-#define unix_read adb_read
-#define unix_write adb_write
+#define unix_read adb_read
+#define unix_write adb_write
#define unix_lseek adb_lseek
-#define unix_close adb_close
+#define unix_close adb_close
static __inline__ int adb_thread_setname(const std::string& name) {
#ifdef __APPLE__
@@ -542,34 +528,31 @@
#endif
}
-static __inline__ int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
-{
- return setsockopt( fd, level, optname, optval, optlen );
+static __inline__ int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+ socklen_t optlen) {
+ return setsockopt(fd.get(), level, optname, optval, optlen);
}
-#undef setsockopt
-#define setsockopt ___xxx_setsockopt
+#undef setsockopt
+#define setsockopt ___xxx_setsockopt
-static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
-{
- return socketpair( d, type, protocol, sv );
+static __inline__ int unix_socketpair(int d, int type, int protocol, int sv[2]) {
+ return socketpair(d, type, protocol, sv);
}
-static __inline__ int adb_socketpair( int sv[2] )
-{
- int rc;
+static __inline__ int adb_socketpair(int sv[2]) {
+ int rc;
- rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
- if (rc < 0)
- return -1;
+ rc = unix_socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (rc < 0) return -1;
- close_on_exec( sv[0] );
- close_on_exec( sv[1] );
+ close_on_exec(sv[0]);
+ close_on_exec(sv[1]);
return 0;
}
-#undef socketpair
-#define socketpair ___xxx_socketpair
+#undef socketpair
+#define socketpair ___xxx_socketpair
typedef struct pollfd adb_pollfd;
static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
@@ -578,13 +561,12 @@
#define poll ___xxx_poll
-static __inline__ int adb_mkdir(const std::string& path, int mode)
-{
+static __inline__ int adb_mkdir(const std::string& path, int mode) {
return mkdir(path.c_str(), mode);
}
-#undef mkdir
-#define mkdir ___xxx_mkdir
+#undef mkdir
+#define mkdir ___xxx_mkdir
static __inline__ int adb_is_absolute_host_path(const char* path) {
return path[0] == '/';
@@ -592,15 +574,15 @@
#endif /* !_WIN32 */
-static inline void disable_tcp_nagle(int fd) {
+static inline void disable_tcp_nagle(borrowed_fd fd) {
int off = 1;
- adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+ adb_setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
}
// Sets TCP socket |fd| to send a keepalive TCP message every |interval_sec| seconds. Set
// |interval_sec| to 0 to disable keepalives. If keepalives are enabled, the connection will be
// configured to drop after 10 missed keepalives. Returns true on success.
-bool set_tcp_keepalive(int fd, int interval_sec);
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec);
#if defined(_WIN32)
// Win32 defines ERROR, which we don't need, but which conflicts with google3 logging.
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index 4de240e..c5c2275 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -104,13 +104,13 @@
socklen_t addrlen = sizeof(addr_storage);
sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
- if (bind(s, addr, addrlen) != 0) {
+ if (bind(s.get(), addr, addrlen) != 0) {
set_error(error);
return -1;
}
if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
- if (listen(s, SOMAXCONN) != 0) {
+ if (listen(s.get(), SOMAXCONN) != 0) {
set_error(error);
return -1;
}
diff --git a/adb/sysdeps/uio.h b/adb/sysdeps/uio.h
index d06ef89..ced884b 100644
--- a/adb/sysdeps/uio.h
+++ b/adb/sysdeps/uio.h
@@ -18,6 +18,8 @@
#include <sys/types.h>
+#include "adb_unique_fd.h"
+
#if defined(_WIN32)
// Layout of this struct must match struct WSABUF (verified via static assert in sysdeps_win32.cpp)
@@ -26,13 +28,15 @@
void* iov_base;
};
-ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt);
+ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt);
#else
#include <sys/uio.h>
using adb_iovec = struct iovec;
-#define adb_writev writev
+inline ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt) {
+ return writev(fd.get(), iov, iovcnt);
+}
#endif
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
index 4445a44..3fdc917 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -16,7 +16,7 @@
#include "sysdeps.h"
-bool set_tcp_keepalive(int fd, int interval_sec) {
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
int enable = (interval_sec > 0);
if (adb_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable))) {
return false;
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 4c5d8cb..f86cd03 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -145,16 +145,14 @@
static FHRec _win32_fhs[ WIN32_MAX_FHS ];
static int _win32_fh_next; // where to start search for free FHRec
-static FH
-_fh_from_int( int fd, const char* func )
-{
- FH f;
+static FH _fh_from_int(borrowed_fd bfd, const char* func) {
+ FH f;
+ int fd = bfd.get();
fd -= WIN32_FH_BASE;
if (fd < 0 || fd >= WIN32_MAX_FHS) {
- D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
- func );
+ D("_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE, func);
errno = EBADF;
return nullptr;
}
@@ -162,8 +160,7 @@
f = &_win32_fhs[fd];
if (f->used == 0) {
- D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
- func );
+ D("_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE, func);
errno = EBADF;
return nullptr;
}
@@ -171,20 +168,15 @@
return f;
}
-
-static int
-_fh_to_int( FH f )
-{
+static int _fh_to_int(FH f) {
if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
return (int)(f - _win32_fhs) + WIN32_FH_BASE;
return -1;
}
-static FH
-_fh_alloc( FHClass clazz )
-{
- FH f = nullptr;
+static FH _fh_alloc(FHClass clazz) {
+ FH f = nullptr;
std::lock_guard<std::mutex> lock(_win32_lock);
@@ -206,10 +198,7 @@
return nullptr;
}
-
-static int
-_fh_close( FH f )
-{
+static int _fh_close(FH f) {
// Use lock so that closing only happens once and so that _fh_alloc can't
// allocate a FH that we're in the middle of closing.
std::lock_guard<std::mutex> lock(_win32_lock);
@@ -456,7 +445,7 @@
return _fh_to_int(f);
}
-int adb_read(int fd, void* buf, int len) {
+int adb_read(borrowed_fd fd, void* buf, int len) {
FH f = _fh_from_int(fd, __func__);
if (f == nullptr) {
@@ -467,7 +456,7 @@
return f->clazz->_fh_read(f, buf, len);
}
-int adb_write(int fd, const void* buf, int len) {
+int adb_write(borrowed_fd fd, const void* buf, int len) {
FH f = _fh_from_int(fd, __func__);
if (f == nullptr) {
@@ -478,7 +467,7 @@
return f->clazz->_fh_write(f, buf, len);
}
-ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt) {
+ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt) {
FH f = _fh_from_int(fd, __func__);
if (f == nullptr) {
@@ -489,7 +478,7 @@
return f->clazz->_fh_writev(f, iov, iovcnt);
}
-int64_t adb_lseek(int fd, int64_t pos, int where) {
+int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
FH f = _fh_from_int(fd, __func__);
if (!f) {
errno = EBADF;
@@ -973,11 +962,11 @@
}
#undef accept
-int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t* addrlen) {
+int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
FH serverfh = _fh_from_int(serverfd, __func__);
if (!serverfh || serverfh->clazz != &_fh_socket_class) {
- D("adb_socket_accept: invalid fd %d", serverfd);
+ D("adb_socket_accept: invalid fd %d", serverfd.get());
errno = EBADF;
return -1;
}
@@ -992,7 +981,7 @@
fh->fh_socket = accept(serverfh->fh_socket, addr, addrlen);
if (fh->fh_socket == INVALID_SOCKET) {
const DWORD err = WSAGetLastError();
- LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd
+ LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd.get()
<< " failed: " + android::base::SystemErrorCodeToString(err);
_socket_set_errno(err);
return -1;
@@ -1000,16 +989,16 @@
const int fd = _fh_to_int(fh.get());
snprintf(fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name);
- D("adb_socket_accept on fd %d returns fd %d", serverfd, fd);
+ D("adb_socket_accept on fd %d returns fd %d", serverfd.get(), fd);
fh.release();
return fd;
}
-int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen) {
+int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval, socklen_t optlen) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || fh->clazz != &_fh_socket_class) {
- D("adb_setsockopt: invalid fd %d", fd);
+ D("adb_setsockopt: invalid fd %d", fd.get());
errno = EBADF;
return -1;
}
@@ -1022,7 +1011,7 @@
setsockopt(fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n", fd, level,
+ D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n", fd.get(), level,
optname, android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
result = -1;
@@ -1030,11 +1019,11 @@
return result;
}
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+static int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* optlen) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || fh->clazz != &_fh_socket_class) {
- D("adb_getsockname: invalid fd %d", fd);
+ D("adb_getsockname: invalid fd %d", fd.get());
errno = EBADF;
return -1;
}
@@ -1042,7 +1031,7 @@
int result = getsockname(fh->fh_socket, sockaddr, optlen);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
+ D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd.get(),
android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
result = -1;
@@ -1050,7 +1039,7 @@
return result;
}
-int adb_socket_get_local_port(int fd) {
+int adb_socket_get_local_port(borrowed_fd fd) {
sockaddr_storage addr_storage;
socklen_t addr_len = sizeof(addr_storage);
@@ -1068,11 +1057,11 @@
return ntohs(reinterpret_cast<sockaddr_in*>(&addr_storage)->sin_port);
}
-int adb_shutdown(int fd, int direction) {
+int adb_shutdown(borrowed_fd fd, int direction) {
FH f = _fh_from_int(fd, __func__);
if (!f || f->clazz != &_fh_socket_class) {
- D("adb_shutdown: invalid fd %d", fd);
+ D("adb_shutdown: invalid fd %d", fd.get());
errno = EBADF;
return -1;
}
@@ -1080,7 +1069,7 @@
D("adb_shutdown: %s", f->name);
if (shutdown(f->fh_socket, direction) == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("socket shutdown fd %d failed: %s", fd,
+ D("socket shutdown fd %d failed: %s", fd.get(),
android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
return -1;
@@ -1138,12 +1127,12 @@
return -1;
}
-bool set_file_block_mode(int fd, bool block) {
+bool set_file_block_mode(borrowed_fd fd, bool block) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || !fh->used) {
errno = EBADF;
- D("Setting nonblocking on bad file descriptor %d", fd);
+ D("Setting nonblocking on bad file descriptor %d", fd.get());
return false;
}
@@ -1152,22 +1141,22 @@
if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) {
int error = WSAGetLastError();
_socket_set_errno(error);
- D("Setting %d nonblocking failed (%d)", fd, error);
+ D("Setting %d nonblocking failed (%d)", fd.get(), error);
return false;
}
return true;
} else {
errno = ENOTSOCK;
- D("Setting nonblocking on non-socket %d", fd);
+ D("Setting nonblocking on non-socket %d", fd.get());
return false;
}
}
-bool set_tcp_keepalive(int fd, int interval_sec) {
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || fh->clazz != &_fh_socket_class) {
- D("set_tcp_keepalive(%d) failed: invalid fd", fd);
+ D("set_tcp_keepalive(%d) failed: invalid fd", fd.get());
errno = EBADF;
return false;
}
@@ -1181,7 +1170,7 @@
if (WSAIoctl(fh->fh_socket, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), nullptr, 0,
&bytes_returned, nullptr, nullptr) != 0) {
const DWORD err = WSAGetLastError();
- D("set_tcp_keepalive(%d) failed: %s", fd,
+ D("set_tcp_keepalive(%d) failed: %s", fd.get(),
android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
return false;
@@ -1228,12 +1217,12 @@
// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
-static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+static HANDLE _get_console_handle(borrowed_fd fd, DWORD* mode = nullptr) {
// First check isatty(); this is very fast and eliminates most non-console
// FDs, but returns 1 for both consoles and character devices like NUL.
#pragma push_macro("isatty")
#undef isatty
- if (!isatty(fd)) {
+ if (!isatty(fd.get())) {
return nullptr;
}
#pragma pop_macro("isatty")
@@ -1241,7 +1230,7 @@
// To differentiate between character devices and consoles we need to get
// the underlying HANDLE and use GetConsoleMode(), which is what requires
// GENERIC_READ permissions.
- const intptr_t intptr_handle = _get_osfhandle(fd);
+ const intptr_t intptr_handle = _get_osfhandle(fd.get());
if (intptr_handle == -1) {
return nullptr;
}
@@ -1265,7 +1254,7 @@
return _get_console_handle(fd);
}
-int unix_isatty(int fd) {
+int unix_isatty(borrowed_fd fd) {
return _get_console_handle(fd) ? 1 : 0;
}
@@ -1645,7 +1634,7 @@
// Prefix the len bytes in buf with the escape character, and then return the
// new buffer length.
-size_t _escape_prefix(char* const buf, const size_t len) {
+static size_t _escape_prefix(char* const buf, const size_t len) {
// If nothing to prefix, don't do anything. We might be called with
// len == 0, if alt was held down with a dead key which produced nothing.
if (len == 0) {
@@ -2073,7 +2062,7 @@
}
// Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
-int unix_read_interruptible(int fd, void* buf, size_t len) {
+int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
if ((fd == STDIN_FILENO) && (_console_handle != nullptr)) {
// If it is a request to read from stdin, and stdin_raw_init() has been
// called, and it successfully configured the console, then read from
@@ -2093,7 +2082,7 @@
// plain read() in favor of unix_read() or adb_read().
#pragma push_macro("read")
#undef read
- return read(fd, buf, len);
+ return read(fd.get(), buf, len);
#pragma pop_macro("read")
}
}
@@ -2617,7 +2606,9 @@
extern "C" int wmain(int argc, wchar_t **argv) {
// Convert args from UTF-16 to UTF-8 and pass that to main().
NarrowArgs narrow_args(argc, argv);
- return main(argc, narrow_args.data());
+
+ // Avoid destructing NarrowArgs: argv might have been mutated to point to string literals.
+ _exit(main(argc, narrow_args.data()));
}
// Shadow UTF-8 environment variable name/value pairs that are created from
diff --git a/base/Android.bp b/base/Android.bp
index 38f301a..58b6fb5 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -29,6 +29,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -45,6 +46,7 @@
name: "libbase_defaults",
defaults: ["libbase_cflags_defaults"],
srcs: [
+ "abi_compatibility.cpp",
"chrono_utils.cpp",
"cmsg.cpp",
"file.cpp",
@@ -100,6 +102,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -108,6 +111,9 @@
"libbase_headers",
],
export_header_lib_headers: ["libbase_headers"],
+ static_libs: ["fmtlib"],
+ whole_static_libs: ["fmtlib"],
+ export_static_lib_headers: ["fmtlib"],
}
cc_library_static {
@@ -116,6 +122,9 @@
sdk_version: "current",
stl: "c++_static",
export_include_dirs: ["include"],
+ static_libs: ["fmtlib_ndk"],
+ whole_static_libs: ["fmtlib_ndk"],
+ export_static_lib_headers: ["fmtlib_ndk"],
}
// Tests
@@ -128,6 +137,7 @@
"cmsg_test.cpp",
"endian_test.cpp",
"errors_test.cpp",
+ "expected_test.cpp",
"file_test.cpp",
"logging_test.cpp",
"macros_test.cpp",
@@ -137,6 +147,7 @@
"parsenetaddress_test.cpp",
"properties_test.cpp",
"quick_exit_test.cpp",
+ "result_test.cpp",
"scopeguard_test.cpp",
"stringprintf_test.cpp",
"strings_test.cpp",
@@ -171,3 +182,21 @@
},
test_suites: ["device-tests"],
}
+
+cc_benchmark {
+ name: "libbase_benchmark",
+ defaults: ["libbase_cflags_defaults"],
+
+ srcs: ["format_benchmark.cpp"],
+ shared_libs: ["libbase"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
new file mode 100644
index 0000000..06a7801
--- /dev/null
+++ b/base/abi_compatibility.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <memory>
+
+#include "android-base/cmsg.h"
+#include "android-base/file.h"
+#include "android-base/mapped_file.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace base {
+
+// These ABI-compatibility shims are in a separate file for two reasons:
+// 1. If they were in the file with the actual functions, it prevents calls to
+// those functions by other functions in the file, due to ambiguity.
+// 2. We will hopefully be able to delete these quickly.
+
+#if !defined(_WIN32)
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+ const std::vector<int>& fds) {
+ return SendFileDescriptorVector(borrowed_fd(sockfd), data, len, fds);
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+ std::vector<unique_fd>* fds) {
+ return ReceiveFileDescriptorVector(borrowed_fd(sockfd), data, len, max_fds, fds);
+}
+#endif
+
+bool ReadFdToString(int fd, std::string* content) {
+ return ReadFdToString(borrowed_fd(fd), content);
+}
+
+bool WriteStringToFd(const std::string& content, int fd) {
+ return WriteStringToFd(content, borrowed_fd(fd));
+}
+
+bool ReadFully(int fd, void* data, size_t byte_count) {
+ return ReadFully(borrowed_fd(fd), data, byte_count);
+}
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+ return ReadFullyAtOffset(borrowed_fd(fd), data, byte_count, offset);
+}
+
+bool WriteFully(int fd, const void* data, size_t byte_count) {
+ return WriteFully(borrowed_fd(fd), data, byte_count);
+}
+
+#if defined(__LP64__)
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
+#else
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEixmi
+#endif
+
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+extern "C" std::unique_ptr<MappedFile> MAPPEDFILE_FROMFD(int fd, off64_t offset, size_t length,
+ int prot) {
+ return MappedFile::FromFd(fd, offset, length, prot);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
index 42866f8..1fa873c 100644
--- a/base/cmsg.cpp
+++ b/base/cmsg.cpp
@@ -29,7 +29,7 @@
namespace android {
namespace base {
-ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+ssize_t SendFileDescriptorVector(borrowed_fd 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());
@@ -67,10 +67,10 @@
int flags = 0;
#endif
- return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, flags));
+ return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
}
-ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
std::vector<unique_fd>* fds) {
fds->clear();
@@ -98,7 +98,7 @@
flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
#endif
- ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, flags));
+ ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
if (rc == -1) {
return -1;
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
new file mode 100644
index 0000000..a74bc1d
--- /dev/null
+++ b/base/expected_test.cpp
@@ -0,0 +1,894 @@
+/*
+ * 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/expected.h"
+
+#include <cstdio>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using android::base::expected;
+using android::base::unexpected;
+
+typedef expected<int, int> exp_int;
+typedef expected<double, double> exp_double;
+typedef expected<std::string, std::string> exp_string;
+typedef expected<std::pair<std::string, int>, int> exp_pair;
+typedef expected<void, int> exp_void;
+
+struct T {
+ int a;
+ int b;
+ T() = default;
+ T(int a, int b) noexcept : a(a), b(b) {}
+};
+bool operator==(const T& x, const T& y) {
+ return x.a == y.a && x.b == y.b;
+}
+bool operator!=(const T& x, const T& y) {
+ return x.a != y.a || x.b != y.b;
+}
+
+struct E {
+ std::string message;
+ int cause;
+ E(const std::string& message, int cause) : message(message), cause(cause) {}
+};
+
+typedef expected<T,E> exp_complex;
+
+TEST(Expected, testDefaultConstructible) {
+ exp_int e;
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ(0, e.value());
+
+ exp_complex e2;
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(T(0,0), e2.value());
+
+ exp_void e3;
+ EXPECT_TRUE(e3.has_value());
+}
+
+TEST(Expected, testCopyConstructible) {
+ exp_int e;
+ exp_int e2 = e;
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(0, e.value());
+ EXPECT_EQ(0, e2.value());
+
+ exp_void e3;
+ exp_void e4 = e3;
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testMoveConstructible) {
+ exp_int e;
+ exp_int e2 = std::move(e);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(0, e.value());
+ EXPECT_EQ(0, e2.value());
+
+ exp_string e3(std::string("hello"));
+ exp_string e4 = std::move(e3);
+
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_EQ("", e3.value()); // e3 is moved
+ EXPECT_EQ("hello", e4.value());
+
+ exp_void e5;
+ exp_void e6 = std::move(e5);
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testCopyConstructibleFromConvertibleType) {
+ exp_double e = 3.3f;
+ exp_int e2 = e;
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3.3f, e.value());
+ EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testMoveConstructibleFromConvertibleType) {
+ exp_double e = 3.3f;
+ exp_int e2 = std::move(e);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3.3f, e.value());
+ EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testConstructibleFromValue) {
+ exp_int e = 3;
+ exp_double e2 = 5.5f;
+ exp_string e3 = std::string("hello");
+ exp_complex e4 = T(10, 20);
+ exp_void e5 = {};
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_EQ(3, e.value());
+ EXPECT_EQ(5.5f, e2.value());
+ EXPECT_EQ("hello", e3.value());
+ EXPECT_EQ(T(10,20), e4.value());
+}
+
+TEST(Expected, testConstructibleFromMovedValue) {
+ std::string hello = "hello";
+ exp_string e = std::move(hello);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("hello", e.value());
+ EXPECT_EQ("", hello);
+}
+
+TEST(Expected, testConstructibleFromConvertibleValue) {
+ exp_int e = 3.3f; // double to int
+ exp_string e2 = "hello"; // char* to std::string
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ(3, e.value());
+
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ("hello", e2.value());
+}
+
+TEST(Expected, testConstructibleFromUnexpected) {
+ exp_int::unexpected_type unexp = unexpected(10);
+ exp_int e = unexp;
+
+ exp_double::unexpected_type unexp2 = unexpected(10.5f);
+ exp_double e2 = unexp2;
+
+ exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
+ exp_string e3 = unexp3;
+
+ exp_void::unexpected_type unexp4 = unexpected(10);
+ exp_void e4 = unexp4;
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e.error());
+ EXPECT_EQ(10.5f, e2.error());
+ EXPECT_EQ("error", e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testMoveConstructibleFromUnexpected) {
+ exp_int e = unexpected(10);
+ exp_double e2 = unexpected(10.5f);
+ exp_string e3 = unexpected(std::string("error"));
+ exp_void e4 = unexpected(10);
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e.error());
+ EXPECT_EQ(10.5f, e2.error());
+ EXPECT_EQ("error", e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testConstructibleByForwarding) {
+ exp_string e(std::in_place, 5, 'a');
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("aaaaa", e.value());
+
+ exp_string e2({'a', 'b', 'c'});
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ("abc", e2.value());
+
+ exp_pair e3({"hello", 30});
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_EQ("hello",e3->first);
+ EXPECT_EQ(30,e3->second);
+
+ exp_void e4({});
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testDestructible) {
+ bool destroyed = false;
+ struct T {
+ bool* flag_;
+ T(bool* flag) : flag_(flag) {}
+ ~T() { *flag_ = true; }
+ };
+ {
+ expected<T, int> exp = T(&destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(Expected, testAssignable) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ e = e2;
+
+ EXPECT_EQ(20, e.value());
+ EXPECT_EQ(20, e2.value());
+
+ exp_int e3 = 10;
+ exp_int e4 = 20;
+ e3 = std::move(e4);
+
+ EXPECT_EQ(20, e3.value());
+ EXPECT_EQ(20, e4.value());
+
+ exp_void e5 = unexpected(10);
+ ASSERT_FALSE(e5.has_value());
+ exp_void e6;
+ e5 = e6;
+
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testAssignableFromValue) {
+ exp_int e = 10;
+ e = 20;
+ EXPECT_EQ(20, e.value());
+
+ exp_double e2 = 3.5f;
+ e2 = 10.5f;
+ EXPECT_EQ(10.5f, e2.value());
+
+ exp_string e3 = "hello";
+ e3 = "world";
+ EXPECT_EQ("world", e3.value());
+
+ exp_void e4 = unexpected(10);
+ ASSERT_FALSE(e4.has_value());
+ e4 = {};
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testAssignableFromUnexpected) {
+ exp_int e = 10;
+ e = unexpected(30);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ(30, e.error());
+
+ exp_double e2 = 3.5f;
+ e2 = unexpected(10.5f);
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(10.5f, e2.error());
+
+ exp_string e3 = "hello";
+ e3 = unexpected("world");
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_EQ("world", e3.error());
+
+ exp_void e4 = {};
+ e4 = unexpected(10);
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testAssignableFromMovedValue) {
+ std::string world = "world";
+ exp_string e = "hello";
+ e = std::move(world);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("world", e.value());
+ EXPECT_EQ("", world);
+}
+
+TEST(Expected, testAssignableFromMovedUnexpected) {
+ std::string world = "world";
+ exp_string e = "hello";
+ e = unexpected(std::move(world));
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ("world", e.error());
+ EXPECT_EQ("", world);
+}
+
+TEST(Expected, testEmplace) {
+ struct T {
+ int a;
+ double b;
+ T() {}
+ T(int a, double b) noexcept : a(a), b(b) {}
+ };
+ expected<T, int> exp;
+ T& t = exp.emplace(3, 10.5f);
+
+ EXPECT_TRUE(exp.has_value());
+ EXPECT_EQ(3, t.a);
+ EXPECT_EQ(10.5f, t.b);
+ EXPECT_EQ(3, exp.value().a);
+ EXPECT_EQ(10.5, exp.value().b);
+
+ exp_void e = unexpected(10);
+ ASSERT_FALSE(e.has_value());
+ e.emplace();
+ EXPECT_TRUE(e.has_value());
+}
+
+TEST(Expected, testSwapExpectedExpected) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ e.swap(e2);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(20, e.value());
+ EXPECT_EQ(10, e2.value());
+
+ exp_void e3;
+ exp_void e4;
+ e3.swap(e4);
+
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testSwapUnexpectedUnexpected) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(20);
+ e.swap(e2);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(20, e.error());
+ EXPECT_EQ(10, e2.error());
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(20);
+ e3.swap(e4);
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(20, e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testSwapExpectedUnepected) {
+ exp_int e = 10;
+ exp_int e2 = unexpected(30);
+ e.swap(e2);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(30, e.error());
+ EXPECT_EQ(10, e2.value());
+
+ exp_void e3;
+ exp_void e4 = unexpected(10);
+ e3.swap(e4);
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_EQ(10, e3.error());
+}
+
+TEST(Expected, testDereference) {
+ struct T {
+ int a;
+ double b;
+ T() {}
+ T(int a, double b) : a(a), b(b) {}
+ };
+ expected<T, int> exp = T(3, 10.5f);
+
+ EXPECT_EQ(3, exp->a);
+ EXPECT_EQ(10.5f, exp->b);
+
+ EXPECT_EQ(3, (*exp).a);
+ EXPECT_EQ(10.5f, (*exp).b);
+}
+
+TEST(Expected, testTest) {
+ exp_int e = 10;
+ EXPECT_TRUE(e);
+ EXPECT_TRUE(e.has_value());
+
+ exp_int e2 = unexpected(10);
+ EXPECT_FALSE(e2);
+ EXPECT_FALSE(e2.has_value());
+}
+
+TEST(Expected, testGetValue) {
+ exp_int e = 10;
+ EXPECT_EQ(10, e.value());
+ EXPECT_EQ(10, e.value_or(20));
+
+ exp_int e2 = unexpected(10);
+ EXPECT_EQ(10, e2.error());
+ EXPECT_EQ(20, e2.value_or(20));
+}
+
+TEST(Expected, testSameValues) {
+ exp_int e = 10;
+ exp_int e2 = 10;
+ EXPECT_TRUE(e == e2);
+ EXPECT_TRUE(e2 == e);
+ EXPECT_FALSE(e != e2);
+ EXPECT_FALSE(e2 != e);
+
+ exp_void e3;
+ exp_void e4;
+ EXPECT_TRUE(e3 == e4);
+ EXPECT_TRUE(e4 == e3);
+ EXPECT_FALSE(e3 != e4);
+ EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentValues) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+}
+
+TEST(Expected, testValueWithError) {
+ exp_int e = 10;
+ exp_int e2 = unexpected(10);
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+
+ exp_void e3;
+ exp_void e4 = unexpected(10);
+ EXPECT_FALSE(e3 == e4);
+ EXPECT_FALSE(e4 == e3);
+ EXPECT_TRUE(e3 != e4);
+ EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testSameErrors) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(10);
+ EXPECT_TRUE(e == e2);
+ EXPECT_TRUE(e2 == e);
+ EXPECT_FALSE(e != e2);
+ EXPECT_FALSE(e2 != e);
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(10);
+ EXPECT_TRUE(e3 == e4);
+ EXPECT_TRUE(e4 == e3);
+ EXPECT_FALSE(e3 != e4);
+ EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentErrors) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(20);
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(20);
+ EXPECT_FALSE(e3 == e4);
+ EXPECT_FALSE(e4 == e3);
+ EXPECT_TRUE(e3 != e4);
+ EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testCompareWithSameValue) {
+ exp_int e = 10;
+ int value = 10;
+ EXPECT_TRUE(e == value);
+ EXPECT_TRUE(value == e);
+ EXPECT_FALSE(e != value);
+ EXPECT_FALSE(value != e);
+}
+
+TEST(Expected, testCompareWithDifferentValue) {
+ exp_int e = 10;
+ int value = 20;
+ EXPECT_FALSE(e == value);
+ EXPECT_FALSE(value == e);
+ EXPECT_TRUE(e != value);
+ EXPECT_TRUE(value != e);
+}
+
+TEST(Expected, testCompareWithSameError) {
+ exp_int e = unexpected(10);
+ exp_int::unexpected_type error = 10;
+ EXPECT_TRUE(e == error);
+ EXPECT_TRUE(error == e);
+ EXPECT_FALSE(e != error);
+ EXPECT_FALSE(error != e);
+
+ exp_void e2 = unexpected(10);
+ exp_void::unexpected_type error2 = 10;
+ EXPECT_TRUE(e2 == error2);
+ EXPECT_TRUE(error2 == e2);
+ EXPECT_FALSE(e2 != error2);
+ EXPECT_FALSE(error2 != e2);
+}
+
+TEST(Expected, testCompareWithDifferentError) {
+ exp_int e = unexpected(10);
+ exp_int::unexpected_type error = 20;
+ EXPECT_FALSE(e == error);
+ EXPECT_FALSE(error == e);
+ EXPECT_TRUE(e != error);
+ EXPECT_TRUE(error != e);
+
+ exp_void e2 = unexpected(10);
+ exp_void::unexpected_type error2 = 20;
+ EXPECT_FALSE(e2 == error2);
+ EXPECT_FALSE(error2 == e2);
+ EXPECT_TRUE(e2 != error2);
+ EXPECT_TRUE(error2 != e2);
+}
+
+TEST(Expected, testCompareDifferentType) {
+ expected<int,int> e = 10;
+ expected<int32_t, int> e2 = 10;
+ EXPECT_TRUE(e == e2);
+ e2 = 20;
+ EXPECT_FALSE(e == e2);
+
+ expected<std::string_view,int> e3 = "hello";
+ expected<std::string,int> e4 = "hello";
+ EXPECT_TRUE(e3 == e4);
+ e4 = "world";
+ EXPECT_FALSE(e3 == e4);
+
+ expected<void,int> e5;
+ expected<int,int> e6 = 10;
+ EXPECT_FALSE(e5 == e6);
+ EXPECT_FALSE(e6 == e5);
+}
+
+TEST(Expected, testDivideExample) {
+ struct QR {
+ int quotient;
+ int remainder;
+ QR(int q, int r) noexcept : quotient(q), remainder(r) {}
+ bool operator==(const QR& rhs) const {
+ return quotient == rhs.quotient && remainder == rhs.remainder;
+ }
+ bool operator!=(const QR& rhs) const {
+ return quotient != rhs.quotient || remainder == rhs.remainder;
+ }
+ };
+
+ auto divide = [](int x, int y) -> expected<QR,E> {
+ if (y == 0) {
+ return unexpected(E("divide by zero", -1));
+ } else {
+ return QR(x / y, x % y);
+ }
+ };
+
+ EXPECT_FALSE(divide(10, 0));
+ EXPECT_EQ("divide by zero", divide(10, 0).error().message);
+ EXPECT_EQ(-1, divide(10, 0).error().cause);
+
+ EXPECT_TRUE(divide(10, 3));
+ EXPECT_EQ(QR(3, 1), divide(10, 3));
+}
+
+TEST(Expected, testPair) {
+ auto test = [](bool yes) -> exp_pair {
+ if (yes) {
+ return exp_pair({"yes", 42});
+ } else {
+ return unexpected(42);
+ }
+ };
+
+ auto r = test(true);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("yes", r->first);
+}
+
+TEST(Expected, testVoid) {
+ auto test = [](bool ok) -> exp_void {
+ if (ok) {
+ return {};
+ } else {
+ return unexpected(10);
+ }
+ };
+
+ auto r = test(true);
+ EXPECT_TRUE(r);
+ r = test(false);
+ EXPECT_FALSE(r);
+ EXPECT_EQ(10, r.error());
+}
+
+// copied from result_test.cpp
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T,
+ typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+ static void Reset() {
+ constructor_called = 0;
+ copy_constructor_called = 0;
+ move_constructor_called = 0;
+ copy_assignment_called = 0;
+ move_assignment_called = 0;
+ }
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+typedef expected<ConstructorTracker, int> exp_track;
+
+TEST(Expected, testNumberOfCopies) {
+ // default constructor
+ ConstructorTracker::Reset();
+ exp_track e("hello");
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // copy constructor
+ ConstructorTracker::Reset();
+ exp_track e2 = e;
+ EXPECT_EQ(0U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // move constructor
+ ConstructorTracker::Reset();
+ exp_track e3 = std::move(e);
+ EXPECT_EQ(0U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // construct from lvalue
+ ConstructorTracker::Reset();
+ ConstructorTracker ct = "hello";
+ exp_track e4(ct);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // construct from rvalue
+ ConstructorTracker::Reset();
+ ConstructorTracker ct2 = "hello";
+ exp_track e5(std::move(ct2));
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // copy assignment
+ ConstructorTracker::Reset();
+ exp_track e6 = "hello";
+ exp_track e7 = "world";
+ e7 = e6;
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // move assignment
+ ConstructorTracker::Reset();
+ exp_track e8 = "hello";
+ exp_track e9 = "world";
+ e9 = std::move(e8);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
+
+ // swap
+ ConstructorTracker::Reset();
+ exp_track e10 = "hello";
+ exp_track e11 = "world";
+ std::swap(e10, e11);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(Expected, testNoCopyOnReturn) {
+ auto test = [](const std::string& in) -> exp_track {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+ };
+
+ ConstructorTracker::Reset();
+ auto result1 = test("");
+ ASSERT_TRUE(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ ConstructorTracker::Reset();
+ auto result2 = test("test2");
+ ASSERT_TRUE(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ ConstructorTracker::Reset();
+ auto result3 = test("test3");
+ ASSERT_TRUE(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(Expected, testNested) {
+ expected<exp_string, std::string> e = "hello";
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e.value().has_value());
+ EXPECT_TRUE(e);
+ EXPECT_TRUE(*e);
+ EXPECT_EQ("hello", e.value().value());
+
+ expected<exp_string, std::string> e2 = unexpected("world");
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e2);
+ EXPECT_EQ("world", e2.error());
+
+ expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_FALSE(e3.value().has_value());
+ EXPECT_TRUE(e3);
+ EXPECT_FALSE(*e3);
+ EXPECT_EQ("world", e3.value().error());
+}
+
+constexpr bool equals(const char* a, const char* b) {
+ return (a == nullptr && b == nullptr) ||
+ (a != nullptr && b != nullptr && *a == *b &&
+ (*a == '\0' || equals(a + 1, b + 1)));
+}
+
+TEST(Expected, testConstexpr) {
+ // Compliation error will occur if these expressions can't be
+ // evaluated at compile time
+ constexpr exp_int e(3);
+ constexpr exp_int::unexpected_type err(3);
+ constexpr int i = 4;
+
+ // default constructor
+ static_assert(exp_int().value() == 0);
+ // copy constructor
+ static_assert(exp_int(e).value() == 3);
+ // move constructor
+ static_assert(exp_int(exp_int(4)).value() == 4);
+ // copy construct from value
+ static_assert(exp_int(i).value() == 4);
+ // copy construct from unexpected
+ static_assert(exp_int(err).error() == 3);
+ // move costruct from unexpected
+ static_assert(exp_int(unexpected(3)).error() == 3);
+ // observers
+ static_assert(*exp_int(3) == 3);
+ static_assert(exp_int(3).has_value() == true);
+ static_assert(exp_int(3).value_or(4) == 3);
+
+ typedef expected<const char*, int> exp_s;
+ constexpr exp_s s("hello");
+ constexpr const char* c = "hello";
+ static_assert(equals(exp_s().value(), nullptr));
+ static_assert(equals(exp_s(s).value(), "hello"));
+ static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
+ static_assert(equals(exp_s("hello").value(), "hello"));
+ static_assert(equals(exp_s(c).value(), "hello"));
+}
+
+TEST(Expected, testWithNonConstructible) {
+ struct AssertNotConstructed {
+ AssertNotConstructed() = delete;
+ };
+
+ expected<int, AssertNotConstructed> v(42);
+ EXPECT_TRUE(v.has_value());
+ EXPECT_EQ(42, v.value());
+
+ expected<AssertNotConstructed, int> e(unexpected(42));
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ(42, e.error());
+}
+
+TEST(Expected, testWithMoveOnlyType) {
+ typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
+ exp_ptr e(std::make_unique<int>(3));
+ exp_ptr e2(unexpected(std::make_unique<int>(4)));
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(3, *(e.value()));
+ EXPECT_EQ(4, *(e2.error()));
+
+ e2 = std::move(e);
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3, *(e2.value()));
+}
diff --git a/base/file.cpp b/base/file.cpp
index adc8984..3dfcfbb 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -176,20 +176,20 @@
// Versions of standard library APIs that support UTF-8 strings.
using namespace android::base::utf8;
-bool ReadFdToString(int fd, std::string* content) {
+bool ReadFdToString(borrowed_fd fd, std::string* content) {
content->clear();
// Although original we had small files in mind, this code gets used for
// very large files too, where the std::string growth heuristics might not
// be suitable. https://code.google.com/p/android/issues/detail?id=258500.
struct stat sb;
- if (fstat(fd, &sb) != -1 && sb.st_size > 0) {
+ if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
content->reserve(sb.st_size);
}
char buf[BUFSIZ];
ssize_t n;
- while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+ while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
content->append(buf, n);
}
return (n == 0) ? true : false;
@@ -206,11 +206,11 @@
return ReadFdToString(fd, content);
}
-bool WriteStringToFd(const std::string& content, int fd) {
+bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
const char* p = content.data();
size_t left = content.size();
while (left > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
if (n == -1) {
return false;
}
@@ -269,11 +269,11 @@
return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
}
-bool ReadFully(int fd, void* data, size_t byte_count) {
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
uint8_t* p = reinterpret_cast<uint8_t*>(data);
size_t remaining = byte_count;
while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
if (n <= 0) return false;
p += n;
remaining -= n;
@@ -284,14 +284,14 @@
#if defined(_WIN32)
// Windows implementation of pread. Note that this DOES move the file descriptors read position,
// but it does so atomically.
-static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
+static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
DWORD bytes_read;
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(OVERLAPPED));
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
- if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count),
- &bytes_read, &overlapped)) {
+ if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
+ static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
// In case someone tries to read errno (since this is masquerading as a POSIX call)
errno = EIO;
return -1;
@@ -300,10 +300,10 @@
}
#endif
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
uint8_t* p = reinterpret_cast<uint8_t*>(data);
while (byte_count > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset));
+ ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
if (n <= 0) return false;
p += n;
byte_count -= n;
@@ -312,11 +312,11 @@
return true;
}
-bool WriteFully(int fd, const void* data, size_t byte_count) {
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
size_t remaining = byte_count;
while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
if (n == -1) return false;
p += n;
remaining -= n;
diff --git a/base/format_benchmark.cpp b/base/format_benchmark.cpp
new file mode 100644
index 0000000..9590b23
--- /dev/null
+++ b/base/format_benchmark.cpp
@@ -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.
+ */
+
+#include "android-base/format.h"
+
+#include <limits>
+
+#include <benchmark/benchmark.h>
+
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
+
+static void BenchmarkFormatInt(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} {} {}", 42, std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkFormatInt);
+
+static void BenchmarkStringPrintfInt(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%d %d %d", 42, std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfInt);
+
+static void BenchmarkFormatFloat(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} {} {}", 42.42, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkFormatFloat);
+
+static void BenchmarkStringPrintfFloat(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%f %f %f", 42.42, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfFloat);
+
+static void BenchmarkFormatStrings(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} hello there {}", "hi,", "!!"));
+ }
+}
+
+BENCHMARK(BenchmarkFormatStrings);
+
+static void BenchmarkStringPrintfStrings(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%s hello there %s", "hi,", "!!"));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfStrings);
+
+// Run the benchmark
+BENCHMARK_MAIN();
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
index 7f93ddc..e4197b1 100644
--- a/base/include/android-base/cmsg.h
+++ b/base/include/android-base/cmsg.h
@@ -51,20 +51,20 @@
// 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,
+ssize_t SendFileDescriptorVector(borrowed_fd 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,
+ssize_t ReceiveFileDescriptorVector(borrowed_fd 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) {
+ssize_t SendFileDescriptors(borrowed_fd 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.
@@ -79,7 +79,7 @@
// 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) {
+ssize_t ReceiveFileDescriptors(borrowed_fd sock, void* data, size_t len, Args&&... received_fds) {
std::vector<unique_fd*> fds;
Append(fds, std::forward<Args>(received_fds)...);
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
new file mode 100644
index 0000000..957a8a0
--- /dev/null
+++ b/base/include/android-base/expected.h
@@ -0,0 +1,786 @@
+/*
+ * 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 <algorithm>
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+// android::base::expected is an Android implementation of the std::expected
+// proposal.
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
+//
+// Usage:
+// using android::base::expected;
+// using android::base::unexpected;
+//
+// expected<double,std::string> safe_divide(double i, double j) {
+// if (j == 0) return unexpected("divide by zero");
+// else return i / j;
+// }
+//
+// void test() {
+// auto q = safe_divide(10, 0);
+// if (q) { printf("%f\n", q.value()); }
+// else { printf("%s\n", q.error().c_str()); }
+// }
+//
+// When the proposal becomes part of the standard and is implemented by
+// libcxx, this will be removed and android::base::expected will be
+// type alias to std::expected.
+//
+
+namespace android {
+namespace base {
+
+// Synopsis
+template<class T, class E>
+ class expected;
+
+template<class E>
+ class unexpected;
+template<class E>
+ unexpected(E) -> unexpected<E>;
+
+template<class E>
+ class bad_expected_access;
+
+template<>
+ class bad_expected_access<void>;
+
+struct unexpect_t {
+ explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+// macros for SFINAE
+#define _ENABLE_IF(...) \
+ , std::enable_if_t<(__VA_ARGS__)>* = nullptr
+
+// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
+// ignored when used as a return value. This is off by default.
+#ifdef NODISCARD_EXPECTED
+#define _NODISCARD_ [[nodiscard]]
+#else
+#define _NODISCARD_
+#endif
+
+namespace {
+template< class T >
+struct remove_cvref {
+ typedef std::remove_cv_t<std::remove_reference_t<T>> type;
+};
+template< class T >
+using remove_cvref_t = typename remove_cvref<T>::type;
+} // namespace
+
+// Class expected
+template<class T, class E>
+class _NODISCARD_ expected {
+ public:
+ using value_type = T;
+ using error_type = E;
+ using unexpected_type = unexpected<E>;
+
+ template<class U>
+ using rebind = expected<U, error_type>;
+
+ // constructors
+ constexpr expected() = default;
+ constexpr expected(const expected& rhs) = default;
+ constexpr expected(expected&& rhs) noexcept = default;
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ !(!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* non-explicit */
+ )>
+ constexpr expected(const expected<U, G>& rhs) {
+ if (rhs.has_value()) var_ = rhs.value();
+ else var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ (!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* explicit */
+ )>
+ constexpr explicit expected(const expected<U, G>& rhs) {
+ if (rhs.has_value()) var_ = rhs.value();
+ else var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ !(!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* non-explicit */
+ )>
+ constexpr expected(expected<U, G>&& rhs) {
+ if (rhs.has_value()) var_ = std::move(rhs.value());
+ else var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ (!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* explicit */
+ )>
+ constexpr explicit expected(expected<U, G>&& rhs) {
+ if (rhs.has_value()) var_ = std::move(rhs.value());
+ else var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
+ !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
+ std::is_convertible_v<U&&,T> /* non-explicit */
+ )>
+ constexpr expected(U&& v)
+ : var_(std::in_place_index<0>, std::forward<U>(v)) {}
+
+ template<class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
+ !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
+ !std::is_convertible_v<U&&,T> /* explicit */
+ )>
+ constexpr explicit expected(U&& v)
+ : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ constexpr expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, e.value()) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, E(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ std::is_convertible_v<G&&, E> /* non-explicit */
+ )>
+ constexpr expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ !std::is_convertible_v<G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<T, Args&&...>
+ )>
+ constexpr explicit expected(std::in_place_t, Args&&... args)
+ : var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<T, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+ : var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<E, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, Args&&... args)
+ : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+ : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+ // destructor
+ ~expected() = default;
+
+ // assignment
+ // Note: SFNAIE doesn't work here because assignment operator should be
+ // non-template. We could workaround this by defining a templated parent class
+ // having the assignment operator. This incomplete implementation however
+ // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+ // assignable. The copy assignment will fail by the underlying std::variant
+ // anyway though the error message won't be clear.
+ expected& operator=(const expected& rhs) = default;
+
+ // Note for SFNAIE above applies to here as well
+ expected& operator=(expected&& rhs) = default;
+
+ template<class U = T _ENABLE_IF(
+ !std::is_void_v<T> &&
+ !std::is_same_v<expected<T,E>, remove_cvref_t<U>> &&
+ !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
+ std::is_constructible_v<T,U> &&
+ std::is_assignable_v<T&,U> &&
+ std::is_nothrow_move_constructible_v<E>
+ )>
+ expected& operator=(U&& rhs) {
+ var_ = T(std::forward<U>(rhs));
+ return *this;
+ }
+
+ template<class G = E>
+ expected& operator=(const unexpected<G>& rhs) {
+ var_ = rhs;
+ return *this;
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_nothrow_move_constructible_v<G> &&
+ std::is_move_assignable_v<G>
+ )>
+ expected& operator=(unexpected<G>&& rhs) {
+ var_ = std::move(rhs);
+ return *this;
+ }
+
+ // modifiers
+ template<class... Args _ENABLE_IF(
+ std::is_nothrow_constructible_v<T, Args...>
+ )>
+ T& emplace(Args&&... args) {
+ expected(std::in_place, std::forward<Args>(args)...).swap(*this);
+ return value();
+ }
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
+ )>
+ T& emplace(std::initializer_list<U> il, Args&&... args) {
+ expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
+ return value();
+ }
+
+ // swap
+ template<typename U = T, typename = std::enable_if_t<(
+ std::is_swappable_v<U> &&
+ std::is_swappable_v<E> &&
+ (std::is_move_constructible_v<U> ||
+ std::is_move_constructible_v<E>))>>
+ void swap(expected& rhs) noexcept(
+ std::is_nothrow_move_constructible_v<T> &&
+ std::is_nothrow_swappable_v<T> &&
+ std::is_nothrow_move_constructible_v<E> &&
+ std::is_nothrow_swappable_v<E>) {
+ var_.swap(rhs.var_);
+ }
+
+ // observers
+ constexpr const T* operator->() const { return std::addressof(value()); }
+ constexpr T* operator->() { return std::addressof(value()); }
+ constexpr const T& operator*() const& { return value(); }
+ constexpr T& operator*() & { return value(); }
+ constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
+ constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
+
+ constexpr explicit operator bool() const noexcept { return has_value(); }
+ constexpr bool has_value() const noexcept { return var_.index() == 0; }
+
+ constexpr const T& value() const& { return std::get<T>(var_); }
+ constexpr T& value() & { return std::get<T>(var_); }
+ constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
+ constexpr T&& value() && { return std::move(std::get<T>(var_)); }
+
+ constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+ constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+ constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+ constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+ template<class U _ENABLE_IF(
+ std::is_copy_constructible_v<T> &&
+ std::is_convertible_v<U, T>
+ )>
+ constexpr T value_or(U&& v) const& {
+ if (has_value()) return value();
+ else return static_cast<T>(std::forward<U>(v));
+ }
+
+ template<class U _ENABLE_IF(
+ std::is_move_constructible_v<T> &&
+ std::is_convertible_v<U, T>
+ )>
+ constexpr T value_or(U&& v) && {
+ if (has_value()) return std::move(value());
+ else return static_cast<T>(std::forward<U>(v));
+ }
+
+ // expected equality operators
+ template<class T1, class E1, class T2, class E2>
+ friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
+ template<class T1, class E1, class T2, class E2>
+ friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
+
+ // comparison with T
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator==(const expected<T1, E1>&, const T2&);
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator==(const T2&, const expected<T1, E1>&);
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator!=(const expected<T1, E1>&, const T2&);
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator!=(const T2&, const expected<T1, E1>&);
+
+ // Comparison with unexpected<E>
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
+
+ // Specialized algorithms
+ template<class T1, class E1>
+ friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+ std::variant<value_type, unexpected_type> var_;
+};
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return *x == *y;
+ }
+}
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+ return !(x == y);
+}
+
+// comparison with T
+template<class T1, class E1, class T2>
+constexpr bool operator==(const expected<T1, E1>& x, const T2& y) {
+ return x.has_value() && (*x == y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator==(const T2& x, const expected<T1, E1>& y) {
+ return y.has_value() && (x == *y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator!=(const expected<T1, E1>& x, const T2& y) {
+ return !x.has_value() || (*x != y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator!=(const T2& x, const expected<T1, E1>& y) {
+ return !y.has_value() || (x != *y);
+}
+
+// Comparison with unexpected<E>
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
+ return !x.has_value() && (x.error() == y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
+ return !y.has_value() && (x.value() == y.error());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
+ return x.has_value() || (x.error() != y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
+ return y.has_value() || (x.value() != y.error());
+}
+
+template<class E>
+class _NODISCARD_ expected<void, E> {
+ public:
+ using value_type = void;
+ using error_type = E;
+ using unexpected_type = unexpected<E>;
+
+ // constructors
+ constexpr expected() = default;
+ constexpr expected(const expected& rhs) = default;
+ constexpr expected(expected&& rhs) noexcept = default;
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ constexpr expected(const expected<U, G>& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const expected<U, G>& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ std::is_convertible_v<const G&&, E> /* non-explicit */
+ )>
+ constexpr expected(expected<U, G>&& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ !std::is_convertible_v<const G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(expected<U, G>&& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ constexpr expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, e.value()) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, E(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ std::is_convertible_v<G&&, E> /* non-explicit */
+ )>
+ constexpr expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ !std::is_convertible_v<G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+ template<class... Args _ENABLE_IF(
+ sizeof...(Args) == 0
+ )>
+ constexpr explicit expected(std::in_place_t, Args&&...) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<E, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, Args&&... args)
+ : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+ : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+ // destructor
+ ~expected() = default;
+
+ // assignment
+ // Note: SFNAIE doesn't work here because assignment operator should be
+ // non-template. We could workaround this by defining a templated parent class
+ // having the assignment operator. This incomplete implementation however
+ // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+ // assignable. The copy assignment will fail by the underlying std::variant
+ // anyway though the error message won't be clear.
+ expected& operator=(const expected& rhs) = default;
+
+ // Note for SFNAIE above applies to here as well
+ expected& operator=(expected&& rhs) = default;
+
+ template<class G = E>
+ expected& operator=(const unexpected<G>& rhs) {
+ var_ = rhs;
+ return *this;
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_nothrow_move_constructible_v<G> &&
+ std::is_move_assignable_v<G>
+ )>
+ expected& operator=(unexpected<G>&& rhs) {
+ var_ = std::move(rhs);
+ return *this;
+ }
+
+ // modifiers
+ void emplace() {
+ var_ = std::monostate();
+ }
+
+ // swap
+ template<typename = std::enable_if_t<
+ std::is_swappable_v<E>>
+ >
+ void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
+ var_.swap(rhs.var_);
+ }
+
+ // observers
+ constexpr explicit operator bool() const noexcept { return has_value(); }
+ constexpr bool has_value() const noexcept { return var_.index() == 0; }
+
+ constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
+
+ constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+ constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+ constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+ constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+ // expected equality operators
+ template<class E1, class E2>
+ friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
+
+ // Specialized algorithms
+ template<class T1, class E1>
+ friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+ std::variant<std::monostate, unexpected_type> var_;
+};
+
+template<class E1, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return true;
+ }
+}
+
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return false;
+ }
+}
+
+template<class E1, class T2, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return false;
+ }
+}
+
+template<class E>
+class unexpected {
+ public:
+ // constructors
+ constexpr unexpected(const unexpected&) = default;
+ constexpr unexpected(unexpected&&) = default;
+
+ template<class Err = E _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_same_v<remove_cvref_t<E>, std::in_place_t> &&
+ !std::is_same_v<remove_cvref_t<E>, unexpected>
+ )>
+ constexpr unexpected(Err&& e)
+ : val_(std::forward<Err>(e)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+ : val_(il, std::forward<Args>(args)...) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ std::is_convertible_v<Err, E> /* non-explicit */
+ )>
+ constexpr unexpected(const unexpected<Err>& rhs)
+ : val_(rhs.value()) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ !std::is_convertible_v<Err, E> /* explicit */
+ )>
+ constexpr explicit unexpected(const unexpected<Err>& rhs)
+ : val_(E(rhs.value())) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ std::is_convertible_v<Err, E> /* non-explicit */
+ )>
+ constexpr unexpected(unexpected<Err>&& rhs)
+ : val_(std::move(rhs.value())) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ !std::is_convertible_v<Err, E> /* explicit */
+ )>
+ constexpr explicit unexpected(unexpected<Err>&& rhs)
+ : val_(E(std::move(rhs.value()))) {}
+
+ // assignment
+ constexpr unexpected& operator=(const unexpected&) = default;
+ constexpr unexpected& operator=(unexpected&&) = default;
+ template<class Err = E>
+ constexpr unexpected& operator=(const unexpected<Err>& rhs) {
+ val_ = rhs.value();
+ return *this;
+ }
+ template<class Err = E>
+ constexpr unexpected& operator=(unexpected<Err>&& rhs) {
+ val_ = std::forward<E>(rhs.value());
+ return *this;
+ }
+
+ // observer
+ constexpr const E& value() const& noexcept { return val_; }
+ constexpr E& value() & noexcept { return val_; }
+ constexpr const E&& value() const&& noexcept { return std::move(val_); }
+ constexpr E&& value() && noexcept { return std::move(val_); }
+
+ void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+ std::swap(val_, other.val_);
+ }
+
+ template<class E1, class E2>
+ friend constexpr bool
+ operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
+ template<class E1, class E2>
+ friend constexpr bool
+ operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
+
+ template<class E1>
+ friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
+
+ private:
+ E val_;
+};
+
+template<class E1, class E2>
+constexpr bool
+operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+ return e1.value() == e2.value();
+}
+
+template<class E1, class E2>
+constexpr bool
+operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+ return e1.value() != e2.value();
+}
+
+template<class E1>
+void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
+ x.swap(y);
+}
+
+// TODO: bad_expected_access class
+
+#undef _ENABLE_IF
+#undef _NODISCARD_
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index f8748b5..c622562 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -21,8 +21,9 @@
#include <string>
-#include <android-base/macros.h>
+#include "android-base/macros.h"
#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
#if !defined(_WIN32) && !defined(O_BINARY)
/** Windows needs O_BINARY, but Unix never mangles line endings. */
@@ -77,13 +78,13 @@
namespace android {
namespace base {
-bool ReadFdToString(int fd, std::string* content);
+bool ReadFdToString(borrowed_fd fd, std::string* content);
bool ReadFileToString(const std::string& path, std::string* content,
bool follow_symlinks = false);
bool WriteStringToFile(const std::string& content, const std::string& path,
bool follow_symlinks = false);
-bool WriteStringToFd(const std::string& content, int fd);
+bool WriteStringToFd(const std::string& content, borrowed_fd fd);
#if !defined(_WIN32)
bool WriteStringToFile(const std::string& content, const std::string& path,
@@ -91,7 +92,7 @@
bool follow_symlinks = false);
#endif
-bool ReadFully(int fd, void* data, size_t byte_count);
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
// Reads `byte_count` bytes from the file descriptor at the specified offset.
// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
@@ -101,9 +102,9 @@
// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
// same function, but concurrently seeking or reading incrementally can lead to unexpected
// behavior.
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset);
+bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
-bool WriteFully(int fd, const void* data, size_t byte_count);
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/base/include/android-base/format.h
similarity index 62%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to base/include/android-base/format.h
index 410d379..6799c1f 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/base/include/android-base/format.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
+// It is accessed through its normal fmt:: namespace.
+#include <fmt/core.h>
+#include <fmt/format.h>
+#include <fmt/ostream.h>
+#include <fmt/printf.h>
+#include <fmt/time.h>
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 80513b1..2ab49ab 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -16,13 +16,14 @@
#pragma once
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-
#include <sys/types.h>
#include <memory>
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
+
#if defined(_WIN32)
#include <windows.h>
#define PROT_READ 1
@@ -35,7 +36,7 @@
namespace base {
/**
- * A region of a file mapped into memory.
+ * A region of a file mapped into memory, also known as MmapFile.
*/
class MappedFile {
public:
@@ -44,7 +45,8 @@
* `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
* will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
*/
- static std::unique_ptr<MappedFile> FromFd(int fd, off64_t offset, size_t length, int prot);
+ static std::unique_ptr<MappedFile> FromFd(borrowed_fd fd, off64_t offset, size_t length,
+ int prot);
/**
* Removes the mapping.
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 31e5273..31823df 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -49,9 +49,6 @@
T max = std::numeric_limits<T>::max());
// Sets the system property `key` to `value`.
-// Note that system property setting is inherently asynchronous so a return value of `true`
-// isn't particularly meaningful, and immediately reading back the value won't necessarily
-// tell you whether or not your call succeeded. A `false` return value definitely means failure.
bool SetProperty(const std::string& key, const std::string& value);
// Waits for the system property `key` to have the value `expected_value`.
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
new file mode 100644
index 0000000..1b763af
--- /dev/null
+++ b/base/include/android-base/result.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
+// Result<T>::error().
+//
+// ResultError is a type that contains both a std::string describing the error and a copy of errno
+// from when the error occurred. ResultError can be used in an ostream directly to print its
+// string value.
+//
+// Result<void> is the correct return type for a function that either returns successfully or
+// returns an error value. Returning {} from a function that returns Result<void> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T. This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
+// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
+// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
+// value can be directly specified via the Error() constructor.
+//
+// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
+// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
+//
+// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
+// In this case, the string that the ResultError takes is passed through the stream normally, but
+// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
+// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
+// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
+// used, the errno of the last one is respected.
+//
+// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
+// function that return Result<T> but you have a Result<U> and want to return its error. In this
+// case, you can return the .error() from the Result<U> to construct the Result<T>.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+// U output;
+// if (!SomeOtherCppFunction(input, &output)) {
+// return Errorf("SomeOtherCppFunction {} failed", input);
+// }
+// if (!c_api_function(output)) {
+// return ErrnoErrorf("c_api_function {} failed", output);
+// }
+// return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#pragma once
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+
+#include "android-base/expected.h"
+#include "android-base/format.h"
+
+namespace android {
+namespace base {
+
+struct ResultError {
+ template <typename T>
+ ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
+
+ template <typename T>
+ operator android::base::expected<T, ResultError>() {
+ return android::base::unexpected(ResultError(message_, code_));
+ }
+
+ std::string message() const { return message_; }
+ int code() const { return code_; }
+
+ private:
+ std::string message_;
+ int code_;
+};
+
+inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
+ return lhs.message() == rhs.message() && lhs.code() == rhs.code();
+}
+
+inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+ os << t.message();
+ return os;
+}
+
+class Error {
+ public:
+ Error() : errno_(0), append_errno_(false) {}
+ Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+
+ template <typename T>
+ operator android::base::expected<T, ResultError>() {
+ return android::base::unexpected(ResultError(str(), errno_));
+ }
+
+ template <typename T>
+ Error& operator<<(T&& t) {
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+ errno_ = t.code();
+ return (*this) << t.message();
+ }
+ int saved = errno;
+ ss_ << t;
+ errno = saved;
+ return *this;
+ }
+
+ const std::string str() const {
+ std::string str = ss_.str();
+ if (append_errno_) {
+ if (str.empty()) {
+ return strerror(errno_);
+ }
+ return std::move(str) + ": " + strerror(errno_);
+ }
+ return str;
+ }
+
+ Error(const Error&) = delete;
+ Error(Error&&) = delete;
+ Error& operator=(const Error&) = delete;
+ Error& operator=(Error&&) = delete;
+
+ template <typename... Args>
+ friend Error Errorf(const char* fmt, const Args&... args);
+
+ template <typename... Args>
+ friend Error ErrnoErrorf(const char* fmt, const Args&... args);
+
+ private:
+ Error(bool append_errno, int errno_to_append, const std::string& message)
+ : errno_(errno_to_append), append_errno_(append_errno) {
+ (*this) << message;
+ }
+
+ std::stringstream ss_;
+ int errno_;
+ const bool append_errno_;
+};
+
+inline Error ErrnoError() {
+ return Error(errno);
+}
+
+inline int ErrorCode(int code) {
+ return code;
+}
+
+// Return the error code of the last ResultError object, if any.
+// Otherwise, return `code` as it is.
+template <typename T, typename... Args>
+inline int ErrorCode(int code, T&& t, const Args&... args) {
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+ return ErrorCode(t.code(), args...);
+ }
+ return ErrorCode(code, args...);
+}
+
+template <typename... Args>
+inline Error Errorf(const char* fmt, const Args&... args) {
+ return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
+}
+
+template <typename... Args>
+inline Error ErrnoErrorf(const char* fmt, const Args&... args) {
+ return Error(true, errno, fmt::format(fmt, args...));
+}
+
+template <typename T>
+using Result = android::base::expected<T, ResultError>;
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 8e9716f..b1c22c9 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
+#include <string_view>
#include <vector>
namespace android {
@@ -68,5 +69,21 @@
// Tests whether 'lhs' equals 'rhs', ignoring case.
bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
+// Removes `prefix` from the start of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
+ if (!StartsWith(*s, prefix)) return false;
+ s->remove_prefix(prefix.size());
+ return true;
+}
+
+// Removes `suffix` from the end of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
+ if (!EndsWith(*s, suffix)) return false;
+ s->remove_suffix(suffix.size());
+ return true;
+}
+
} // namespace base
} // namespace android
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 3fa3bea..3a02cff 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -103,7 +103,17 @@
void reset(int new_value = -1) { reset(new_value, nullptr); }
int get() const { return fd_; }
+
+#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
+ // unique_fd's operator int is dangerous, but we have way too much code that
+ // depends on it, so make this opt-in at first.
operator int() const { return get(); } // NOLINT
+#endif
+
+ bool operator>=(int rhs) const { return get() >= rhs; }
+ bool operator<(int rhs) const { return get() < rhs; }
+ bool operator==(int rhs) const { return get() == rhs; }
+ bool operator!=(int rhs) const { return get() != rhs; }
// Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
bool operator!() const = delete;
@@ -246,6 +256,22 @@
#endif // !defined(_WIN32)
+// A wrapper type that can be implicitly constructed from either int or unique_fd.
+struct borrowed_fd {
+ /* implicit */ borrowed_fd(int fd) : fd_(fd) {}
+ template <typename T>
+ /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {}
+
+ int get() const { return fd_; }
+
+ bool operator>=(int rhs) const { return get() >= rhs; }
+ bool operator<(int rhs) const { return get() < rhs; }
+ bool operator==(int rhs) const { return get() == rhs; }
+ bool operator!=(int rhs) const { return get() != rhs; }
+
+ private:
+ int fd_ = -1;
+};
} // namespace base
} // namespace android
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index d26e8ba..f60de56 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -18,6 +18,8 @@
#include <errno.h>
+#include "android-base/unique_fd.h"
+
namespace android {
namespace base {
@@ -31,7 +33,8 @@
#endif
}
-std::unique_ptr<MappedFile> MappedFile::FromFd(int fd, off64_t offset, size_t length, int prot) {
+std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
+ int prot) {
static off64_t page_size = InitPageSize();
size_t slop = offset % page_size;
off64_t file_offset = offset - slop;
@@ -39,7 +42,7 @@
#if defined(_WIN32)
HANDLE handle =
- CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
+ CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), nullptr,
(prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
if (handle == nullptr) {
// http://b/119818070 "app crashes when reading asset of zero length".
@@ -58,7 +61,7 @@
return std::unique_ptr<MappedFile>(
new MappedFile{static_cast<char*>(base), length, slop, handle});
#else
- void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd, file_offset);
+ void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd.get(), file_offset);
if (base == MAP_FAILED) {
// http://b/119818070 "app crashes when reading asset of zero length".
// mmap fails with EINVAL for a zero length region.
diff --git a/base/result_test.cpp b/base/result_test.cpp
new file mode 100644
index 0000000..2ee4057
--- /dev/null
+++ b/base/result_test.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2017 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/result.h"
+
+#include "errno.h"
+
+#include <istream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace base {
+
+TEST(result, result_accessors) {
+ Result<std::string> result = "success";
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("success", *result);
+ EXPECT_EQ("success", result.value());
+
+ EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+ ASSERT_TRUE(Result<std::string>("success"));
+ ASSERT_TRUE(Result<std::string>("success").has_value());
+
+ EXPECT_EQ("success", *Result<std::string>("success"));
+ EXPECT_EQ("success", Result<std::string>("success").value());
+
+ EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_void) {
+ Result<void> ok = {};
+ EXPECT_TRUE(ok);
+ ok.value(); // should not crash
+ ASSERT_DEATH(ok.error(), "");
+
+ Result<void> fail = Error() << "failure" << 1;
+ EXPECT_FALSE(fail);
+ EXPECT_EQ("failure1", fail.error().message());
+ EXPECT_EQ(0, fail.error().code());
+ EXPECT_TRUE(ok != fail);
+ ASSERT_DEATH(fail.value(), "");
+
+ auto test = [](bool ok) -> Result<void> {
+ if (ok) return {};
+ else return Error() << "failure" << 1;
+ };
+ EXPECT_TRUE(test(true));
+ EXPECT_FALSE(test(false));
+ test(true).value(); // should not crash
+ ASSERT_DEATH(test(true).error(), "");
+ ASSERT_DEATH(test(false).value(), "");
+ EXPECT_EQ("failure1", test(false).error().message());
+}
+
+TEST(result, result_error) {
+ Result<void> result = Error() << "failure" << 1;
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error().code());
+ EXPECT_EQ("failure1", result.error().message());
+}
+
+TEST(result, result_error_empty) {
+ Result<void> result = Error();
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error().code());
+ EXPECT_EQ("", result.error().message());
+}
+
+TEST(result, result_error_rvalue) {
+ // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+ // Under the hood, they are an intermediate class that can be implicitly constructed into a
+ // Result<T>. This is needed both to create the ostream and because Error() itself, by
+ // definition will not know what the type, T, of the underlying Result<T> object that it would
+ // create is.
+
+ auto MakeRvalueErrorResult = []() -> Result<void> { return Error() << "failure" << 1; };
+ ASSERT_FALSE(MakeRvalueErrorResult());
+ ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+ EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
+ EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
+}
+
+TEST(result, result_errno_error) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<void> result = ErrnoError() << "failure" << 1;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_errno_error_no_text) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<void> result = ErrnoError();
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ(strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_error_from_other_result) {
+ auto error_text = "test error"s;
+ Result<void> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result2.error().code());
+ EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_error_through_ostream) {
+ auto error_text = "test error"s;
+ Result<void> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result2.error().code());
+ EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+ auto error_text = "test error"s;
+ constexpr int test_errno = 6;
+ errno = 6;
+ Result<void> result = ErrnoError() << error_text;
+
+ errno = 0;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(test_errno, result2.error().code());
+ EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
+}
+
+TEST(result, constructor_forwarding) {
+ auto result = Result<std::string>(std::in_place, 5, 'a');
+
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+};
+
+TEST(result, no_copy_on_return) {
+ // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+ // then those parameters are forwarded to the construction of Result<T>.
+
+ // If returning an prvalue or xvalue, it will be move constructed during the construction of
+ // Result<T>.
+
+ // This check ensures that that is the case, and particularly that no copy constructors
+ // are called.
+
+ auto result1 = ReturnConstructorTracker("");
+ ASSERT_TRUE(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result2 = ReturnConstructorTracker("test2");
+ ASSERT_TRUE(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result3 = ReturnConstructorTracker("test3");
+ ASSERT_TRUE(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor. This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+ auto return_result_result_with_success = []() -> Result<Result<void>> { return Result<void>(); };
+ auto result = return_result_result_with_success();
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(*result);
+
+ auto inner_result = result.value();
+ ASSERT_TRUE(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+ auto return_result_result_with_error = []() -> Result<Result<void>> {
+ return Result<void>(ResultError("failure string", 6));
+ };
+ auto result = return_result_result_with_error();
+ ASSERT_TRUE(result);
+ ASSERT_FALSE(*result);
+ EXPECT_EQ("failure string", (*result).error().message());
+ EXPECT_EQ(6, (*result).error().code());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+ struct TestStruct {
+ TestStruct(int value) : value_(value) {}
+ TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+ int value_;
+ };
+
+ auto return_test_struct = []() -> Result<TestStruct> {
+ return Result<TestStruct>(std::in_place, Result<TestStruct>(std::in_place, 6), 6);
+ };
+
+ auto result = return_test_struct();
+ ASSERT_TRUE(result);
+ EXPECT_EQ(36, result->value_);
+}
+
+TEST(result, die_on_access_failed_result) {
+ Result<std::string> result = Error();
+ ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+ Result<std::string> result = "success";
+ ASSERT_DEATH(result.error(), "");
+}
+
+template <class CharT>
+std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
+ errno = 2;
+ return ss;
+}
+
+TEST(result, preserve_errno) {
+ errno = 1;
+ int old_errno = errno;
+ Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
+ ASSERT_FALSE(result);
+ EXPECT_EQ(old_errno, errno);
+
+ errno = 1;
+ old_errno = errno;
+ Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
+ ASSERT_FALSE(result2);
+ EXPECT_EQ(old_errno, errno);
+ EXPECT_EQ(old_errno, result2.error().code());
+}
+
+TEST(result, error_with_fmt) {
+ Result<int> result = Errorf("{} {}!", "hello", "world");
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("{} {}!", std::string("hello"), std::string("world"));
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("{h} {w}!", fmt::arg("w", "world"), fmt::arg("h", "hello"));
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("hello world!");
+ EXPECT_EQ("hello world!", result.error().message());
+
+ Result<int> result2 = Errorf("error occurred with {}", result.error());
+ EXPECT_EQ("error occurred with hello world!", result2.error().message());
+
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ result = ErrnoErrorf("{} {}!", "hello", "world");
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, error_with_fmt_carries_errno) {
+ constexpr int inner_errno = 6;
+ errno = inner_errno;
+ Result<int> inner_result = ErrnoErrorf("inner failure");
+ errno = 0;
+ EXPECT_EQ(inner_errno, inner_result.error().code());
+
+ // outer_result is created with Errorf, but its error code is got from inner_result.
+ Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
+ EXPECT_EQ(inner_errno, outer_result.error().code());
+ EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
+ outer_result.error().message());
+
+ // now both result objects are created with ErrnoErrorf. errno from the inner_result
+ // is not passed to outer_result.
+ constexpr int outer_errno = 10;
+ errno = outer_errno;
+ outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
+ EXPECT_EQ(outer_errno, outer_result.error().code());
+ EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
+ strerror(outer_errno),
+ outer_result.error().message());
+}
+
+TEST(result, errno_chaining_multiple) {
+ constexpr int errno1 = 6;
+ errno = errno1;
+ Result<int> inner1 = ErrnoErrorf("error1");
+
+ constexpr int errno2 = 10;
+ errno = errno2;
+ Result<int> inner2 = ErrnoErrorf("error2");
+
+ // takes the error code of inner2 since its the last one.
+ Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
+ EXPECT_EQ(errno2, outer.error().code());
+ EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
+ outer.error().message());
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 9d74094..ca3c0b8 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -295,3 +295,19 @@
TEST(strings, ubsan_28729303) {
android::base::Split("/dev/null", ":");
}
+
+TEST(strings, ConsumePrefix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
+ ASSERT_EQ("bar", s);
+}
+
+TEST(strings, ConsumeSuffix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
+ ASSERT_EQ("foo", s);
+}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index cb09433..8979b0c 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -1253,7 +1253,12 @@
echo 1 reboot,empty # negative test (ID for unknown is 1)
;;
reboot)
- echo 1 reboo # negative test (ID for unknown is 1)
+ echo 1 reboog # negative test (ID for unknown is 1)
+ ;;
+ 'reboot,pmic_off_fault,.*')
+ echo ${id} reboot,pmic_off_fault,hello,world
+ echo ${id} reboot,pmic_off_fault,
+ echo 1 reboot,pmic_off_fault
;;
esac
echo ${id} "${match}" # matches b/c of exact
@@ -1266,6 +1271,8 @@
- (wait until screen is up, boot has completed)
- read bootstat for kBootReasonMap entries and test them all" ]
test_kBootReasonMap() {
+ checkDebugBuild || return
+ duration_test 15
local tempfile="`mktemp`"
local arg=--boot_reason_enum
adb_su bootstat ${arg} </dev/null 2>/dev/null |
@@ -1295,7 +1302,8 @@
report_bootstat_logs -t${T} \
'-bootstat: Service started: bootstat --boot_reason_enum=' \
'-bootstat: Unknown boot reason: reboot,empty' \
- '-bootstat: Unknown boot reason: reboo'
+ '-bootstat: Unknown boot reason: reboog' \
+ '-bootstat: Unknown boot reason: reboot,pmic_off_fault'
}
[ "USAGE: ${progname} [-s SERIAL] [tests]...
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 7b60524..8e7d918 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -140,7 +140,7 @@
{"mba_err", 13},
{"Watchdog", 14},
{"Panic", 15},
- {"power_key", 16},
+ {"power_key", 16}, // Mediatek
{"power_on", 17},
{"Reboot", 18},
{"rtc", 19},
@@ -203,13 +203,13 @@
{"shutdown,hibernate", 74}, // Suspend to DISK
{"power_on_key", 75},
{"reboot_by_key", 76},
- {"wdt_by_pass_pwk", 77},
+ {"wdt_by_pass_pwk", 77}, // Mediatek
{"reboot_longkey", 78},
{"powerkey", 79},
- {"usb", 80},
- {"wdt", 81},
- {"tool_by_pass_pwk", 82},
- {"2sec_reboot", 83},
+ {"usb", 80}, // Mediatek
+ {"wdt", 81}, // Mediatek
+ {"tool_by_pass_pwk", 82}, // Mediatek
+ {"2sec_reboot", 83}, // Mediatek
{"reboot,by_key", 84},
{"reboot,longkey", 85},
{"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec
@@ -276,10 +276,10 @@
{"software_master", 147},
{"cold,charger", 148},
{"cold,rtc", 149},
- {"cold,rtc,2sec", 150},
- {"reboot,tool", 151},
- {"reboot,wdt", 152},
- {"reboot,unknown", 153},
+ {"cold,rtc,2sec", 150}, // Mediatek
+ {"reboot,tool", 151}, // Mediatek
+ {"reboot,wdt", 152}, // Mediatek
+ {"reboot,unknown", 153}, // Mediatek
{"kernel_panic,audit", 154},
{"kernel_panic,atomic", 155},
{"kernel_panic,hung", 156},
@@ -304,6 +304,13 @@
{"reboot,pmic_off_fault,.*", 175},
{"reboot,pmic_off_s3rst,.*", 176},
{"reboot,pmic_off_other,.*", 177},
+ {"reboot,userrequested,fastboot", 178},
+ {"reboot,userrequested,recovery", 179},
+ {"reboot,userrequested,recovery,ui", 180},
+ {"shutdown,userrequested,fastboot", 181},
+ {"shutdown,userrequested,recovery", 182},
+ {"reboot,unknown[0-9]*", 183},
+ {"reboot,longkey,.*", 184},
};
// Converts a string value representing the reason the system booted to an
@@ -1174,6 +1181,7 @@
boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
+ RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.first_stage");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0bdfbf..3041664 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -193,6 +193,7 @@
fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
#endif
+ fprintf(stderr, " xom read execute-only memory\n");
fprintf(stderr, "\n");
fprintf(stderr, " LOG_ALWAYS_FATAL call liblog LOG_ALWAYS_FATAL\n");
fprintf(stderr, " LOG_ALWAYS_FATAL_IF call liblog LOG_ALWAYS_FATAL_IF\n");
@@ -314,6 +315,11 @@
} else if (!strcasecmp(arg, "seccomp")) {
set_system_seccomp_filter();
syscall(99999);
+#if defined(__LP64__)
+ } else if (!strcasecmp(arg, "xom")) {
+ // Try to read part of our code, which will fail if XOM is active.
+ printf("*%lx = %lx\n", reinterpret_cast<long>(usage), *reinterpret_cast<long*>(usage));
+#endif
#if defined(__arm__)
} else if (!strcasecmp(arg, "kuser_helper_version")) {
return __kuser_helper_version;
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index 7a3aa81..7bae356 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -15,6 +15,7 @@
*/
#include <assert.h>
+#include <string.h>
#include <cctype>
#include <stack>
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 1b09f79..409ef70 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -39,6 +39,7 @@
#include "flashing.h"
#include "utility.h"
+using android::fs_mgr::MetadataBuilder;
using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::CommandResult;
@@ -46,8 +47,6 @@
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
-using namespace android::fs_mgr;
-
struct VariableHandlers {
// Callback to retrieve the value of a single variable.
std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
@@ -340,7 +339,7 @@
PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name)
: device_(device) {
std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
- slot_number_ = SlotNumberForSlotSuffix(slot_suffix);
+ slot_number_ = android::fs_mgr::SlotNumberForSlotSuffix(slot_suffix);
auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
if (!super_device) {
return;
@@ -350,7 +349,7 @@
}
bool PartitionBuilder::Write() {
- std::unique_ptr<LpMetadata> metadata = builder_->Export();
+ auto metadata = builder_->Export();
if (!metadata) {
return false;
}
@@ -381,7 +380,7 @@
return device->WriteFail("Partition already exists");
}
- Partition* partition = builder->AddPartition(partition_name, 0);
+ auto partition = builder->AddPartition(partition_name, 0);
if (!partition) {
return device->WriteFail("Failed to add partition");
}
@@ -437,7 +436,7 @@
return device->WriteFail("Could not open super partition");
}
- Partition* partition = builder->FindPartition(partition_name);
+ auto partition = builder->FindPartition(partition_name);
if (!partition) {
return device->WriteFail("Partition does not exist");
}
@@ -466,7 +465,7 @@
class AutoMountMetadata {
public:
AutoMountMetadata() {
- Fstab proc_mounts;
+ android::fs_mgr::Fstab proc_mounts;
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
LOG(ERROR) << "Could not read /proc/mounts";
return;
@@ -494,7 +493,7 @@
explicit operator bool() const { return mounted_; }
private:
- Fstab fstab_;
+ android::fs_mgr::Fstab fstab_;
bool mounted_ = false;
bool should_unmount_ = false;
};
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 1014291..5066046 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -258,7 +258,7 @@
auto bytes_read_now =
handle_->read(handle_.get(), char_data, bytes_to_read, true /* allow_partial */);
if (bytes_read_now < 0) {
- return bytes_read_total;
+ return bytes_read_total == 0 ? -1 : bytes_read_total;
}
bytes_read_total += bytes_read_now;
char_data += bytes_read_now;
@@ -279,7 +279,7 @@
auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
if (bytes_written_now < 0) {
- return bytes_written_total;
+ return bytes_written_total == 0 ? -1 : bytes_written_total;
}
bytes_written_total += bytes_written_now;
char_data += bytes_written_now;
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index c8caa67..39abc4a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -502,9 +502,8 @@
static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
std::vector<char>* out) {
- ZipString zip_entry_name(entry_name.c_str());
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
return false;
}
@@ -614,9 +613,8 @@
static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
- ZipString zip_entry_name(entry_name);
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
errno = ENOENT;
return -1;
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index f597f50..8a3c213 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -106,7 +106,7 @@
kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
if (kr != 0) {
- ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
+ WARN("Couldn't create a device interface iterator: (%08x)\n", kr);
return -1;
}
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index b00edb3..bf840f8 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -195,25 +195,28 @@
ssize_t WindowsUsbTransport::Read(void* data, size_t len) {
unsigned long time_out = 0;
unsigned long read = 0;
+ size_t count = 0;
int ret;
DBG("usb_read %zu\n", len);
if (nullptr != handle_) {
- while (1) {
- int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
+ while (len > 0) {
+ size_t xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
ret = AdbReadEndpointSync(handle_->adb_read_pipe, data, xfer, &read, time_out);
errno = GetLastError();
- DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
- if (ret) {
- return read;
- } else {
+ DBG("usb_read got: %lu, expected: %zu, errno: %d\n", read, xfer, errno);
+ if (ret == 0) {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
usb_kick(handle_.get());
break;
}
- // else we timed out - try again
+ count += read;
+ len -= read;
+ data = (char*)data + read;
+
+ if (xfer != read || len == 0) return count;
}
} else {
DBG("usb_read NULL handle\n");
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 4ee9624..3d3503c 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -35,6 +35,7 @@
export_include_dirs: ["include"],
include_dirs: ["system/vold"],
srcs: [
+ "file_wait.cpp",
"fs_mgr.cpp",
"fs_mgr_format.cpp",
"fs_mgr_verity.cpp",
@@ -121,9 +122,14 @@
shared_libs: [
"libbootloader_message",
"libbase",
+ "libcutils",
"libcrypto",
+ "libext4_utils",
"libfec",
"libfs_mgr",
+ "liblog",
+ "liblp",
+ "libselinux",
],
header_libs: [
"libcutils_headers",
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index f89e598..d204bfd 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -117,3 +117,25 @@
be used to clear scratch storage to permit the flash.
Then reinstate the overrides and continue.
- File bugs or submit fixes for review.
+- There are other subtle caveats requiring complex logic to solve.
+ Have evaluated them as too complex or not worth the trouble, please
+ File a bug if a use case needs to be covered.
+ - The backing storage is treated fragile, if anything else has
+ issue with the space taken, the backing storage will be cleared
+ out and we reserve the right to not inform, if the layering
+ does not prevent any messaging.
+ - Space remaining threshold is hard coded. If 1% or more space
+ still remains, overlayfs will not be used, yet that amount of
+ space remaining is problematic.
+ - Flashing a partition via bootloader fastboot, as opposed to user
+ space fastbootd, is not detected, thus a partition may have
+ override content remaining. adb enable-verity to wipe.
+ - Space is limited, there is near unlimited space on userdata,
+ we have made an architectural decision to not utilize
+ /data/overlay/ at this time. Acquiring space to use for
+ backing remains an ongoing battle.
+ - First stage init, or ramdisk, can not be overriden.
+ - Backing storage will be discarded or ignored on errors, leading
+ to confusion. When debugging using **adb remount** it is
+ currently advised to confirm update is present after a reboot
+ to develop confidence.
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
new file mode 100644
index 0000000..cbf6845
--- /dev/null
+++ b/fs_mgr/file_wait.cpp
@@ -0,0 +1,235 @@
+// 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_mgr/file_wait.h>
+
+#include <limits.h>
+#if defined(__linux__)
+#include <poll.h>
+#include <sys/inotify.h>
+#endif
+#if defined(WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <functional>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+using namespace std::literals;
+using android::base::unique_fd;
+
+bool PollForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
+
+ while (true) {
+ if (!access(path.c_str(), F_OK) || errno != ENOENT) return true;
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
+ }
+}
+
+bool PollForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
+
+ while (true) {
+ if (access(path.c_str(), F_OK) && errno == ENOENT) return true;
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
+ }
+}
+
+#if defined(__linux__)
+class OneShotInotify {
+ public:
+ OneShotInotify(const std::string& path, uint32_t mask,
+ const std::chrono::milliseconds relative_timeout);
+
+ bool Wait();
+
+ private:
+ bool CheckCompleted();
+ int64_t RemainingMs() const;
+ bool ConsumeEvents();
+
+ enum class Result { Success, Timeout, Error };
+ Result WaitImpl();
+
+ unique_fd inotify_fd_;
+ std::string path_;
+ uint32_t mask_;
+ std::chrono::time_point<std::chrono::steady_clock> start_time_;
+ std::chrono::milliseconds relative_timeout_;
+ bool finished_;
+};
+
+OneShotInotify::OneShotInotify(const std::string& path, uint32_t mask,
+ const std::chrono::milliseconds relative_timeout)
+ : path_(path),
+ mask_(mask),
+ start_time_(std::chrono::steady_clock::now()),
+ relative_timeout_(relative_timeout),
+ finished_(false) {
+ // If the condition is already met, don't bother creating an inotify.
+ if (CheckCompleted()) return;
+
+ unique_fd inotify_fd(inotify_init1(IN_CLOEXEC | IN_NONBLOCK));
+ if (inotify_fd < 0) {
+ PLOG(ERROR) << "inotify_init1 failed";
+ return;
+ }
+
+ std::string watch_path;
+ if (mask == IN_CREATE) {
+ watch_path = android::base::Dirname(path);
+ } else {
+ watch_path = path;
+ }
+ if (inotify_add_watch(inotify_fd, watch_path.c_str(), mask) < 0) {
+ PLOG(ERROR) << "inotify_add_watch failed";
+ return;
+ }
+
+ // It's possible the condition was met before the add_watch. Check for
+ // this and abort early if so.
+ if (CheckCompleted()) return;
+
+ inotify_fd_ = std::move(inotify_fd);
+}
+
+bool OneShotInotify::Wait() {
+ Result result = WaitImpl();
+ if (result == Result::Success) return true;
+ if (result == Result::Timeout) return false;
+
+ // Some kind of error with inotify occurred, so fallback to a poll.
+ std::chrono::milliseconds timeout(RemainingMs());
+ if (mask_ == IN_CREATE) {
+ return PollForFile(path_, timeout);
+ } else if (mask_ == IN_DELETE_SELF) {
+ return PollForFileDeleted(path_, timeout);
+ } else {
+ LOG(ERROR) << "Unknown inotify mask: " << mask_;
+ return false;
+ }
+}
+
+OneShotInotify::Result OneShotInotify::WaitImpl() {
+ // If the operation completed super early, we'll never have created an
+ // inotify instance.
+ if (finished_) return Result::Success;
+ if (inotify_fd_ < 0) return Result::Error;
+
+ while (true) {
+ auto remaining_ms = RemainingMs();
+ if (remaining_ms <= 0) return Result::Timeout;
+
+ struct pollfd event = {
+ .fd = inotify_fd_,
+ .events = POLLIN,
+ .revents = 0,
+ };
+ int rv = poll(&event, 1, static_cast<int>(remaining_ms));
+ if (rv <= 0) {
+ if (rv == 0 || errno == EINTR) {
+ continue;
+ }
+ PLOG(ERROR) << "poll for inotify failed";
+ return Result::Error;
+ }
+ if (event.revents & POLLERR) {
+ LOG(ERROR) << "error reading inotify for " << path_;
+ return Result::Error;
+ }
+
+ // Note that we don't bother checking what kind of event it is, since
+ // it's cheap enough to just see if the initial condition is satisified.
+ // If it's not, we consume all the events available and continue.
+ if (CheckCompleted()) return Result::Success;
+ if (!ConsumeEvents()) return Result::Error;
+ }
+}
+
+bool OneShotInotify::CheckCompleted() {
+ if (mask_ == IN_CREATE) {
+ finished_ = !access(path_.c_str(), F_OK) || errno != ENOENT;
+ } else if (mask_ == IN_DELETE_SELF) {
+ finished_ = access(path_.c_str(), F_OK) && errno == ENOENT;
+ } else {
+ LOG(ERROR) << "Unexpected mask: " << mask_;
+ }
+ return finished_;
+}
+
+bool OneShotInotify::ConsumeEvents() {
+ // According to the manpage, this is enough to read at least one event.
+ static constexpr size_t kBufferSize = sizeof(struct inotify_event) + NAME_MAX + 1;
+ char buffer[kBufferSize];
+
+ do {
+ ssize_t rv = TEMP_FAILURE_RETRY(read(inotify_fd_, buffer, sizeof(buffer)));
+ if (rv <= 0) {
+ if (rv == 0 || errno == EAGAIN) {
+ return true;
+ }
+ PLOG(ERROR) << "read inotify failed";
+ return false;
+ }
+ } while (true);
+}
+
+int64_t OneShotInotify::RemainingMs() const {
+ auto remaining = (std::chrono::steady_clock::now() - start_time_);
+ auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
+ return (relative_timeout_ - elapsed).count();
+}
+#endif
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+#if defined(__linux__)
+ OneShotInotify inotify(path, IN_CREATE, relative_timeout);
+ return inotify.Wait();
+#else
+ return PollForFile(path, relative_timeout);
+#endif
+}
+
+// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+#if defined(__linux__)
+ OneShotInotify inotify(path, IN_DELETE_SELF, relative_timeout);
+ return inotify.Wait();
+#else
+ return PollForFileDeleted(path, relative_timeout);
+#endif
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 3ea479e..2a9a9d0 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -56,6 +56,7 @@
#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/wipe.h>
#include <fs_avb/fs_avb.h>
+#include <fs_mgr/file_wait.h>
#include <fs_mgr_overlayfs.h>
#include <libdm/dm.h>
#include <liblp/metadata_format.h>
@@ -116,28 +117,6 @@
FS_STAT_ENABLE_VERITY_FAILED = 0x80000,
};
-// TODO: switch to inotify()
-bool fs_mgr_wait_for_file(const std::string& filename,
- const std::chrono::milliseconds relative_timeout,
- FileWaitMode file_wait_mode) {
- auto start_time = std::chrono::steady_clock::now();
-
- while (true) {
- int rv = access(filename.c_str(), F_OK);
- if (file_wait_mode == FileWaitMode::Exists) {
- if (!rv || errno != ENOENT) return true;
- } else if (file_wait_mode == FileWaitMode::DoesNotExist) {
- if (rv && errno == ENOENT) return true;
- }
-
- std::this_thread::sleep_for(50ms);
-
- auto now = std::chrono::steady_clock::now();
- auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- if (time_elapsed > relative_timeout) return false;
- }
-}
-
static void log_fs_stat(const std::string& blk_device, int fs_stat) {
if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
std::string msg =
@@ -245,11 +224,11 @@
if (should_force_check(*fs_stat)) {
ret = android_fork_execvp_ext(
ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
- true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
} else {
ret = android_fork_execvp_ext(
ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
- LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
}
if (ret < 0) {
@@ -263,13 +242,19 @@
}
} else if (is_f2fs(fs_type)) {
const char* f2fs_fsck_argv[] = {F2FS_FSCK_BIN, "-a", blk_device.c_str()};
- LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
+ const char* f2fs_fsck_forced_argv[] = {F2FS_FSCK_BIN, "-f", blk_device.c_str()};
- ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv),
- const_cast<char **>(f2fs_fsck_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true, const_cast<char *>(FSCK_LOG_FILE),
- NULL, 0);
+ if (should_force_check(*fs_stat)) {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(f2fs_fsck_forced_argv), const_cast<char**>(f2fs_fsck_forced_argv), &status,
+ true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+ } else {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(f2fs_fsck_argv), const_cast<char**>(f2fs_fsck_argv), &status, true,
+ LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+ }
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
@@ -1097,8 +1082,7 @@
continue;
}
- if (current_entry.fs_mgr_flags.wait &&
- !fs_mgr_wait_for_file(current_entry.blk_device, 20s)) {
+ if (current_entry.fs_mgr_flags.wait && !WaitForFile(current_entry.blk_device, 20s)) {
LERROR << "Skipping '" << current_entry.blk_device << "' during mount_all";
continue;
}
@@ -1367,7 +1351,7 @@
}
// First check the filesystem if requested.
- if (fstab_entry.fs_mgr_flags.wait && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+ if (fstab_entry.fs_mgr_flags.wait && !WaitForFile(n_blk_device, 20s)) {
LERROR << "Skipping mounting '" << n_blk_device << "'";
continue;
}
@@ -1570,7 +1554,7 @@
fprintf(zram_fp.get(), "%" PRId64 "\n", entry.zram_size);
}
- if (entry.fs_mgr_flags.wait && !fs_mgr_wait_for_file(entry.blk_device, 20s)) {
+ if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
LERROR << "Skipping mkswap for '" << entry.blk_device << "'";
ret = false;
continue;
@@ -1610,38 +1594,6 @@
return ret;
}
-bool fs_mgr_load_verity_state(int* mode) {
- /* return the default mode, unless any of the verified partitions are in
- * logging mode, in which case return that */
- *mode = VERITY_MODE_DEFAULT;
-
- Fstab fstab;
- if (!ReadDefaultFstab(&fstab)) {
- LERROR << "Failed to read default fstab";
- return false;
- }
-
- for (const auto& entry : fstab) {
- if (entry.fs_mgr_flags.avb) {
- *mode = VERITY_MODE_RESTART; // avb only supports restart mode.
- break;
- } else if (!entry.fs_mgr_flags.verify) {
- continue;
- }
-
- int current;
- if (load_verity_state(entry, ¤t) < 0) {
- continue;
- }
- if (current != VERITY_MODE_DEFAULT) {
- *mode = current;
- break;
- }
- }
-
- return true;
-}
-
bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
return false;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index ee6ffdb..1f21a71 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -38,6 +38,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.h>
#include <liblp/reader.h>
#include "fs_mgr_priv.h"
@@ -128,7 +129,7 @@
return false;
}
if (timeout_ms > std::chrono::milliseconds::zero()) {
- if (!fs_mgr_wait_for_file(*path, timeout_ms, FileWaitMode::Exists)) {
+ if (!WaitForFile(*path, timeout_ms)) {
DestroyLogicalPartition(name, {});
LERROR << "Timed out waiting for device path: " << *path;
return false;
@@ -202,7 +203,7 @@
if (!dm.DeleteDevice(name)) {
return false;
}
- if (!path.empty() && !fs_mgr_wait_for_file(path, timeout_ms, FileWaitMode::DoesNotExist)) {
+ if (!path.empty() && !WaitForFileDeleted(path, timeout_ms)) {
LERROR << "Timed out waiting for device path to unlink: " << path;
return false;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 7df7cfd..31790b1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -171,6 +171,18 @@
fs_options.append(","); // appends a comma if not the first
}
fs_options.append(flag);
+
+ if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
+ std::string arg;
+ if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
+ arg = flag.substr(equal_sign + 1);
+ }
+ if (!ParseInt(arg, &entry->reserved_size)) {
+ LWARNING << "Warning: reserve_root= flag malformed: " << arg;
+ } else {
+ entry->reserved_size <<= 12;
+ }
+ }
}
}
entry->fs_options = std::move(fs_options);
@@ -262,10 +274,6 @@
LWARNING << "Warning: zramsize= flag malformed: " << arg;
}
}
- } else if (StartsWith(flag, "verify=")) {
- // If the verify flag is followed by an = and the location for the verity state.
- entry->fs_mgr_flags.verify = true;
- entry->verity_loc = arg;
} else if (StartsWith(flag, "forceencrypt=")) {
// The forceencrypt flag is followed by an = and the location of the keys.
entry->fs_mgr_flags.force_crypt = true;
@@ -719,6 +727,7 @@
[&skip_mount_point](const auto& entry) {
return entry.mount_point == skip_mount_point;
});
+ if (it == fstab->end()) continue;
fstab->erase(it, fstab->end());
LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
}
@@ -808,8 +817,11 @@
std::string GetVerityDeviceName(const FstabEntry& entry) {
std::string base_device;
if (entry.mount_point == "/") {
- // In AVB, the dm device name is vroot instead of system.
- base_device = entry.fs_mgr_flags.avb ? "vroot" : "system";
+ // When using system-as-root, the device name is fixed as "vroot".
+ if (entry.fs_mgr_flags.avb) {
+ return "vroot";
+ }
+ base_device = "system";
} else {
base_device = android::base::Basename(entry.mount_point);
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 0a6014d..05ca5fc 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -44,6 +44,7 @@
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
+#include <fs_mgr/file_wait.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
@@ -652,6 +653,25 @@
return true;
}
+static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
+ auto& dm = DeviceMapper::Instance();
+
+ // Remove <other> partitions
+ for (const auto& group : builder->ListGroups()) {
+ for (const auto& part : builder->ListPartitionsInGroup(group)) {
+ const auto& name = part->name();
+ if (!android::base::EndsWith(name, suffix)) {
+ continue;
+ }
+ if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name, 2s)) {
+ continue;
+ }
+ builder->ResizePartition(builder->FindPartition(name), 0);
+ }
+ }
+}
+
+// This is where we find and steal backing storage from the system.
bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
bool* partition_exists, bool* change) {
*scratch_device = fs_mgr_overlayfs_scratch_device();
@@ -698,15 +718,24 @@
// the adb remount overrides :-( .
auto margin_size = uint64_t(3 * 256 * 1024);
BlockDeviceInfo info;
- if (builder->GetBlockDeviceInfo(partition_name, &info)) {
+ if (builder->GetBlockDeviceInfo(fs_mgr_get_super_partition_name(slot_number), &info)) {
margin_size = 3 * info.logical_block_size;
}
partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
partition_size / 2);
if (partition_size > partition->size()) {
if (!builder->ResizePartition(partition, partition_size)) {
- LERROR << "resize " << partition_name;
- return false;
+ // Try to free up space by deallocating partitions in the other slot.
+ TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
+
+ partition_size =
+ builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+ partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+ partition_size / 2);
+ if (!builder->ResizePartition(partition, partition_size)) {
+ LERROR << "resize " << partition_name;
+ return false;
+ }
}
if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
changed = true;
@@ -839,7 +868,7 @@
scratch_can_be_mounted = false;
auto scratch_device = fs_mgr_overlayfs_scratch_device();
if (fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device) &&
- fs_mgr_wait_for_file(scratch_device, 10s)) {
+ WaitForFile(scratch_device, 10s)) {
const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
if (fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type,
true /* readonly */)) {
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 70abf5b..3a33cf3 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -88,18 +88,11 @@
using namespace std::chrono_literals;
-enum class FileWaitMode { Exists, DoesNotExist };
-
-bool fs_mgr_wait_for_file(const std::string& filename,
- const std::chrono::milliseconds relative_timeout,
- FileWaitMode wait_mode = FileWaitMode::Exists);
-
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
bool fs_mgr_is_device_unlocked();
const std::string& get_android_dt_dir();
bool is_dt_compatible();
-int load_verity_state(const android::fs_mgr::FstabEntry& entry, int* mode);
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 967631b..149bee3 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -93,14 +93,14 @@
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.";
+[[noreturn]] void reboot(bool overlayfs = false) {
+ if (overlayfs) {
+ LOG(INFO) << "Successfully setup overlayfs\nrebooting device";
} else {
LOG(INFO) << "Successfully disabled verity\nrebooting device";
}
::sync();
- android::base::SetProperty(ANDROID_RB_PROPERTY, dedupe ? "reboot,recovery" : "reboot,remount");
+ android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
::sleep(60);
::exit(0); // SUCCESS
}
@@ -249,48 +249,38 @@
// Check verity and optionally setup overlayfs backing.
auto reboot_later = false;
- auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
+ auto user_please_reboot_later = false;
+ auto setup_overlayfs = false;
auto just_disabled_verity = false;
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;
+ auto ret = false;
if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") != "locked") {
if (AvbOps* ops = avb_ops_user_new()) {
- auto ret = avb_user_verity_set(
+ 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) << "Disabling verity for " << mount_point;
- just_disabled_verity = true;
- 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) << "Disabling verity for " << mount_point;
- just_disabled_verity = true;
- reboot_later = can_reboot;
- if (reboot_later && !uses_overlayfs) {
- ++it;
- continue;
- }
- }
- }
+ }
+ if (!ret && fs_mgr_set_blk_ro(entry.blk_device, false)) {
+ fec::io fh(entry.blk_device.c_str(), O_RDWR);
+ ret = fh && fh.set_verity_status(false);
+ }
+ if (ret) {
+ LOG(WARNING) << "Disabling verity for " << mount_point;
+ just_disabled_verity = true;
+ reboot_later = can_reboot;
+ user_please_reboot_later = true;
}
}
- LOG(ERROR) << "Skipping " << mount_point;
- it = partitions.erase(it);
- continue;
+ if (!ret) {
+ LOG(ERROR) << "Skipping " << mount_point << " for remount";
+ it = partitions.erase(it);
+ continue;
+ }
}
auto change = false;
@@ -298,6 +288,9 @@
if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change, just_disabled_verity)) {
if (change) {
LOG(INFO) << "Using overlayfs for " << mount_point;
+ reboot_later = can_reboot;
+ user_please_reboot_later = true;
+ setup_overlayfs = true;
}
} else if (errno) {
PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
@@ -308,8 +301,12 @@
++it;
}
- if (partitions.empty()) {
- if (reboot_later) reboot(false);
+ if (partitions.empty() || just_disabled_verity) {
+ if (reboot_later) reboot(setup_overlayfs);
+ if (user_please_reboot_later) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ return 0;
+ }
LOG(WARNING) << "No partitions to remount";
return retval;
}
@@ -385,7 +382,11 @@
retval = REMOUNT_FAILED;
}
- if (reboot_later) reboot(false);
+ if (reboot_later) reboot(setup_overlayfs);
+ if (user_please_reboot_later) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ return 0;
+ }
return retval;
}
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 58ef9b6..1e65587 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -101,7 +101,9 @@
}
}
- auto mounted = GetMountState(rec->mount_point);
+ const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
+
+ auto mounted = GetMountState(mount_point);
if (mounted == MountState::ERROR) {
return false;
}
@@ -109,8 +111,6 @@
return true;
}
- const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
-
static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 3f09157..be8077b 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,6 +35,7 @@
#include <android-base/unique_fd.h>
#include <crypto_utils/android_pubkey.h>
#include <cutils/properties.h>
+#include <fs_mgr/file_wait.h>
#include <libdm/dm.h>
#include <logwrap/logwrap.h>
#include <openssl/obj_mac.h>
@@ -275,248 +276,6 @@
return 0;
}
-static int check_verity_restart(const char *fname)
-{
- char buffer[VERITY_KMSG_BUFSIZE + 1];
- int fd;
- int rc = 0;
- ssize_t size;
- struct stat s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- if (errno != ENOENT) {
- PERROR << "Failed to open " << fname;
- }
- goto out;
- }
-
- if (fstat(fd, &s) == -1) {
- PERROR << "Failed to fstat " << fname;
- goto out;
- }
-
- size = VERITY_KMSG_BUFSIZE;
-
- if (size > s.st_size) {
- size = s.st_size;
- }
-
- if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
- PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
- goto out;
- }
-
- if (!android::base::ReadFully(fd, buffer, size)) {
- PERROR << "Failed to read " << size << " bytes from " << fname;
- goto out;
- }
-
- buffer[size] = '\0';
-
- if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
- rc = 1;
- }
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int was_verity_restart()
-{
- static const char* files[] = {
- // clang-format off
- "/sys/fs/pstore/console-ramoops-0",
- "/sys/fs/pstore/console-ramoops",
- "/proc/last_kmsg",
- NULL
- // clang-format on
- };
- int i;
-
- for (i = 0; files[i]; ++i) {
- if (check_verity_restart(files[i])) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static int metadata_add(FILE *fp, long start, const char *tag,
- unsigned int length, off64_t *offset)
-{
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fprintf(fp, "%s %u\n", tag, length) < 0) {
- return -1;
- }
-
- *offset = ftell(fp);
-
- if (fseek(fp, length, SEEK_CUR) < 0 ||
- fprintf(fp, METADATA_EOD " 0\n") < 0) {
- return -1;
- }
-
- return 0;
-}
-
-static int metadata_find(const char *fname, const char *stag,
- unsigned int slength, off64_t *offset)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int rc = -1;
- int n;
- long start = 0x4000; /* skip cryptfs metadata area */
- uint32_t magic;
- unsigned int length = 0;
-
- if (!fname) {
- return -1;
- }
-
- auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
-
- if (!fp) {
- PERROR << "Failed to open " << fname;
- return -1;
- }
-
- /* check magic */
- if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to read magic from " << fname;
- return -1;
- }
-
- if (magic != METADATA_MAGIC) {
- magic = METADATA_MAGIC;
-
- if (fseek(fp.get(), start, SEEK_SET) < 0 ||
- fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to write magic to " << fname;
- return -1;
- }
-
- rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to add metadata to " << fname;
- }
-
- return rc;
- }
-
- start += sizeof(magic);
-
- while (1) {
- n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
-
- if (n == 2 && strcmp(tag, METADATA_EOD)) {
- /* found a tag */
- start = ftell(fp.get());
-
- if (!strcmp(tag, stag) && length == slength) {
- *offset = start;
- return 0;
- }
-
- start += length;
-
- if (fseek(fp.get(), length, SEEK_CUR) < 0) {
- PERROR << "Failed to seek " << fname;
- return -1;
- }
- } else {
- rc = metadata_add(fp.get(), start, stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to write metadata to " << fname;
- }
- return rc;
- }
- }
-}
-
-static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
-{
- int fd;
- int rc = -1;
- struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
- << " to offset " << offset;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int read_verity_state(const char *fname, off64_t offset, int *mode)
-{
- int fd = -1;
- int rc = -1;
- struct verity_state s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to read " << sizeof(s) << " bytes from " << fname
- << " offset " << offset;
- goto out;
- }
-
- if (s.header != VERITY_STATE_HEADER) {
- /* space allocated, but no state written. write default state */
- *mode = VERITY_MODE_DEFAULT;
- rc = write_verity_state(fname, offset, *mode);
- goto out;
- }
-
- if (s.version != VERITY_STATE_VERSION) {
- LERROR << "Unsupported verity state version (" << s.version << ")";
- goto out;
- }
-
- if (s.mode < VERITY_MODE_EIO ||
- s.mode > VERITY_MODE_LAST) {
- LERROR << "Unsupported verity mode (" << s.mode << ")";
- goto out;
- }
-
- *mode = s.mode;
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
static int read_partition(const char *path, uint64_t size)
{
char buf[READ_BUF_SIZE];
@@ -540,119 +299,23 @@
return 0;
}
-static int compare_last_signature(const FstabEntry& entry, int* match) {
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int fd = -1;
- int rc = -1;
- off64_t offset = 0;
- struct fec_handle *f = NULL;
- struct fec_verity_metadata verity;
- uint8_t curr[SHA256_DIGEST_LENGTH];
- uint8_t prev[SHA256_DIGEST_LENGTH];
-
- *match = 1;
-
- if (fec_open(&f, entry.blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) ==
- -1) {
- PERROR << "Failed to open '" << entry.blk_device << "'";
- return rc;
- }
-
- // read verity metadata
- if (fec_verity_get_metadata(f, &verity) == -1) {
- PERROR << "Failed to get verity metadata '" << entry.blk_device << "'";
- goto out;
- }
-
- SHA256(verity.signature, sizeof(verity.signature), curr);
-
- if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s", basename(entry.mount_point.c_str())) >=
- (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << entry.mount_point;
- goto out;
- }
-
- if (metadata_find(entry.verity_loc.c_str(), tag, SHA256_DIGEST_LENGTH, &offset) < 0) {
- goto out;
- }
-
- fd = TEMP_FAILURE_RETRY(open(entry.verity_loc.c_str(), O_RDWR | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << entry.verity_loc;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev), offset)) != sizeof(prev)) {
- PERROR << "Failed to read " << sizeof(prev) << " bytes from " << entry.verity_loc
- << " offset " << offset;
- goto out;
- }
-
- *match = !memcmp(curr, prev, SHA256_DIGEST_LENGTH);
-
- if (!*match) {
- /* update current signature hash */
- if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
- offset)) != sizeof(curr)) {
- PERROR << "Failed to write " << sizeof(curr) << " bytes to " << entry.verity_loc
- << " offset " << offset;
- goto out;
- }
- }
-
- rc = 0;
-
-out:
- fec_close(f);
- return rc;
-}
-
-static int get_verity_state_offset(const FstabEntry& entry, off64_t* offset) {
- char tag[METADATA_TAG_MAX_LENGTH + 1];
-
- if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s", basename(entry.mount_point.c_str())) >=
- (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << entry.mount_point;
- return -1;
- }
-
- return metadata_find(entry.verity_loc.c_str(), tag, sizeof(struct verity_state), offset);
-}
-
-int load_verity_state(const FstabEntry& entry, int* mode) {
+bool fs_mgr_load_verity_state(int* mode) {
// unless otherwise specified, use EIO mode.
*mode = VERITY_MODE_EIO;
- // use the kernel parameter if set.
- std::string veritymode;
- if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
- if (veritymode == "enforcing") {
- *mode = VERITY_MODE_DEFAULT;
- }
- return 0;
+ // The bootloader communicates verity mode via the kernel commandline
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ return false;
}
- off64_t offset = 0;
- if (get_verity_state_offset(entry, &offset) < 0) {
- /* fall back to stateless behavior */
- return 0;
- }
-
- if (was_verity_restart()) {
- /* device was restarted after dm-verity detected a corrupted
- * block, so use EIO mode */
- return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
- }
-
- int match = 0;
- if (!compare_last_signature(entry, &match) && !match) {
- /* partition has been reflashed, reset dm-verity state */
+ if (verity_mode == "enforcing") {
*mode = VERITY_MODE_DEFAULT;
- return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
+ } else if (verity_mode == "logging") {
+ *mode = VERITY_MODE_LOGGING;
}
- return read_verity_state(entry.verity_loc.c_str(), offset, mode);
+ return true;
}
// Update the verity table using the actual block device path.
@@ -759,7 +422,7 @@
params.ecc_dev = entry->blk_device.c_str();
- if (load_verity_state(*entry, ¶ms.mode) < 0) {
+ if (!fs_mgr_load_verity_state(¶ms.mode)) {
/* if accessing or updating the state failed, switch to the default
* safe mode. This makes sure the device won't end up in an endless
* restart loop, and no corrupted data will be exposed to userspace
@@ -867,7 +530,7 @@
}
// make sure we've set everything up properly
- if (wait_for_verity_dev && !fs_mgr_wait_for_file(entry->blk_device, 1s)) {
+ if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) {
goto out;
}
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
new file mode 100644
index 0000000..74d160e
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -0,0 +1,34 @@
+// 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 <chrono>
+#include <string>
+
+namespace android {
+namespace fs_mgr {
+
+// Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)
+// must already exist. For example, to wait on /dev/block/dm-6, /dev/block must
+// be a valid directory.
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);
+
+// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
+// Note that this only returns true if the inode itself no longer exists, i.e.,
+// all outstanding file descriptors have been closed.
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index d7afed6..c7193ab 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,6 @@
std::string fs_options;
std::string key_loc;
std::string key_dir;
- std::string verity_loc;
off64_t length = 0;
std::string label;
int partnum = -1;
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index c8c2d83..21255df 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -46,6 +46,7 @@
static_libs: [
"libdm",
"libbase",
+ "libfs_mgr",
"liblog",
],
srcs: [
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a4614d0..d54b6ef 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -210,6 +210,20 @@
return true;
}
+bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
+ std::vector<DmTargetTypeInfo> targets;
+ if (!GetAvailableTargets(&targets)) {
+ return false;
+ }
+ for (const auto& target : targets) {
+ if (target.name() == name) {
+ if (info) *info = target;
+ return true;
+ }
+ }
+ return false;
+}
+
bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
devices->clear();
@@ -288,6 +302,26 @@
return true;
}
+bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
+ return false;
+ }
+ *dev = io.dev;
+ return true;
+}
+
+bool DeviceMapper::GetDeviceString(const std::string& name, std::string* dev) {
+ dev_t num;
+ if (!GetDeviceNumber(name, &num)) {
+ return false;
+ }
+ *dev = std::to_string(major(num)) + ":" + std::to_string(minor(num));
+ return true;
+}
+
bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
return GetTable(name, 0, table);
}
@@ -354,5 +388,13 @@
}
}
+std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
+ if (const void* p = memchr(spec.target_type, '\0', sizeof(spec.target_type))) {
+ ptrdiff_t length = reinterpret_cast<const char*>(p) - spec.target_type;
+ return std::string{spec.target_type, static_cast<size_t>(length)};
+ }
+ return std::string{spec.target_type, sizeof(spec.target_type)};
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index cb33eea..da1013e 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -16,8 +16,12 @@
#include "libdm/dm_target.h"
+#include <stdio.h>
+#include <sys/types.h>
+
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <libdm/dm.h>
@@ -115,5 +119,107 @@
return keyid_ + " " + block_device_;
}
+std::string DmTargetSnapshot::name() const {
+ if (mode_ == SnapshotStorageMode::Merge) {
+ return "snapshot-merge";
+ }
+ return "snapshot";
+}
+
+std::string DmTargetSnapshot::GetParameterString() const {
+ std::string mode;
+ switch (mode_) {
+ case SnapshotStorageMode::Persistent:
+ case SnapshotStorageMode::Merge:
+ // Note: "O" lets us query for overflow in the status message. This
+ // is only supported on kernels 4.4+. On earlier kernels, an overflow
+ // will be reported as "Invalid" in the status string.
+ mode = "P";
+ if (ReportsOverflow(name())) {
+ mode += "O";
+ }
+ break;
+ case SnapshotStorageMode::Transient:
+ mode = "N";
+ break;
+ default:
+ LOG(ERROR) << "DmTargetSnapshot unknown mode";
+ break;
+ }
+ return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
+}
+
+bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ DmTargetTypeInfo info;
+ if (!dm.GetTargetByName(target_type, &info)) {
+ return false;
+ }
+ if (target_type == "snapshot") {
+ return info.IsAtLeast(1, 15, 0);
+ }
+ if (target_type == "snapshot-merge") {
+ return info.IsAtLeast(1, 4, 0);
+ }
+ return false;
+}
+
+bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+ auto sections = android::base::Split(text, " ");
+ if (sections.size() == 1) {
+ // This is probably an error code, "Invalid" is possible as is "Overflow"
+ // on 4.4+.
+ status->error = text;
+ return true;
+ }
+ if (sections.size() != 2) {
+ LOG(ERROR) << "snapshot status should have two components";
+ return false;
+ }
+ auto sector_info = android::base::Split(sections[0], "/");
+ if (sector_info.size() != 2) {
+ LOG(ERROR) << "snapshot sector info should have two components";
+ return false;
+ }
+ if (!android::base::ParseUint(sections[1], &status->metadata_sectors)) {
+ LOG(ERROR) << "could not parse metadata sectors";
+ return false;
+ }
+ if (!android::base::ParseUint(sector_info[0], &status->sectors_allocated)) {
+ LOG(ERROR) << "could not parse sectors allocated";
+ return false;
+ }
+ if (!android::base::ParseUint(sector_info[1], &status->total_sectors)) {
+ LOG(ERROR) << "could not parse total sectors";
+ return false;
+ }
+ return true;
+}
+
+std::string DmTargetCrypt::GetParameterString() const {
+ std::vector<std::string> argv = {
+ cipher_,
+ key_,
+ std::to_string(iv_sector_offset_),
+ device_,
+ std::to_string(device_sector_),
+ };
+
+ std::vector<std::string> extra_argv;
+ if (allow_discards_) extra_argv.emplace_back("allow_discards");
+ if (allow_encrypt_override_) extra_argv.emplace_back("allow_encrypt_override");
+ if (iv_large_sectors_) extra_argv.emplace_back("iv_large_sectors");
+ if (sector_size_) extra_argv.emplace_back("sector_size:" + std::to_string(sector_size_));
+
+ if (!extra_argv.empty()) argv.emplace_back(std::to_string(extra_argv.size()));
+
+ argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
+ return android::base::Join(argv, " ");
+}
+
+std::string DmTargetDefaultKey::GetParameterString() const {
+ return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_);
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 70823c6..c5881dd 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <ctime>
+#include <iostream>
#include <map>
#include <thread>
@@ -35,21 +36,15 @@
#include "test_util.h"
using namespace std;
+using namespace std::chrono_literals;
using namespace android::dm;
using unique_fd = android::base::unique_fd;
TEST(libdm, HasMinimumTargets) {
+ DmTargetTypeInfo info;
+
DeviceMapper& dm = DeviceMapper::Instance();
- vector<DmTargetTypeInfo> targets;
- ASSERT_TRUE(dm.GetAvailableTargets(&targets));
-
- map<string, DmTargetTypeInfo> by_name;
- for (const auto& target : targets) {
- by_name[target.name()] = target;
- }
-
- auto iter = by_name.find("linear");
- EXPECT_NE(iter, by_name.end());
+ ASSERT_TRUE(dm.GetTargetByName("linear", &info));
}
// Helper to ensure that device mapper devices are released.
@@ -137,8 +132,8 @@
// Define a 2-sector device, with each sector mapping to the first sector
// of one of our loop devices.
DmTable table;
- ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(0, 1, loop_a.device(), 0)));
- ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(1, 1, loop_b.device(), 0)));
+ ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
+ ASSERT_TRUE(table.Emplace<DmTargetLinear>(1, 1, loop_b.device(), 0));
ASSERT_TRUE(table.valid());
TempDevice dev("libdm-test-dm-linear", table);
@@ -146,6 +141,16 @@
ASSERT_FALSE(dev.path().empty());
ASSERT_TRUE(dev.WaitForUdev());
+ auto& dm = DeviceMapper::Instance();
+
+ dev_t dev_number;
+ ASSERT_TRUE(dm.GetDeviceNumber(dev.name(), &dev_number));
+ ASSERT_NE(dev_number, 0);
+
+ std::string dev_string;
+ ASSERT_TRUE(dm.GetDeviceString(dev.name(), &dev_string));
+ ASSERT_FALSE(dev_string.empty());
+
// Note: a scope is needed to ensure that there are no open descriptors
// when we go to close the device.
{
@@ -162,7 +167,6 @@
}
// Test GetTableStatus.
- DeviceMapper& dm = DeviceMapper::Instance();
vector<DeviceMapper::TargetInfo> targets;
ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
ASSERT_EQ(targets.size(), 2);
@@ -175,6 +179,10 @@
EXPECT_EQ(targets[1].spec.sector_start, 1);
EXPECT_EQ(targets[1].spec.length, 1);
+ // Test GetTargetType().
+ EXPECT_EQ(DeviceMapper::GetTargetType(targets[0].spec), std::string{"linear"});
+ EXPECT_EQ(DeviceMapper::GetTargetType(targets[1].spec), std::string{"linear"});
+
// Normally the TestDevice destructor would delete this, but at least one
// test should ensure that device deletion works.
ASSERT_TRUE(dev.Destroy());
@@ -201,3 +209,268 @@
"2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
EXPECT_EQ(target.GetParameterString(), expected);
}
+
+TEST(libdm, DmSnapshotArgs) {
+ DmTargetSnapshot target1(0, 512, "base", "cow", SnapshotStorageMode::Persistent, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ EXPECT_EQ(target1.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target1.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target1.name(), "snapshot");
+
+ DmTargetSnapshot target2(0, 512, "base", "cow", SnapshotStorageMode::Transient, 8);
+ EXPECT_EQ(target2.GetParameterString(), "base cow N 8");
+ EXPECT_EQ(target2.name(), "snapshot");
+
+ DmTargetSnapshot target3(0, 512, "base", "cow", SnapshotStorageMode::Merge, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot-merge")) {
+ EXPECT_EQ(target3.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target3.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target3.name(), "snapshot-merge");
+}
+
+TEST(libdm, DmSnapshotOriginArgs) {
+ DmTargetSnapshotOrigin target(0, 512, "base");
+ EXPECT_EQ(target.GetParameterString(), "base");
+ EXPECT_EQ(target.name(), "snapshot-origin");
+}
+
+class SnapshotTestHarness final {
+ public:
+ bool Setup();
+ bool Merge();
+
+ std::string origin_dev() const { return origin_dev_->path(); }
+ std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+ int base_fd() const { return base_fd_; }
+
+ static const uint64_t kBaseDeviceSize = 1024 * 1024;
+ static const uint64_t kCowDeviceSize = 1024 * 64;
+ static const uint64_t kSectorSize = 512;
+
+ private:
+ void SetupImpl();
+ void MergeImpl();
+
+ unique_fd base_fd_;
+ unique_fd cow_fd_;
+ unique_ptr<LoopDevice> base_loop_;
+ unique_ptr<LoopDevice> cow_loop_;
+ unique_ptr<TempDevice> origin_dev_;
+ unique_ptr<TempDevice> snapshot_dev_;
+ bool setup_ok_ = false;
+ bool merge_ok_ = false;
+};
+
+bool SnapshotTestHarness::Setup() {
+ SetupImpl();
+ return setup_ok_;
+}
+
+void SnapshotTestHarness::SetupImpl() {
+ base_fd_ = CreateTempFile("base_device", kBaseDeviceSize);
+ ASSERT_GE(base_fd_, 0);
+ cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
+ ASSERT_GE(cow_fd_, 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_);
+ ASSERT_TRUE(base_loop_->valid());
+ cow_loop_ = std::make_unique<LoopDevice>(cow_fd_);
+ ASSERT_TRUE(cow_loop_->valid());
+
+ DmTable origin_table;
+ ASSERT_TRUE(origin_table.AddTarget(make_unique<DmTargetSnapshotOrigin>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device())));
+ ASSERT_TRUE(origin_table.valid());
+
+ origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
+ ASSERT_TRUE(origin_dev_->valid());
+ ASSERT_FALSE(origin_dev_->path().empty());
+ ASSERT_TRUE(origin_dev_->WaitForUdev());
+
+ // chunk size = 4K blocks.
+ DmTable snap_table;
+ ASSERT_TRUE(snap_table.AddTarget(make_unique<DmTargetSnapshot>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device(), cow_loop_->device(),
+ SnapshotStorageMode::Persistent, 8)));
+ ASSERT_TRUE(snap_table.valid());
+
+ snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
+ ASSERT_TRUE(snapshot_dev_->valid());
+ ASSERT_FALSE(snapshot_dev_->path().empty());
+ ASSERT_TRUE(snapshot_dev_->WaitForUdev());
+
+ setup_ok_ = true;
+}
+
+bool SnapshotTestHarness::Merge() {
+ MergeImpl();
+ return merge_ok_;
+}
+
+void SnapshotTestHarness::MergeImpl() {
+ DmTable merge_table;
+ ASSERT_TRUE(merge_table.AddTarget(
+ make_unique<DmTargetSnapshot>(0, kBaseDeviceSize / kSectorSize, base_loop_->device(),
+ cow_loop_->device(), SnapshotStorageMode::Merge, 8)));
+ ASSERT_TRUE(merge_table.valid());
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.LoadTableAndActivate("libdm-test-dm-snapshot", merge_table));
+
+ while (true) {
+ vector<DeviceMapper::TargetInfo> status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &status));
+ ASSERT_EQ(status.size(), 1);
+ ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+ 0);
+
+ DmTargetSnapshot::Status merge_status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+ ASSERT_TRUE(merge_status.error.empty());
+ if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+ break;
+ }
+
+ std::this_thread::sleep_for(250ms);
+ }
+
+ merge_ok_ = true;
+}
+
+bool CheckSnapshotAvailability() {
+ DmTargetTypeInfo info;
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.GetTargetByName("snapshot", &info)) {
+ cout << "snapshot module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-merge", &info)) {
+ cout << "snapshot-merge module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-origin", &info)) {
+ cout << "snapshot-origin module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+TEST(libdm, DmSnapshot) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd origin_fd(open(harness.origin_dev().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(origin_fd, 0);
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Write to the first block of the snapshot device.
+ std::string data("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+ ASSERT_TRUE(android::base::WriteFully(snapshot_fd, data.data(), data.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+
+ // We should get the same data back from the snapshot device.
+ std::string read(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(snapshot_fd, read.data(), read.size()));
+ ASSERT_EQ(read, data);
+
+ // We should see the original data from the origin device.
+ std::string zeroes(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(origin_fd, read.data(), read.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // We should also see the original data from the base device.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // Now, perform the merge and wait.
+ ASSERT_TRUE(harness.Merge());
+
+ // Reading from the base device should give us the modified data.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, data);
+}
+
+TEST(libdm, DmSnapshotOverflow) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Fill the copy-on-write device until it overflows.
+ uint64_t bytes_remaining = SnapshotTestHarness::kCowDeviceSize;
+ uint8_t byte = 1;
+ while (bytes_remaining) {
+ std::string data(4096, char(byte));
+ if (!android::base::WriteFully(snapshot_fd, data.data(), data.size())) {
+ ASSERT_EQ(errno, EIO);
+ break;
+ }
+ bytes_remaining -= data.size();
+ }
+
+ // If writes succeed (because they are buffered), then we should expect an
+ // fsync to fail with EIO.
+ if (!bytes_remaining) {
+ ASSERT_EQ(fsync(snapshot_fd), -1);
+ ASSERT_EQ(errno, EIO);
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+
+ vector<DeviceMapper::TargetInfo> target_status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &target_status));
+ ASSERT_EQ(target_status.size(), 1);
+ ASSERT_EQ(strncmp(target_status[0].spec.target_type, "snapshot", strlen("snapshot")), 0);
+
+ DmTargetSnapshot::Status status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(target_status[0].data, &status));
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ ASSERT_EQ(status.error, "Overflow");
+ } else {
+ ASSERT_EQ(status.error, "Invalid");
+ }
+}
+
+TEST(libdm, CryptArgs) {
+ DmTargetCrypt target1(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
+ ASSERT_EQ(target1.name(), "crypt");
+ ASSERT_TRUE(target1.Valid());
+ ASSERT_EQ(target1.GetParameterString(), "sha1 abcdefgh 50 /dev/loop0 100");
+
+ DmTargetCrypt target2(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
+ target2.SetSectorSize(64);
+ target2.AllowDiscards();
+ target2.SetIvLargeSectors();
+ target2.AllowEncryptOverride();
+ ASSERT_EQ(target2.GetParameterString(),
+ "sha1 abcdefgh 50 /dev/loop0 100 4 allow_discards allow_encrypt_override "
+ "iv_large_sectors sector_size:64");
+}
+
+TEST(libdm, DefaultKeyArgs) {
+ DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+ ASSERT_EQ(target.name(), "default-key");
+ ASSERT_TRUE(target.Valid());
+ ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 28e6e01..afcb090 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <linux/dm-ioctl.h>
#include <linux/kdev_t.h>
+#include <linux/types.h>
#include <stdint.h>
#include <sys/sysmacros.h>
#include <unistd.h>
@@ -96,6 +97,10 @@
// successfully read and stored in 'targets'. Returns 'false' otherwise.
bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
+ // Finds a target by name and returns its information if found. |info| may
+ // be null to check for the existence of a target.
+ bool GetTargetByName(const std::string& name, DmTargetTypeInfo* info);
+
// Return 'true' if it can successfully read the list of device mapper block devices
// currently created. 'devices' will be empty if the kernel interactions
// were successful and there are no block devices at the moment. Returns
@@ -107,6 +112,13 @@
// parameter is not set.
bool GetDmDevicePathByName(const std::string& name, std::string* path);
+ // Returns the dev_t for the named device-mapper node.
+ bool GetDeviceNumber(const std::string& name, dev_t* dev);
+
+ // Returns a major:minor string for the named device-mapper node, that can
+ // be used as inputs to DmTargets that take a block device.
+ bool GetDeviceString(const std::string& name, std::string* dev);
+
// The only way to create a DeviceMapper object.
static DeviceMapper& Instance();
@@ -132,6 +144,8 @@
// mapper device from the kernel.
bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+ static std::string GetTargetType(const struct dm_target_spec& spec);
+
private:
// Maximum possible device mapper targets registered in the kernel.
// This is only used to read the list of targets from kernel so we allocate
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
index 5c639be..ee66653 100644
--- a/fs_mgr/libdm/include/libdm/dm_table.h
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -43,12 +43,20 @@
// successfully removed.
bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+ // Adds a target, constructing it in-place for convenience. For example,
+ //
+ // table.Emplace<DmTargetZero>(0, num_sectors);
+ template <typename T, typename... Args>
+ bool Emplace(Args&&... args) {
+ return AddTarget(std::make_unique<T>(std::forward<Args>(args)...));
+ }
+
// Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
// and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
// table is malformed.
bool valid() const;
- // Returns the toatl number of targets.
+ // Returns the total number of targets.
size_t num_targets() const { return targets_.size(); }
// Returns the total size represented by the table in terms of number of 512-byte sectors.
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 175b0f0..722922d 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -40,6 +40,18 @@
return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
}
+ uint32_t major_version() const { return major_; }
+ uint32_t minor_version() const { return minor_; }
+ uint32_t patch_level() const { return patch_; }
+
+ bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch) const {
+ if (major_ > major) return true;
+ if (major_ < major) return false;
+ if (minor_ > minor) return true;
+ if (minor_ < minor) return false;
+ return patch_ >= patch;
+ }
+
private:
std::string name_;
uint32_t major_;
@@ -170,6 +182,119 @@
std::string target_string_;
};
+enum class SnapshotStorageMode {
+ // The snapshot will be persisted to the COW device.
+ Persistent,
+ // The snapshot will be lost on reboot.
+ Transient,
+ // The snapshot will be merged from the COW device into the base device,
+ // in the background.
+ Merge
+};
+
+// Writes to a snapshot device will be written to the given COW device. Reads
+// will read from the COW device or base device. The chunk size is specified
+// in sectors.
+class DmTargetSnapshot final : public DmTarget {
+ public:
+ DmTargetSnapshot(uint64_t start, uint64_t length, const std::string& base_device,
+ const std::string& cow_device, SnapshotStorageMode mode, uint64_t chunk_size)
+ : DmTarget(start, length),
+ base_device_(base_device),
+ cow_device_(cow_device),
+ mode_(mode),
+ chunk_size_(chunk_size) {}
+
+ std::string name() const override;
+ std::string GetParameterString() const override;
+ bool Valid() const override { return true; }
+
+ struct Status {
+ uint64_t sectors_allocated;
+ uint64_t total_sectors;
+ uint64_t metadata_sectors;
+ std::string error;
+ };
+
+ static bool ParseStatusText(const std::string& text, Status* status);
+ static bool ReportsOverflow(const std::string& target_type);
+
+ private:
+ std::string base_device_;
+ std::string cow_device_;
+ SnapshotStorageMode mode_;
+ uint64_t chunk_size_;
+};
+
+// snapshot-origin will read/write directly to the backing device, updating any
+// snapshot devices with a matching origin.
+class DmTargetSnapshotOrigin final : public DmTarget {
+ public:
+ DmTargetSnapshotOrigin(uint64_t start, uint64_t length, const std::string& device)
+ : DmTarget(start, length), device_(device) {}
+
+ std::string name() const override { return "snapshot-origin"; }
+ std::string GetParameterString() const override { return device_; }
+ bool Valid() const override { return true; }
+
+ private:
+ std::string device_;
+};
+
+class DmTargetCrypt final : public DmTarget {
+ public:
+ DmTargetCrypt(uint64_t start, uint64_t length, const std::string& cipher,
+ const std::string& key, uint64_t iv_sector_offset, const std::string& device,
+ uint64_t device_sector)
+ : DmTarget(start, length),
+ cipher_(cipher),
+ key_(key),
+ iv_sector_offset_(iv_sector_offset),
+ device_(device),
+ device_sector_(device_sector) {}
+
+ void AllowDiscards() { allow_discards_ = true; }
+ void AllowEncryptOverride() { allow_encrypt_override_ = true; }
+ void SetIvLargeSectors() { iv_large_sectors_ = true; }
+ void SetSectorSize(uint32_t sector_size) { sector_size_ = sector_size; }
+
+ std::string name() const override { return "crypt"; }
+ bool Valid() const override { return true; }
+ std::string GetParameterString() const override;
+
+ private:
+ std::string cipher_;
+ std::string key_;
+ uint64_t iv_sector_offset_;
+ std::string device_;
+ uint64_t device_sector_;
+ bool allow_discards_ = false;
+ bool allow_encrypt_override_ = false;
+ bool iv_large_sectors_ = false;
+ uint32_t sector_size_ = 0;
+};
+
+class DmTargetDefaultKey final : public DmTarget {
+ public:
+ DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
+ const std::string& key, const std::string& blockdev, uint64_t start_sector)
+ : DmTarget(start, length),
+ cipher_(cipher),
+ key_(key),
+ blockdev_(blockdev),
+ start_sector_(start_sector) {}
+
+ std::string name() const override { return "default-key"; }
+ bool Valid() const override { return true; }
+ std::string GetParameterString() const override;
+
+ private:
+ std::string cipher_;
+ std::string key_;
+ std::string blockdev_;
+ uint64_t start_sector_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index e6e83f4..6b4c2d8 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -35,6 +35,9 @@
// Detach the loop device given by 'loopdev' from the attached backing file.
bool Detach(const std::string& loopdev) const;
+ // Enable Direct I/O on a loop device. This requires kernel 4.9+.
+ static bool EnableDirectIo(int fd);
+
LoopControl(const LoopControl&) = delete;
LoopControl& operator=(const LoopControl&) = delete;
LoopControl& operator=(LoopControl&&) = default;
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
index 0beb1a6..16bf4b0 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -91,6 +91,27 @@
return true;
}
+bool LoopControl::EnableDirectIo(int fd) {
+#if !defined(LOOP_SET_BLOCK_SIZE)
+ static constexpr int LOOP_SET_BLOCK_SIZE = 0x4C09;
+#endif
+#if !defined(LOOP_SET_DIRECT_IO)
+ static constexpr int LOOP_SET_DIRECT_IO = 0x4C08;
+#endif
+
+ // Note: the block size has to be >= the logical block size of the underlying
+ // block device, *not* the filesystem block size.
+ if (ioctl(fd, LOOP_SET_BLOCK_SIZE, 4096)) {
+ PLOG(ERROR) << "Could not set loop device block size";
+ return false;
+ }
+ if (ioctl(fd, LOOP_SET_DIRECT_IO, 1)) {
+ PLOG(ERROR) << "Could not set loop direct IO";
+ return false;
+ }
+ return true;
+}
+
LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
Init();
}
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
index 32fc3d2..ed209aa 100644
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -30,6 +30,7 @@
],
static_libs: [
+ "libdm",
"libext4_utils",
],
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index e3803d5..0a3ba6c 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -37,10 +37,13 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
namespace android {
namespace fiemap_writer {
+using namespace android::dm;
+
// We are expecting no more than 512 extents in a fiemap of the file we create.
// If we find more, then it is treated as error for now.
static constexpr const uint32_t kMaxExtents = 512;
@@ -86,15 +89,75 @@
return true;
}
-static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
- // TODO: Stop popping the device mapper stack if dm-linear target is found
- if (!::android::base::StartsWith(bdev, "dm-")) {
- // We are at the bottom of the device mapper stack.
- *bdev_raw = bdev;
+static bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {
+ const auto& entry = target.spec;
+ if (entry.sector_start != 0) {
+ LOG(INFO) << "Stopping at target with non-zero starting sector";
+ return false;
+ }
+
+ auto target_type = DeviceMapper::GetTargetType(entry);
+ if (target_type == "bow" || target_type == "default-key" || target_type == "crypt") {
+ return true;
+ }
+ if (target_type == "linear") {
+ auto pieces = android::base::Split(target.data, " ");
+ if (pieces[1] != "0") {
+ LOG(INFO) << "Stopping at complex linear target with non-zero starting sector: "
+ << pieces[1];
+ return false;
+ }
return true;
}
- std::string dm_leaf_dir = ::android::base::StringPrintf("/sys/block/%s/slaves", bdev.c_str());
+ LOG(INFO) << "Stopping at complex target type " << target_type;
+ return false;
+}
+
+static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
+ *bdev_raw = bdev;
+
+ if (!::android::base::StartsWith(bdev, "dm-")) {
+ // We are at the bottom of the device mapper stack.
+ return true;
+ }
+
+ // Get the device name.
+ auto dm_name_file = "/sys/block/" + bdev + "/dm/name";
+ std::string dm_name;
+ if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
+ PLOG(ERROR) << "Could not read file: " << dm_name_file;
+ return false;
+ }
+ dm_name = android::base::Trim(dm_name);
+
+ auto& dm = DeviceMapper::Instance();
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (!dm.GetTableInfo(dm_name, &table)) {
+ LOG(ERROR) << "Could not read device-mapper table for " << dm_name << " at " << bdev;
+ return false;
+ }
+
+ // The purpose of libfiemap_writer is to provide an extent-based view into
+ // a file. This is difficult if devices are not layered in a 1:1 manner;
+ // we would have to translate and break up extents based on the actual
+ // block mapping. Since this is too complex, we simply stop processing
+ // the device-mapper stack if we encounter a complex case.
+ //
+ // It is up to the caller to decide whether stopping at a virtual block
+ // device is allowable. In most cases it is not, because we want either
+ // "userdata" or an external volume. It is useful for tests however.
+ // Callers can check by comparing the device number to that of userdata,
+ // or by checking whether is a device-mapper node.
+ if (table.size() > 1) {
+ LOG(INFO) << "Stopping at complex table for " << dm_name << " at " << bdev;
+ return true;
+ }
+ if (!ValidateDmTarget(table[0])) {
+ return true;
+ }
+
+ auto dm_leaf_dir = "/sys/block/" + bdev + "/slaves";
auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
if (d == nullptr) {
PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
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 9486122..ee79262 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -60,10 +60,17 @@
// FiemapWriter::Open).
static bool HasPinnedExtents(const std::string& file_path);
- // 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.
+ // Returns the underlying block device of a file. This will look past device-mapper layers
+ // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-
+ // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,
+ // it will be returned in place of any physical block device.
+ //
+ // It is the caller's responsibility to check whether the returned block device is acceptable.
+ // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.
+ // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major
+ // number against a boot device.
+ //
+ // If device-mapper nodes were 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);
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 27222af..25a042f 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -214,7 +214,7 @@
sABOverrideValue = ab_device;
}
-MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false), ignore_slot_suffixing_(false) {
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
memset(&geometry_, 0, sizeof(geometry_));
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
geometry_.struct_size = sizeof(geometry_);
@@ -443,11 +443,6 @@
LERROR << "Could not find partition group: " << group_name;
return nullptr;
}
- if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" && !ignore_slot_suffixing_ &&
- GetPartitionSlotSuffix(name).empty()) {
- LERROR << "Unsuffixed partition not allowed on A/B device: " << name;
- return nullptr;
- }
partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
return partitions_.back().get();
}
@@ -1049,15 +1044,11 @@
auto_slot_suffixing_ = true;
}
-void MetadataBuilder::IgnoreSlotSuffixing() {
- ignore_slot_suffixing_ = true;
-}
-
bool MetadataBuilder::IsABDevice() const {
if (sABOverrideSet) {
return sABOverrideValue;
}
- return android::base::GetBoolProperty("ro.build.ab_update", false);
+ return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
}
bool MetadataBuilder::IsRetrofitDevice() const {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 46bfe92..34c68d4 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -106,6 +106,13 @@
EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
EXPECT_EQ(extent->physical_sector(), 32);
+ auto exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_EQ(FindPartition(*exported.get(), "not found"), nullptr);
+ auto entry = FindPartition(*exported.get(), "system");
+ ASSERT_NE(entry, nullptr);
+ ASSERT_EQ(GetPartitionSize(*exported.get(), *entry), 32768);
+
// Test shrinking to 0.
builder->ResizePartition(system, 0);
EXPECT_EQ(system->size(), 0);
@@ -765,15 +772,6 @@
EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
}
-TEST_F(BuilderTest, UnsuffixedPartitions) {
- MetadataBuilder::OverrideABForTesting(true);
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- ASSERT_NE(builder, nullptr);
-
- ASSERT_EQ(builder->AddPartition("system", 0), nullptr);
- ASSERT_NE(builder->AddPartition("system_a", 0), nullptr);
-}
-
TEST_F(BuilderTest, ABExtents) {
BlockDeviceInfo device_info("super", 10_GiB, 768 * 1024, 0, 4096);
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index db27022..58a88b5 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -89,8 +89,8 @@
return true;
}
-bool WriteToImageFile(const char* file, const LpMetadata& input) {
- unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
+bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
return false;
@@ -149,8 +149,8 @@
return device_images_.size() == metadata_.block_devices.size();
}
-bool ImageBuilder::Export(const char* file) {
- unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
+bool ImageBuilder::Export(const std::string& file) {
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << "open failed: " << file;
return false;
@@ -438,7 +438,7 @@
return temp_fds_.back().get();
}
-bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images, bool sparsify) {
ImageBuilder builder(metadata, block_size, images, sparsify);
return builder.IsValid() && builder.Build() && builder.Export(file);
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 75060f9..a284d2e 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -41,7 +41,7 @@
const std::map<std::string, std::string>& images, bool sparsify);
bool Build();
- bool Export(const char* file);
+ bool Export(const std::string& file);
bool ExportFiles(const std::string& dir);
bool IsValid() const;
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index c706f2a..e70c552 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -345,7 +345,6 @@
std::vector<std::unique_ptr<PartitionGroup>> groups_;
std::vector<LpMetadataBlockDevice> block_devices_;
bool auto_slot_suffixing_;
- bool ignore_slot_suffixing_;
};
// Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 5f782b0..135a1b3 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -72,9 +72,9 @@
// Read/Write logical partition metadata to an image file, for diagnostics or
// flashing.
-bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images, bool sparsify);
-bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
@@ -107,6 +107,10 @@
std::string SlotSuffixForSlotNumber(uint32_t slot_number);
std::string GetPartitionSlotSuffix(const std::string& partition_name);
+// Helpers for common functions.
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name);
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index bb8ec9c..cc4a882 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include "utility.h"
@@ -37,7 +38,7 @@
namespace {
std::string GetPartitionAbsolutePath(const std::string& path) {
- if (path[0] == '/') {
+ if (android::base::StartsWith(path, "/")) {
return path;
}
return "/dev/block/by-name/" + path;
@@ -63,6 +64,12 @@
PERROR << __PRETTY_FUNCTION__ << "BLKALIGNOFF failed on " << block_device;
return false;
}
+ // The kernel can return -1 here when misaligned devices are stacked (i.e.
+ // device-mapper).
+ if (alignment_offset == -1) {
+ alignment_offset = 0;
+ }
+
int logical_block_size;
if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed on " << block_device;
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 72a3c57..338b525 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -135,6 +135,24 @@
return list;
}
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name) {
+ for (const auto& partition : metadata.partitions) {
+ if (GetPartitionName(partition) == name) {
+ return &partition;
+ }
+ }
+ return nullptr;
+}
+
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
+ uint64_t total_size = 0;
+ for (uint32_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = metadata.extents[partition.first_extent_index + i];
+ total_size += extent.num_sectors * LP_SECTOR_SIZE;
+ }
+ return total_size;
+}
+
std::string GetPartitionSlotSuffix(const std::string& partition_name) {
if (partition_name.size() <= 2) {
return "";
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index eb9f525..83668e9 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -25,6 +25,7 @@
"libfstab",
],
srcs: [
+ "file_wait_test.cpp",
"fs_mgr_test.cpp",
],
diff --git a/fs_mgr/tests/adb-remount-sh.xml b/fs_mgr/tests/adb-remount-sh.xml
index 716e324..fa0d63f 100644
--- a/fs_mgr/tests/adb-remount-sh.xml
+++ b/fs_mgr/tests/adb-remount-sh.xml
@@ -18,6 +18,8 @@
<!-- This test requires a device, so it's not annotated with a null-device -->
<test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
<option name="binary" value="adb-remount-test.sh" />
+ <!-- Increase default timeout as script is quite long -->
+ <option name="per-binary-timeout" value="1h" />
</test>
</configuration>
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index c22176b..c2a0f33 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -33,6 +33,7 @@
## Helper Variables
##
+EMPTY=""
SPACE=" "
# A _real_ embedded tab character
TAB="`echo | tr '\n' '\t'`"
@@ -50,6 +51,9 @@
start_time=`date +%s`
ACTIVE_SLOT=
+ADB_WAIT=4m
+FASTBOOT_WAIT=2m
+
##
## Helper Functions
##
@@ -131,10 +135,30 @@
adb_logcat() {
echo "${RED}[ INFO ]${NORMAL} logcat ${@}" >&2 &&
adb logcat "${@}" </dev/null |
+ tr -d '\r' |
grep -v 'logd : logdr: UID=' |
sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
}
+[ "USAGE: avc_check >/dev/stderr
+
+Returns: worrisome avc violations" ]
+avc_check() {
+ if ! ${overlayfs_supported:-false}; then
+ return
+ fi
+ local L=`adb_logcat -b all -v brief -d \
+ -e 'context=u:object_r:unlabeled:s0' 2>/dev/null |
+ sed -n 's/.*avc: //p' |
+ sort -u`
+ if [ -z "${L}" ]; then
+ return
+ fi
+ echo "${ORANGE}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+ echo "${L}" |
+ sed 's/^/ /' >&2
+}
+
[ "USAGE: get_property <prop>
Returns the property value" ]
@@ -173,7 +197,8 @@
Returns: true if the reboot command succeeded" ]
adb_reboot() {
- adb reboot remount-test || true
+ avc_check
+ adb reboot remount-test </dev/null || true
sleep 2
}
@@ -219,13 +244,34 @@
echo ${hours}:`expr ${minutes} / 10``expr ${minutes} % 10`:`expr ${seconds} / 10``expr ${seconds} % 10`
}
+[ "USAGE: USB_DEVICE=\`usb_devnum [--next]\`
+
+USB_DEVICE contains cache. Update if system changes.
+
+Returns: the devnum for the USB_SERIAL device" ]
+usb_devnum() {
+ if [ -n "${USB_SERIAL}" ]; then
+ local usb_device=`cat ${USB_SERIAL%/serial}/devnum 2>/dev/null | tr -d ' \t\r\n'`
+ if [ -n "${usb_device}" ]; then
+ USB_DEVICE=dev${usb_device}
+ elif [ -n "${USB_DEVICE}" -a "${1}" ]; then
+ USB_DEVICE=dev`expr ${USB_DEVICE#dev} + 1`
+ fi
+ echo "${USB_DEVICE}"
+ fi
+}
+
[ "USAGE: adb_wait [timeout]
Returns: waits until the device has returned for adb or optional timeout" ]
adb_wait() {
+ local start=`date +%s`
+ local duration=
local ret
if [ -n "${1}" ]; then
- echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
+ USB_DEVICE=`usb_devnum --next`
+ duration=`format_duration ${1}`
+ echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
ret=${?}
echo -n " ${CR}"
@@ -233,30 +279,83 @@
adb wait-for-device
ret=${?}
fi
+ USB_DEVICE=`usb_devnum`
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
+ local end=`date +%s`
+ local diff_time=`expr ${end} - ${start}`
+ local _print_time=${print_time}
+ if [ ${diff_time} -lt 15 ]; then
+ _print_time=false
+ fi
+ diff_time=`format_duration ${diff_time}`
+ if [ "${diff_time}" = "${duration}" ]; then
+ _print_time=false
+ fi
+
+ local reason=
+ if inAdb; then
+ reason=`get_property ro.boot.bootreason`
+ fi
+ case ${reason} in
+ reboot*)
+ reason=
+ ;;
+ ${EMPTY})
+ ;;
+ *)
+ reason=" for boot reason ${reason}"
+ ;;
+ esac
+ if ${_print_time} || [ -n "${reason}" ]; then
+ echo "${BLUE}[ INFO ]${NORMAL} adb wait duration ${diff_time}${reason}"
+ fi >&2
+
return ${ret}
}
-[ "USAGE: usb_status > stdout
+[ "USAGE: adb_user > /dev/stdout
-If adb_wait failed, check if device is in adb, recovery or fastboot mode
-and report status string.
+Returns: the adb daemon user" ]
+adb_user() {
+ adb_sh echo '${USER}' </dev/null
+}
-Returns: \"(USB stack borken?)\", \"(In fastboot mode)\" or \"(in adb mode)\"" ]
+[ "USAGE: usb_status > stdout 2> stderr
+
+Assumes referenced right after adb_wait or fastboot_wait failued.
+If wait failed, check if device is in adb, recovery or fastboot mode
+and report status strings like \"(USB stack borken?)\",
+\"(In fastboot mode)\", \"(In recovery mode)\" or \"(in adb mode)\".
+Additional diagnostics may be provided to the stderr output.
+
+Returns: USB status string" ]
usb_status() {
if inFastboot; then
echo "(In fastboot mode)"
elif inRecovery; then
echo "(In recovery mode)"
elif inAdb; then
- echo "(In adb mode)"
+ echo "(In adb mode `adb_user`)"
else
- echo "(USB stack borken?)"
+ echo "(USB stack borken for ${USB_ADDRESS})"
+ USB_DEVICE=`usb_devnum`
+ if [ -n "${USB_DEVICE}" ]; then
+ echo "# lsusb -v -s ${USB_DEVICE#dev}"
+ local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
+ if [ -n "${D}" ]; then
+ echo "${D}"
+ else
+ lsusb -v
+ fi
+ else
+ echo "# lsusb -v (expected device missing)"
+ lsusb -v
+ fi >&2
fi
}
@@ -268,7 +367,8 @@
# 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}"
+ USB_DEVICE=`usb_devnum --next`
+ echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
ret=${?}
echo -n " ${CR}"
@@ -278,11 +378,12 @@
fi ||
inFastboot
ret=${?}
+ USB_DEVICE=`usb_devnum`
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
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ fi >&2
fi
return ${ret}
}
@@ -293,7 +394,8 @@
recovery_wait() {
local ret
if [ -n "${1}" ]; then
- echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
+ USB_DEVICE=`usb_devnum --next`
+ echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
ret=${?}
echo -n " ${CR}"
@@ -301,11 +403,12 @@
adb wait-for-recovery
ret=${?}
fi
+ USB_DEVICE=`usb_devnum`
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
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ fi >&2
fi
return ${ret}
}
@@ -327,17 +430,68 @@
inFastboot || inAdb || inRecovery
}
+wait_for_screen_timeout=900
+[ "USAGE: wait_for_screen [-n] [TIMEOUT]
+
+-n - echo newline at exit
+TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
+wait_for_screen() {
+ exit_function=true
+ if [ X"-n" = X"${1}" ]; then
+ exit_function=echo
+ shift
+ fi
+ timeout=${wait_for_screen_timeout}
+ if [ ${#} -gt 0 ]; then
+ timeout=${1}
+ shift
+ fi
+ counter=0
+ while true; do
+ if inFastboot; then
+ fastboot reboot
+ elif inAdb; then
+ if [ 0 != ${counter} ]; then
+ adb_wait
+ fi
+ if [ -n "`get_property sys.boot.reason`" ]
+ then
+ vals=`get_property |
+ sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+ if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
+ then
+ sleep 1
+ break
+ fi
+ if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
+ then
+ sleep 1
+ break
+ fi
+ fi
+ fi
+ counter=`expr ${counter} + 1`
+ if [ ${counter} -gt ${timeout} ]; then
+ ${exit_function}
+ echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+ return 1
+ fi
+ sleep 1
+ done
+ ${exit_function}
+}
+
[ "USAGE: adb_root
NB: This can be flakey on devices due to USB state
Returns: true if device in root state" ]
adb_root() {
- [ root != "`adb_sh echo '${USER}' </dev/null`" ] || return 0
+ [ root != "`adb_user`" ] || return 0
adb root >/dev/null </dev/null 2>/dev/null
sleep 2
- adb_wait 2m &&
- [ root = "`adb_sh echo '${USER}' </dev/null`" ]
+ adb_wait ${ADB_WAIT} &&
+ [ root = "`adb_user`" ]
}
[ "USAGE: adb_unroot
@@ -346,11 +500,11 @@
Returns: true if device in un root state" ]
adb_unroot() {
- [ root = "`adb_sh echo '${USER}' </dev/null`" ] || return 0
+ [ root = "`adb_user`" ] || return 0
adb unroot >/dev/null </dev/null 2>/dev/null
sleep 2
- adb_wait 2m &&
- [ root != "`adb_sh echo '${USER}' </dev/null`" ]
+ adb_wait ${ADB_WAIT} &&
+ [ root != "`adb_user`" ]
}
[ "USAGE: fastboot_getvar var expected >/dev/stderr
@@ -370,10 +524,10 @@
O="${1}: <empty>"
fi
if [ -n "${2}" -a "${1}: ${2}" != "${O}" ]; then
- echo "${2} != ${O}" >&2
+ echo "${2} != ${O}"
false
return
- fi
+ fi >&2
echo ${O} >&2
}
@@ -430,16 +584,16 @@
Returns: exit failure, report status" ]
die() {
if [ X"-d" = X"${1}" ]; then
- adb_logcat -b all -v nsec -d >&2
+ adb_logcat -b all -v nsec -d
shift
elif [ X"-t" = X"${1}" ]; then
if [ -n "${2}" ]; then
- adb_logcat -b all -v nsec -t ${2} >&2
+ adb_logcat -b all -v nsec -t ${2}
else
- adb_logcat -b all -v nsec -d >&2
+ adb_logcat -b all -v nsec -d
fi
shift 2
- fi
+ fi >&2
echo "${RED}[ FAILED ]${NORMAL} ${@}" >&2
cleanup
restore
@@ -464,39 +618,63 @@
if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
*}" ]; then
- echo "${prefix} expected \"${lval}\"" >&2
+ echo "${prefix} expected \"${lval}\""
echo "${prefix} got \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
- t again' >&2
+ t again'
if [ -n "${*}" ] ; then
- echo "${prefix} ${*}" >&2
+ echo "${prefix} ${*}"
fi
else
- echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}" >&2
- fi
+ echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}"
+ fi >&2
return ${error}
fi
if [ -n "${*}" ] ; then
prefix="${GREEN}[ INFO ]${NORMAL}"
if [ X"${lval}" != X"${rval}" ]; then # we were supplied a regex?
if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
- echo "${prefix} ok \"${lval}\"" >&2
+ echo "${prefix} ok \"${lval}\""
echo " = \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
- t again' >&2
+ t again'
if [ -n "${*}" ] ; then
- echo "${prefix} ${*}" >&2
+ echo "${prefix} ${*}"
fi
else
- echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}" >&2
+ echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}"
fi
else
- echo "${prefix} ok \"${lval}\" ${*}" >&2
- fi
+ echo "${prefix} ok \"${lval}\" ${*}"
+ fi >&2
+ fi
+ return 0
+}
+
+[ "USAGE: EXPECT_NE <lval> <rval> [--warning [message]]
+
+Returns true if lval matches rval" ]
+EXPECT_NE() {
+ local lval="${1}"
+ local rval="${2}"
+ shift 2
+ local error=1
+ local prefix="${RED}[ ERROR ]${NORMAL}"
+ if [ X"${1}" = X"--warning" ]; then
+ prefix="${RED}[ WARNING ]${NORMAL}"
+ error=0
+ shift 1
+ fi
+ if [ X"${rval}" = X"${lval}" ]; then
+ echo "${prefix} did not expect \"${lval}\" ${*}" >&2
+ return ${error}
+ fi
+ if [ -n "${*}" ] ; then
+ echo "${prefix} ok \"${lval}\" not \"${rval}\" ${*}" >&2
fi
return 0
}
@@ -516,6 +694,21 @@
die "${@}"
}
+[ "USAGE: check_ne <lval> <rval> [--warning [message]]
+
+Exits if lval matches rval" ]
+check_ne() {
+ local lval="${1}"
+ local rval="${2}"
+ shift 2
+ if [ X"${1}" = X"--warning" ]; then
+ EXPECT_NE "${lval}" "${rval}" ${*}
+ return
+ fi
+ EXPECT_NE "${lval}" "${rval}" ||
+ die "${@}"
+}
+
[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
@@ -606,7 +799,7 @@
inRecovery && die "device in recovery mode"
if ! inAdb; then
echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" >&2
- adb_wait 2m
+ adb_wait ${ADB_WAIT}
fi
inAdb || die "specified device not in adb mode"
isDebuggable || die "device not a debug build"
@@ -631,7 +824,8 @@
USB_ADDRESS=usb${USB_ADDRESS##*/}
fi
[ -z "${ANDROID_SERIAL}${USB_ADDRESS}" ] ||
- echo "${BLUE}[ INFO ]${NORMAL}" ${ANDROID_SERIAL} ${USB_ADDRESS} >&2
+ USB_DEVICE=`usb_devnum`
+ echo "${BLUE}[ INFO ]${NORMAL}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} >&2
BUILD_DESCRIPTION=`get_property ro.build.description`
[ -z "${BUILD_DESCRIPTION}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
@@ -657,6 +851,8 @@
esac
done
+# If reboot too soon after fresh flash, could trip device update failure logic
+wait_for_screen
# Can we test remount -R command?
overlayfs_supported=true
if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
@@ -665,19 +861,20 @@
${overlayfs_supported} || return 0
inFastboot &&
fastboot reboot &&
- adb_wait 2m
+ adb_wait ${ADB_WAIT}
inAdb &&
adb_root &&
adb enable-verity >/dev/null 2>/dev/null &&
adb_reboot &&
- adb_wait 2m
+ adb_wait ${ADB_WAIT}
}
echo "${GREEN}[ RUN ]${NORMAL} Testing adb shell su root remount -R command" >&2
+ avc_check
adb_su remount -R system </dev/null || true
sleep 2
- adb_wait 2m ||
+ adb_wait ${ADB_WAIT} ||
die "waiting for device after remount -R `usb_status`"
if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
"2" = "`get_property partition.system.verified`" ]; then
@@ -735,7 +932,7 @@
if ${reboot}; then
echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
adb_reboot &&
- adb_wait 2m ||
+ adb_wait ${ADB_WAIT} ||
die "lost device after reboot after wipe `usb_status`"
adb_root ||
die "lost device after elevation to root after wipe `usb_status`"
@@ -766,6 +963,15 @@
echo "${D}"
if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
overlayfs_needed=false
+ # if device does not need overlays, then adb enable-verity will brick device
+ restore() {
+ ${overlayfs_supported} || return 0
+ inFastboot &&
+ fastboot reboot &&
+ adb_wait ${ADB_WAIT}
+ inAdb &&
+ adb_wait ${ADB_WAIT}
+ }
elif ! ${overlayfs_supported}; then
die "need overlayfs, but do not have it"
fi
@@ -800,7 +1006,7 @@
echo "${GREEN}[ INFO ]${NORMAL} rebooting as requested" >&2
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
adb_reboot &&
- adb_wait 2m ||
+ adb_wait ${ADB_WAIT} ||
die "lost device after reboot requested `usb_status`"
adb_root ||
die "lost device after elevation to root `usb_status`"
@@ -841,6 +1047,11 @@
echo "${GREEN}[ RUN ]${NORMAL} remount" >&2
+# Feed log with selinux denials as baseline before overlays
+adb_unroot
+adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_root
+
D=`adb remount 2>&1`
ret=${?}
echo "${D}"
@@ -941,6 +1152,26 @@
B="`adb_cat /vendor/hello`" ||
die "vendor hello"
check_eq "${A}" "${B}" /vendor before reboot
+SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
+VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
+SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
+VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
+BASE_SYSTEM_DEVT=`adb_sh stat --format=%D /system/bin/stat </dev/null`
+BASE_VENDOR_DEVT=`adb_sh stat --format=%D /vendor/bin/stat </dev/null`
+check_eq "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" vendor and system devt
+check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
+if ${overlayfs_needed}; then
+ check_ne "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
+ check_ne "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
+else
+ check_eq "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
+ check_eq "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
+fi
+check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
+[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
+ die "system devt ${SYSTEM_DEVT} is major 0"
+[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
+ die "vendor devt ${SYSTEM_DEVT} is major 0"
# Download libc.so, append some gargage, push back, and check if the file
# is updated.
@@ -961,8 +1192,16 @@
echo "${GREEN}[ RUN ]${NORMAL} reboot to confirm content persistent" >&2
+fixup_from_recovery() {
+ inRecovery || return 1
+ echo "${ORANGE}[ ERROR ]${NORMAL} Device in recovery" >&2
+ adb reboot </dev/null
+ adb_wait ${ADB_WAIT}
+}
+
adb_reboot &&
- adb_wait 2m ||
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_recovery ||
die "reboot after override content added failed `usb_status`"
if ${overlayfs_needed}; then
@@ -985,6 +1224,9 @@
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
+ # Feed unprivileged log with selinux denials as a result of overlays
+ wait_for_screen
+ adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
fi
B="`adb_cat /system/hello`"
check_eq "${A}" "${B}" /system after reboot
@@ -996,6 +1238,17 @@
check_eq "${A}" "${B}" vendor after reboot
echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2
+check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
+check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
+check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
+check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+
+# Feed log with selinux denials as a result of overlays
+adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+
# Check if the updated libc.so is persistent after reboot.
adb_root &&
adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
@@ -1025,10 +1278,17 @@
echo "${ORANGE}[ WARNING ]${NORMAL} wrong vendor image, skipping"
elif [ -z "${ANDROID_HOST_OUT}" ]; then
echo "${ORANGE}[ WARNING ]${NORMAL} please run lunch, skipping"
+elif ! (
+ adb_cat /vendor/build.prop |
+ cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
+ ) >/dev/null 2>/dev/null; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
else
- adb reboot fastboot ||
+ wait_for_screen
+ avc_check
+ adb reboot fastboot </dev/null ||
die "fastbootd not supported (wrong adb in path?)"
- any_wait 2m &&
+ any_wait ${ADB_WAIT} &&
inFastboot ||
die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
fastboot flash vendor ||
@@ -1069,8 +1329,9 @@
fastboot reboot ||
die "can not reboot out of fastboot"
echo "${ORANGE}[ WARNING ]${NORMAL} adb after fastboot"
- adb_wait 2m ||
- die "did not reboot after flash `usb_status`"
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_recovery ||
+ die "did not reboot after formatting ${scratch_partition} `usb_status`"
if ${overlayfs_needed}; then
adb_root &&
D=`adb_sh df -k </dev/null` &&
@@ -1101,8 +1362,15 @@
check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
--warning vendor content after flash vendor
fi
+
+ check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
+ check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+
fi
+wait_for_screen
echo "${GREEN}[ RUN ]${NORMAL} remove test content (cleanup)" >&2
T=`adb_date`
@@ -1114,7 +1382,7 @@
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`
adb_reboot &&
- adb_wait 2m &&
+ adb_wait ${ADB_WAIT} &&
adb_root ||
die "failed to reboot"
T=`adb_date`
@@ -1136,14 +1404,15 @@
echo "${GREEN}[ RUN ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
- adb reboot fastboot ||
+ avc_check
+ adb reboot fastboot </dev/null ||
die "Reboot into fastbootd"
img=${TMPDIR}/adb-remount-test-${$}.img
cleanup() {
rm ${img}
}
dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
- fastboot_wait 2m ||
+ fastboot_wait ${FASTBOOT_WAIT} ||
die "reboot into fastboot `usb_status`"
fastboot flash --force ${scratch_partition} ${img}
err=${?}
@@ -1155,9 +1424,9 @@
die "can not reboot out of fastboot"
[ 0 -eq ${err} ] ||
die "fastboot flash ${scratch_partition}"
- adb_wait 2m &&
+ adb_wait ${ADB_WAIT} &&
adb_root ||
- die "did not reboot after flash"
+ die "did not reboot after flashing empty ${scratch_partition} `usb_status`"
T=`adb_date`
D=`adb disable-verity 2>&1`
err=${?}
@@ -1165,7 +1434,7 @@
then
echo "${ORANGE}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
adb_reboot &&
- adb_wait 2m &&
+ adb_wait ${ADB_WAIT} &&
adb_root ||
die "failed to reboot"
T=`adb_date`
@@ -1191,9 +1460,25 @@
echo "${GREEN}[ RUN ]${NORMAL} test raw remount commands" >&2
+fixup_from_fastboot() {
+ inFastboot || return 1
+ if [ -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ else
+ echo "${ORANGE}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
+ fi >&2
+ fastboot --set-active=${ACTIVE_SLOT}
+ fi
+ fastboot reboot
+ adb_wait ${ADB_WAIT}
+}
+
# Prerequisite is a prepped device from above.
adb_reboot &&
- adb_wait 2m ||
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_fastboot ||
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"
@@ -1205,8 +1490,9 @@
# Prerequisite is a prepped device from above.
adb_reboot &&
- adb_wait 2m ||
- die "lost device after reboot to ro state (USB stack broken?)"
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_fastboot ||
+ 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 remount vendor </dev/null ||
@@ -1225,26 +1511,35 @@
die "/${d}/overlay wipe"
done
adb_reboot &&
- adb_wait 2m ||
- die "lost device after reboot after wipe (USB stack broken?)"
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_fastboot ||
+ die "lost device after reboot after wipe `usb_status`"
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_su df -k </dev/null | skip_unrelated_mounts
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 &&
+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 ! restore; then
+ restore() {
+ true
+ }
+ die "failed to restore verity after remount from scratch test"
+fi
-if [ ${err} = 0 ] && ${overlayfs_supported}; then
+err=0
+
+if ${overlayfs_supported}; then
echo "${GREEN}[ RUN ]${NORMAL} test 'adb remount -R'" >&2
+ avc_check
adb_root &&
adb remount -R &&
- adb_wait 2m ||
+ adb_wait ${ADB_WAIT} ||
die "adb remount -R"
if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
"2" = "`get_property partition.system.verified`" ]; then
@@ -1262,7 +1557,7 @@
}
[ ${err} = 0 ] ||
- die "failed to restore verity" >&2
+ die "failed to restore verity"
echo "${GREEN}[ PASSED ]${NORMAL} adb remount" >&2
diff --git a/fs_mgr/tests/file_wait_test.cpp b/fs_mgr/tests/file_wait_test.cpp
new file mode 100644
index 0000000..cc8b143
--- /dev/null
+++ b/fs_mgr/tests/file_wait_test.cpp
@@ -0,0 +1,91 @@
+// 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 <chrono>
+#include <string>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.h>
+#include <gtest/gtest.h>
+
+using namespace std::literals;
+using android::base::unique_fd;
+using android::fs_mgr::WaitForFile;
+using android::fs_mgr::WaitForFileDeleted;
+
+class FileWaitTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ test_file_ = temp_dir_.path + "/"s + tinfo->name();
+ }
+
+ void TearDown() override { unlink(test_file_.c_str()); }
+
+ TemporaryDir temp_dir_;
+ std::string test_file_;
+};
+
+TEST_F(FileWaitTest, FileExists) {
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ ASSERT_GE(fd, 0);
+
+ ASSERT_TRUE(WaitForFile(test_file_, 500ms));
+ ASSERT_FALSE(WaitForFileDeleted(test_file_, 500ms));
+}
+
+TEST_F(FileWaitTest, FileDoesNotExist) {
+ ASSERT_FALSE(WaitForFile(test_file_, 500ms));
+ ASSERT_TRUE(WaitForFileDeleted(test_file_, 500ms));
+}
+
+TEST_F(FileWaitTest, CreateAsync) {
+ std::thread thread([this] {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ });
+ EXPECT_TRUE(WaitForFile(test_file_, 3s));
+ thread.join();
+}
+
+TEST_F(FileWaitTest, CreateOtherAsync) {
+ std::thread thread([this] {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ });
+ EXPECT_FALSE(WaitForFile(test_file_ + ".wontexist", 2s));
+ thread.join();
+}
+
+TEST_F(FileWaitTest, DeleteAsync) {
+ // Note: need to close the file, otherwise inotify considers it not deleted.
+ {
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ ASSERT_GE(fd, 0);
+ }
+
+ std::thread thread([this] {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ unlink(test_file_.c_str());
+ });
+ EXPECT_TRUE(WaitForFileDeleted(test_file_, 3s));
+ thread.join();
+}
+
+TEST_F(FileWaitTest, BadPath) {
+ ASSERT_FALSE(WaitForFile("/this/path/does/not/exist", 5ms));
+ EXPECT_EQ(errno, ENOENT);
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 72afa69..6d87594 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -394,7 +394,7 @@
std::string fstab_contents = R"fs(
source none0 swap defaults encryptable,forceencrypt,fileencryption,forcefdeorfbe,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_loopback_path,zram_loopback_size,zram_backing_dev_path
-source none1 swap defaults encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,verify=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
+source none1 swap defaults encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
source none2 swap defaults forcefdeorfbe=
@@ -413,7 +413,6 @@
}
EXPECT_EQ("", entry->key_loc);
EXPECT_EQ("", entry->key_dir);
- EXPECT_EQ("", entry->verity_loc);
EXPECT_EQ(0, entry->length);
EXPECT_EQ("", entry->label);
EXPECT_EQ(-1, entry->partnum);
@@ -437,13 +436,11 @@
flags.crypt = true;
flags.force_crypt = true;
flags.file_encryption = true;
- flags.verify = true;
flags.avb = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
EXPECT_EQ("", entry->key_loc);
EXPECT_EQ("", entry->key_dir);
- EXPECT_EQ("", entry->verity_loc);
EXPECT_EQ(0, entry->length);
EXPECT_EQ("", entry->label);
EXPECT_EQ(-1, entry->partnum);
@@ -639,29 +636,6 @@
EXPECT_EQ(0, entry->zram_size);
}
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Verify) {
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- std::string fstab_contents = R"fs(
-source none0 swap defaults verify=/dir/key
-)fs";
-
- ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
- Fstab fstab;
- EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_EQ(1U, fstab.size());
-
- auto entry = fstab.begin();
- EXPECT_EQ("none0", entry->mount_point);
-
- FstabEntry::FsMgrFlags flags = {};
- flags.verify = true;
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-
- EXPECT_EQ("/dir/key", entry->verity_loc);
-}
-
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 9309aad..7e6ad5b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,15 +38,7 @@
#include <vector>
using namespace std::literals::string_literals;
-
-using DeviceMapper = ::android::dm::DeviceMapper;
-using DmTable = ::android::dm::DmTable;
-using DmTarget = ::android::dm::DmTarget;
-using DmTargetLinear = ::android::dm::DmTargetLinear;
-using DmTargetZero = ::android::dm::DmTargetZero;
-using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
-using DmTargetBow = ::android::dm::DmTargetBow;
-using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
static int Usage(void) {
@@ -57,6 +49,7 @@
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
+ std::cerr << " status <dm-name>" << std::endl;
std::cerr << " table <dm-name>" << std::endl;
std::cerr << " help" << std::endl;
std::cerr << std::endl;
@@ -122,6 +115,62 @@
}
std::string block_device = NextArg();
return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
+ } else if (target_type == "snapshot-origin") {
+ if (!HasArgs(1)) {
+ std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
+ return nullptr;
+ }
+ std::string block_device = NextArg();
+ return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
+ block_device);
+ } else if (target_type == "snapshot") {
+ if (!HasArgs(4)) {
+ std::cerr
+ << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string mode_str = NextArg();
+ std::string chunk_size_str = NextArg();
+
+ SnapshotStorageMode mode;
+ if (mode_str == "P") {
+ mode = SnapshotStorageMode::Persistent;
+ } else if (mode_str == "N") {
+ mode = SnapshotStorageMode::Transient;
+ } else {
+ std::cerr << "Unrecognized mode: " << mode_str << "\n";
+ return nullptr;
+ }
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
+ } else if (target_type == "snapshot-merge") {
+ if (!HasArgs(3)) {
+ std::cerr
+ << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string chunk_size_str = NextArg();
+ SnapshotStorageMode mode = SnapshotStorageMode::Merge;
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
@@ -308,7 +357,7 @@
return 0;
}
-static int TableCmdHandler(int argc, char** argv) {
+static int DumpTable(const std::string& mode, int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
return -EINVAL;
@@ -316,9 +365,18 @@
DeviceMapper& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableInfo(argv[0], &table)) {
- std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
- return -EINVAL;
+ if (mode == "status") {
+ if (!dm.GetTableStatus(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (mode == "table") {
+ if (!dm.GetTableInfo(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
}
std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
for (const auto& target : table) {
@@ -333,6 +391,14 @@
return 0;
}
+static int TableCmdHandler(int argc, char** argv) {
+ return DumpTable("table", argc, argv);
+}
+
+static int StatusCmdHandler(int argc, char** argv) {
+ return DumpTable("status", argc, argv);
+}
+
static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
// clang-format off
{"create", DmCreateCmdHandler},
@@ -341,6 +407,7 @@
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
{"table", TableCmdHandler},
+ {"status", StatusCmdHandler},
// clang-format on
};
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 0e5aa4f..edf34f7 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -78,6 +78,7 @@
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
#define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
#define MAX_BATT_LEVEL_WAIT_TIME (3 * MSEC_PER_SEC)
+#define UNPLUGGED_SHUTDOWN_TIME_PROP "ro.product.charger.unplugged_shutdown_time"
#define LAST_KMSG_MAX_SZ (32 * 1024)
@@ -513,6 +514,7 @@
}
static void handle_power_supply_state(charger* charger, int64_t now) {
+ int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
if (!charger->have_battery_state) return;
if (!charger->charger_connected) {
@@ -525,12 +527,14 @@
* Reset & kick animation to show complete animation cycles
* when charger disconnected.
*/
+ timer_shutdown =
+ property_get_int32(UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME);
charger->next_screen_transition = now - 1;
reset_animation(charger->batt_anim);
kick_animation(charger->batt_anim);
- charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
+ charger->next_pwr_check = now + timer_shutdown;
LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
- now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+ now, (int64_t)timer_shutdown, charger->next_pwr_check);
} else if (now >= charger->next_pwr_check) {
LOGW("[%" PRId64 "] shutting down\n", now);
reboot(RB_POWER_OFF);
diff --git a/init/Android.bp b/init/Android.bp
index 6be7290..9c1ed15 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -63,6 +63,7 @@
"libavb",
"libc++fs",
"libcgrouprc_format",
+ "libmodprobe",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
@@ -124,8 +125,10 @@
"reboot.cpp",
"reboot_utils.cpp",
"security.cpp",
+ "selabel.cpp",
"selinux.cpp",
"service.cpp",
+ "service_utils.cpp",
"sigchld_handler.cpp",
"subcontext.cpp",
"subcontext.proto",
@@ -172,7 +175,6 @@
exclude_shared_libs: ["libbinder", "libutils"],
},
},
- ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
}
// Tests
@@ -189,7 +191,6 @@
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",
- "result_test.cpp",
"rlimit_parser_test.cpp",
"service_test.cpp",
"subcontext_test.cpp",
@@ -259,6 +260,7 @@
"rlimit_parser.cpp",
"tokenizer.cpp",
"service.cpp",
+ "service_utils.cpp",
"subcontext.cpp",
"subcontext.proto",
"util.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index c4f7d34..b24f757 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -52,6 +52,7 @@
first_stage_mount.cpp \
mount_namespace.cpp \
reboot_utils.cpp \
+ selabel.cpp \
selinux.cpp \
switch_root.cpp \
uevent_listener.cpp \
@@ -109,6 +110,7 @@
libdexfile_support \
libunwindstack \
libbacktrace \
+ libmodprobe \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/README.md b/init/README.md
index 1b8b848..8179bff 100644
--- a/init/README.md
+++ b/init/README.md
@@ -35,29 +35,19 @@
at the beginning of its execution. It is responsible for the initial
set up of the system.
-Devices that mount /system, /vendor through the first stage mount mechanism
-load all of the files contained within the
+Init loads all of the files contained within the
/{system,vendor,odm}/etc/init/ directories immediately after loading
the primary /init.rc. This is explained in more details in the
Imports section of this file.
-Legacy devices without the first stage mount mechanism do the following:
-1. /init.rc imports /init.${ro.hardware}.rc which is the primary
- vendor supplied .rc file.
-2. During the mount\_all command, the init executable loads all of the
- files contained within the /{system,vendor,odm}/etc/init/ directories.
- These directories are intended for all Actions and Services used after
- file system mounting.
-
-One may specify paths in the mount\_all command line to have it import
-.rc files at the specified paths instead of the default ones listed above.
-This is primarily for supporting factory mode and other non-standard boot
-modes. The three default paths should be used for the normal boot process.
+Legacy devices without the first stage mount mechanism previously were
+able to import init scripts during mount_all, however that is deprecated
+and not allowed for devices launching after Q.
The intention of these directories is:
1. /system/etc/init/ is for core system items such as
- SurfaceFlinger, MediaService, and logcatd.
+ SurfaceFlinger, MediaService, and logd.
2. /vendor/etc/init/ is for SoC vendor items such as actions or
daemons needed for core SoC functionality.
3. /odm/etc/init/ is for device manufacturer items such as
@@ -72,7 +62,7 @@
init .rc file should additionally contain any actions associated with
its service.
-An example is the logcatd.rc and Android.mk files located in the
+An example is the userdebug logcatd.rc and Android.mk files located in the
system/core/logcat directory. The LOCAL\_INIT\_RC macro in the
Android.mk file places logcatd.rc in /system/etc/init/ during the
build process. Init loads logcatd.rc during the mount\_all command and
@@ -88,14 +78,6 @@
conflict resolution when multiple services are added to the system, as
each one will go into a separate file.
-There are two options "early" and "late" in mount\_all command
-which can be set after optional paths. With "--early" set, the
-init executable will skip mounting entries with "latemount" flag
-and triggering fs encryption state event. With "--late" set,
-init executable will only mount entries with "latemount" flag but skip
-importing rc files. By default, no option is set, and mount\_all will
-process all entries in the given fstab.
-
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
@@ -214,9 +196,9 @@
`interface <interface name> <instance name>`
> Associates this service with a list of the HIDL services that it provides. The interface name
- must be a fully-qualified name and not a value name. This is used to allow hwservicemanager to
- lazily start services. When multiple interfaces are served, this tag should be used multiple
- times.
+ must be a fully-qualified name and not a value name. For instance, this is used to allow
+ hwservicemanager to lazily start services. When multiple interfaces are served, this tag should
+ be used multiple times.
For example: interface vendor.foo.bar@1.0::IBaz default
`ioprio <class> <priority>`
@@ -317,7 +299,7 @@
See the below section on debugging for how this can be used.
`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
-> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
+> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
group default to 0. 'seclabel' is the SELinux security context for the
socket. It defaults to the service security context, as specified by
@@ -497,7 +479,11 @@
This is included in the default init.rc.
`loglevel <level>`
-> Sets the kernel log level to level. Properties are expanded within _level_.
+> Sets init's log level to the integer level, from 7 (all logging) to 0
+ (fatal logging only). The numeric values correspond to the kernel log
+ levels, but this command does not affect the kernel log level. Use the
+ `write` command to write to `/proc/sys/kernel/printk` to change that.
+ Properties are expanded within _level_.
`mark_post_data`
> Used to mark the point right after /data is mounted. Used to implement the
@@ -510,19 +496,21 @@
will be updated if the directory exists already.
`mount_all <fstab> [ <path> ]\* [--<option>]`
-> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab and imports .rc files
- at the specified paths (e.g., on the partitions just mounted) with optional
+> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
options "early" and "late".
- Refer to the section of "Init .rc Files" for detail.
+ With "--early" set, the init executable will skip mounting entries with
+ "latemount" flag and triggering fs encryption state event. With "--late" set,
+ init executable will only mount entries with "latemount" flag. By default,
+ no option is set, and mount\_all will process all entries in the given fstab.
`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
> Attempt to mount the named device at the directory _dir_
_flag_s include "ro", "rw", "remount", "noatime", ...
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
- a comma separated string, eg: barrier=1,noauto\_da\_alloc
+ a comma separated string, e.g. barrier=1,noauto\_da\_alloc
`parse_apex_configs`
-> Parses config file(s) from the mounted APEXes. Intented to be used only once
+> Parses config file(s) from the mounted APEXes. Intended to be used only once
when apexd notifies the mount event by setting apexd.status to ready.
`restart <service>`
@@ -585,7 +573,7 @@
`symlink <target> <path>`
> Create a symbolic link at _path_ with the value _target_
-`sysclktz <mins_west_of_gmt>`
+`sysclktz <minutes_west_of_gmt>`
> Set the system clock base (0 if system clock ticks in GMT)
`trigger <event>`
@@ -595,9 +583,6 @@
`umount <path>`
> Unmount the filesystem mounted at that path.
-`verity_load_state`
-> Internal implementation detail used to load dm-verity state.
-
`verity_update_state <mount-point>`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
@@ -637,8 +622,9 @@
`ro.boot.init_rc` during initial boot.
2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
devices immediately after importing /init.rc.
- 3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
- paths during mount_all.
+ 3. (Deprecated) When it imports /{system,vendor,odm}/etc/init/ or .rc files
+ at specified paths during mount_all, not allowed for devices launching
+ after Q.
The order that files are imported is a bit complex for legacy reasons
and to keep backwards compatibility. It is not strictly guaranteed.
@@ -648,7 +634,7 @@
earlier executed trigger, or 2) place it in an Action with the same
trigger within the same file at an earlier line.
-Nonetheless, the defacto order for first stage mount devices is:
+Nonetheless, the de facto order for first stage mount devices is:
1. /init.rc is parsed then recursively each of its imports are
parsed.
2. The contents of /system/etc/init/ are alphabetized and parsed
@@ -695,8 +681,11 @@
> Time after boot in ns (via the CLOCK\_BOOTTIME clock) at which the first
stage of init started.
+`ro.boottime.init.first_stage`
+> How long in ns it took to run first stage.
+
`ro.boottime.init.selinux`
-> How long it took the first stage to initialize SELinux.
+> How long in ns it took to run SELinux stage.
`ro.boottime.init.cold_boot_wait`
> How long init waited for ueventd's coldboot phase to end.
@@ -738,7 +727,7 @@
A handy script named compare-bootcharts.py can be used to compare the
start/end time of selected processes. The aforementioned grab-bootchart.sh
will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
-If two such barballs are preserved on the host machine under different
+If two such tarballs are preserved on the host machine under different
directories, the script can list the timestamps differences. For example:
Usage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_
@@ -786,7 +775,7 @@
This option will send SIGSTOP to a service immediately before calling exec. This gives a window
where developers can attach a debugger, strace, etc before continuing the service with SIGCONT.
-This flag can also be dynamically controled via the ctl.sigstop_on and ctl.sigstop_off properties.
+This flag can also be dynamically controlled via the ctl.sigstop_on and ctl.sigstop_off properties.
Below is an example of dynamically debugging logd via the above:
diff --git a/init/action.cpp b/init/action.cpp
index 94ccef2..a169591 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -28,9 +28,8 @@
namespace android {
namespace init {
-Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
- const std::vector<std::string>& args,
- const std::string& context) {
+Result<void> RunBuiltinFunction(const BuiltinFunction& function,
+ const std::vector<std::string>& args, const std::string& context) {
auto builtin_arguments = BuiltinArguments(context);
builtin_arguments.args.resize(args.size());
@@ -51,7 +50,7 @@
args_(std::move(args)),
line_(line) {}
-Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
+Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
if (subcontext) {
if (execute_in_subcontext_) {
return subcontext->Execute(args_);
@@ -83,7 +82,7 @@
const KeywordFunctionMap* Action::function_map_ = nullptr;
-Result<Success> Action::AddCommand(std::vector<std::string>&& args, int line) {
+Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
if (!function_map_) {
return Error() << "no function map available";
}
@@ -92,7 +91,7 @@
if (!function) return Error() << function.error();
commands_.emplace_back(function->second, function->first, std::move(args), line);
- return Success();
+ return {};
}
void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
@@ -127,7 +126,7 @@
// report such failures unless we're running at the DEBUG log level.
bool report_failure = !result.has_value();
if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
- result.error_errno() == ENOENT) {
+ result.error().code() == ENOENT) {
report_failure = false;
}
@@ -139,7 +138,7 @@
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
<< ":" << command.line() << ") took " << duration.count() << "ms and "
- << (result ? "succeeded" : "failed: " + result.error_string());
+ << (result ? "succeeded" : "failed: " + result.error().message());
}
}
diff --git a/init/action.h b/init/action.h
index 967c682..13b250a 100644
--- a/init/action.h
+++ b/init/action.h
@@ -31,15 +31,15 @@
namespace android {
namespace init {
-Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
- const std::vector<std::string>& args, const std::string& context);
+Result<void> RunBuiltinFunction(const BuiltinFunction& function,
+ const std::vector<std::string>& args, const std::string& context);
class Command {
public:
Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
int line);
- Result<Success> InvokeFunc(Subcontext* subcontext) const;
+ Result<void> InvokeFunc(Subcontext* subcontext) const;
std::string BuildCommandString() const;
int line() const { return line_; }
@@ -61,7 +61,7 @@
const std::string& event_trigger,
const std::map<std::string, std::string>& property_triggers);
- Result<Success> AddCommand(std::vector<std::string>&& args, int line);
+ Result<void> AddCommand(std::vector<std::string>&& args, int line);
void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
std::size_t NumCommands() const;
void ExecuteOneCommand(std::size_t command) const;
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 4f8bd16..ff20e43 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -55,8 +55,8 @@
return CanReadProperty(subcontext->context(), prop_name);
}
-Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
- std::map<std::string, std::string>* property_triggers) {
+Result<void> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
+ std::map<std::string, std::string>* property_triggers) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
@@ -74,12 +74,12 @@
if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
return Error() << "multiple property triggers found for same property";
}
- return Success();
+ return {};
}
-Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
- std::string* event_trigger,
- std::map<std::string, std::string>* property_triggers) {
+Result<void> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
+ std::string* event_trigger,
+ std::map<std::string, std::string>* property_triggers) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
if (args[i].empty()) {
@@ -108,13 +108,13 @@
}
}
- return Success();
+ return {};
}
} // namespace
-Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<void> ActionParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
return Error() << "Actions must have a trigger";
@@ -142,19 +142,19 @@
property_triggers);
action_ = std::move(action);
- return Success();
+ return {};
}
-Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
- return action_ ? action_->AddCommand(std::move(args), line) : Success();
+Result<void> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return action_ ? action_->AddCommand(std::move(args), line) : Result<void>{};
}
-Result<Success> ActionParser::EndSection() {
+Result<void> ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
action_manager_->AddAction(std::move(action_));
}
- return Success();
+ return {};
}
} // namespace init
diff --git a/init/action_parser.h b/init/action_parser.h
index b7f7074..2fe9983 100644
--- a/init/action_parser.h
+++ b/init/action_parser.h
@@ -32,10 +32,10 @@
public:
ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
: action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
- Result<Success> EndSection() override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ Result<void> EndSection() override;
private:
ActionManager* action_manager_;
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index c2cf573..b7db9b6 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -165,20 +165,20 @@
LOG(INFO) << "Bootcharting finished";
}
-static Result<Success> do_bootchart_start() {
+static Result<void> do_bootchart_start() {
// We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
std::string start;
if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
LOG(VERBOSE) << "Not bootcharting";
- return Success();
+ return {};
}
g_bootcharting_thread = new std::thread(bootchart_thread_main);
- return Success();
+ return {};
}
-static Result<Success> do_bootchart_stop() {
- if (!g_bootcharting_thread) return Success();
+static Result<void> do_bootchart_stop() {
+ if (!g_bootcharting_thread) return {};
// Tell the worker thread it's time to quit.
{
@@ -190,10 +190,10 @@
g_bootcharting_thread->join();
delete g_bootcharting_thread;
g_bootcharting_thread = nullptr;
- return Success();
+ return {};
}
-Result<Success> do_bootchart(const BuiltinArguments& args) {
+Result<void> do_bootchart(const BuiltinArguments& args) {
if (args[1] == "start") return do_bootchart_start();
return do_bootchart_stop();
}
diff --git a/init/bootchart.h b/init/bootchart.h
index 05474ca..6f19aad 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -26,7 +26,7 @@
namespace android {
namespace init {
-Result<Success> do_bootchart(const BuiltinArguments& args);
+Result<void> do_bootchart(const BuiltinArguments& args);
} // namespace init
} // namespace android
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
index 0408d30..759eb43 100644
--- a/init/boringssl_self_test.cpp
+++ b/init/boringssl_self_test.cpp
@@ -25,7 +25,7 @@
namespace android {
namespace init {
-Result<Success> StartBoringSslSelfTest(const BuiltinArguments&) {
+Result<void> StartBoringSslSelfTest(const BuiltinArguments&) {
pid_t id = fork();
if (id == 0) {
@@ -49,7 +49,7 @@
PLOG(FATAL) << "Failed to fork for BoringSSL self test";
}
- return Success();
+ return {};
}
} // namespace init
diff --git a/init/boringssl_self_test.h b/init/boringssl_self_test.h
index b21fc78..9e717d0 100644
--- a/init/boringssl_self_test.h
+++ b/init/boringssl_self_test.h
@@ -22,7 +22,7 @@
namespace android {
namespace init {
-Result<Success> StartBoringSslSelfTest(const BuiltinArguments&);
+Result<void> StartBoringSslSelfTest(const BuiltinArguments&);
} // namespace init
} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 25f324c..d9b1b85 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -16,6 +16,7 @@
#include "builtins.h"
+#include <android/api-level.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -62,12 +63,14 @@
#include "action_manager.h"
#include "bootchart.h"
+#include "host_init_stubs.h"
#include "init.h"
#include "mount_namespace.h"
#include "parser.h"
#include "property_service.h"
#include "reboot.h"
#include "rlimit_parser.h"
+#include "selabel.h"
#include "selinux.h"
#include "service.h"
#include "subcontext.h"
@@ -87,14 +90,14 @@
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
-static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<void> reboot_into_recovery(const std::vector<std::string>& options) {
LOG(ERROR) << "Rebooting into recovery";
std::string err;
if (!write_bootloader_message(options, &err)) {
return Error() << "Failed to set bootloader message: " << err;
}
property_set("sys.powerctl", "reboot,recovery");
- return Success();
+ return {};
}
template <typename F>
@@ -104,10 +107,10 @@
}
}
-static Result<Success> do_class_start(const BuiltinArguments& args) {
+static Result<void> do_class_start(const BuiltinArguments& args) {
// Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
- return Success();
+ return {};
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) {
@@ -118,10 +121,10 @@
}
}
}
- return Success();
+ return {};
}
-static Result<Success> do_class_start_post_data(const BuiltinArguments& args) {
+static Result<void> do_class_start_post_data(const BuiltinArguments& args) {
if (args.context != kInitContext) {
return Error() << "command 'class_start_post_data' only available in init context";
}
@@ -133,43 +136,43 @@
}
}
}
- return Success();
+ return {};
}
-static Result<Success> do_class_stop(const BuiltinArguments& args) {
+static Result<void> do_class_stop(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Stop);
- return Success();
+ return {};
}
-static Result<Success> do_class_reset(const BuiltinArguments& args) {
+static Result<void> do_class_reset(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Reset);
- return Success();
+ return {};
}
-static Result<Success> do_class_reset_post_data(const BuiltinArguments& args) {
+static Result<void> do_class_reset_post_data(const BuiltinArguments& args) {
if (args.context != kInitContext) {
return Error() << "command 'class_reset_post_data' only available in init context";
}
ForEachServiceInClass(args[1], &Service::ResetIfPostData);
- return Success();
+ return {};
}
-static Result<Success> do_class_restart(const BuiltinArguments& args) {
+static Result<void> do_class_restart(const BuiltinArguments& args) {
// Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
- return Success();
+ return {};
ForEachServiceInClass(args[1], &Service::Restart);
- return Success();
+ return {};
}
-static Result<Success> do_domainname(const BuiltinArguments& args) {
+static Result<void> do_domainname(const BuiltinArguments& args) {
if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_enable(const BuiltinArguments& args) {
+static Result<void> do_enable(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "Could not find service";
@@ -177,10 +180,10 @@
return Error() << "Could not enable service: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_exec(const BuiltinArguments& args) {
+static Result<void> do_exec(const BuiltinArguments& args) {
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
return Error() << "Could not create exec service";
@@ -190,10 +193,10 @@
}
ServiceList::GetInstance().AddService(std::move(service));
- return Success();
+ return {};
}
-static Result<Success> do_exec_background(const BuiltinArguments& args) {
+static Result<void> do_exec_background(const BuiltinArguments& args) {
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
return Error() << "Could not create exec background service";
@@ -203,10 +206,10 @@
}
ServiceList::GetInstance().AddService(std::move(service));
- return Success();
+ return {};
}
-static Result<Success> do_exec_start(const BuiltinArguments& args) {
+static Result<void> do_exec_start(const BuiltinArguments& args) {
Service* service = ServiceList::GetInstance().FindService(args[1]);
if (!service) {
return Error() << "Service not found";
@@ -216,24 +219,24 @@
return Error() << "Could not start exec service: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_export(const BuiltinArguments& args) {
+static Result<void> do_export(const BuiltinArguments& args) {
if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
return ErrnoError() << "setenv() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_hostname(const BuiltinArguments& args) {
+static Result<void> do_hostname(const BuiltinArguments& args) {
if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_ifup(const BuiltinArguments& args) {
+static Result<void> do_ifup(const BuiltinArguments& args) {
struct ifreq ifr;
strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
@@ -251,10 +254,10 @@
return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_insmod(const BuiltinArguments& args) {
+static Result<void> do_insmod(const BuiltinArguments& args) {
int flags = 0;
auto it = args.begin() + 1;
@@ -272,34 +275,34 @@
int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
- return Success();
+ return {};
}
-static Result<Success> do_interface_restart(const BuiltinArguments& args) {
+static Result<void> do_interface_restart(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
svc->Restart();
- return Success();
+ return {};
}
-static Result<Success> do_interface_start(const BuiltinArguments& args) {
+static Result<void> do_interface_start(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
if (auto result = svc->Start(); !result) {
return Error() << "Could not start interface: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_interface_stop(const BuiltinArguments& args) {
+static Result<void> do_interface_stop(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
svc->Stop();
- return Success();
+ return {};
}
// mkdir <path> [mode] [owner] [group]
-static Result<Success> do_mkdir(const BuiltinArguments& args) {
+static Result<void> do_mkdir(const BuiltinArguments& args) {
mode_t mode = 0755;
if (args.size() >= 3) {
mode = std::strtoul(args[2].c_str(), 0, 8);
@@ -348,15 +351,15 @@
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
}
}
- return Success();
+ return {};
}
/* umount <path> */
-static Result<Success> do_umount(const BuiltinArguments& args) {
+static Result<void> do_umount(const BuiltinArguments& args) {
if (umount(args[1].c_str()) < 0) {
return ErrnoError() << "umount() failed";
}
- return Success();
+ return {};
}
static struct {
@@ -384,7 +387,7 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-static Result<Success> do_mount(const BuiltinArguments& args) {
+static Result<void> do_mount(const BuiltinArguments& args) {
const char* options = nullptr;
unsigned flags = 0;
bool wait = false;
@@ -431,7 +434,7 @@
ioctl(loop, LOOP_CLR_FD, 0);
return ErrnoError() << "mount() failed";
}
- return Success();
+ return {};
}
}
}
@@ -446,7 +449,7 @@
}
- return Success();
+ return {};
}
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -474,78 +477,6 @@
if (false) DumpState();
}
-/* handle_fstab
- *
- * Read the given fstab file and execute func on it.
- */
-static Result<int> handle_fstab(const std::string& fstabfile, std::function<int(Fstab*)> func) {
- /*
- * Call fs_mgr_[u]mount_all() to [u]mount all filesystems. We fork(2) and
- * do the call in the child to provide protection to the main init
- * process if anything goes wrong (crash or memory leak), and wait for
- * the child to finish in the parent.
- */
- pid_t pid = fork();
- if (pid > 0) {
- /* Parent. Wait for the child to return */
- int status;
- int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (wp_ret == -1) {
- // Unexpected error code. We will continue anyway.
- PLOG(WARNING) << "waitpid failed";
- }
-
- if (WIFEXITED(status)) {
- return WEXITSTATUS(status);
- } else {
- return Error() << "child aborted";
- }
- } else if (pid == 0) {
- /* child, call fs_mgr_[u]mount_all() */
-
- // So we can always see what fs_mgr_[u]mount_all() does.
- // Only needed if someone explicitly changes the default log level in their init.rc.
- android::base::ScopedLogSeverity info(android::base::INFO);
-
- Fstab fstab;
- ReadFstabFromFile(fstabfile, &fstab);
-
- int child_ret = func(&fstab);
-
- _exit(child_ret);
- } else {
- return Error() << "fork() failed";
- }
-}
-
-/* mount_fstab
- *
- * Call fs_mgr_mount_all() to mount the given fstab
- */
-static Result<int> mount_fstab(const std::string& fstabfile, int mount_mode) {
- return handle_fstab(fstabfile, [mount_mode](Fstab* fstab) {
- int ret = fs_mgr_mount_all(fstab, mount_mode);
- if (ret == -1) {
- PLOG(ERROR) << "fs_mgr_mount_all returned an error";
- }
- return ret;
- });
-}
-
-/* umount_fstab
- *
- * Call fs_mgr_umount_all() to umount the given fstab
- */
-static Result<int> umount_fstab(const std::string& fstabfile) {
- return handle_fstab(fstabfile, [](Fstab* fstab) {
- int ret = fs_mgr_umount_all(fstab);
- if (ret != 0) {
- PLOG(ERROR) << "fs_mgr_umount_all returned " << ret;
- }
- return ret;
- });
-}
-
/* Queue event based on fs_mgr return code.
*
* code: return code of fs_mgr_mount_all
@@ -555,23 +486,23 @@
*
* return code is processed based on input code
*/
-static Result<Success> queue_fs_event(int code) {
+static Result<void> queue_fs_event(int code) {
if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
ActionManager::GetInstance().QueueEventTrigger("encrypt");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
property_set("ro.crypto.state", "unsupported");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
if (android::gsi::IsGsiRunning()) {
@@ -591,7 +522,7 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
if (fscrypt_install_keyring()) {
return Error() << "fscrypt_install_keyring() failed";
@@ -602,7 +533,7 @@
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (fscrypt_install_keyring()) {
return Error() << "fscrypt_install_keyring() failed";
@@ -613,7 +544,7 @@
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code > 0) {
Error() << "fs_mgr_mount_all() returned unexpected error " << code;
}
@@ -627,12 +558,12 @@
* This function might request a reboot, in which case it will
* not return.
*/
-static Result<Success> do_mount_all(const BuiltinArguments& args) {
+static Result<void> do_mount_all(const BuiltinArguments& args) {
std::size_t na = 0;
bool import_rc = true;
bool queue_event = true;
int mount_mode = MOUNT_MODE_DEFAULT;
- const auto& fstabfile = args[1];
+ const auto& fstab_file = args[1];
std::size_t path_arg_end = args.size();
const char* prop_post_fix = "default";
@@ -652,13 +583,20 @@
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
android::base::Timer t;
- auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+
+ Fstab fstab;
+ if (!ReadFstabFromFile(fstab_file, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
+
+ auto mount_fstab_return_code =
+ CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
if (!mount_fstab_return_code) {
- return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+ return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
}
property_set(prop_name, std::to_string(t.duration().count()));
- if (import_rc) {
+ if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
import_late(args.args, 2, path_arg_end);
}
@@ -672,72 +610,86 @@
}
}
- return Success();
+ return {};
}
/* umount_all <fstab> */
-static Result<Success> do_umount_all(const BuiltinArguments& args) {
- auto umount_fstab_return_code = umount_fstab(args[1]);
- if (!umount_fstab_return_code) {
- return Error() << "umount_fstab() failed " << umount_fstab_return_code.error();
+static Result<void> do_umount_all(const BuiltinArguments& args) {
+ Fstab fstab;
+ if (!ReadFstabFromFile(args[1], &fstab)) {
+ return Error() << "Could not read fstab";
}
- return Success();
+
+ auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
+ if (!result) {
+ return Error() << "Could not call fs_mgr_mount_all() " << result.error();
+ }
+
+ if (*result != 0) {
+ return Error() << "fs_mgr_mount_all() failed: " << *result;
+ }
+ return {};
}
-static Result<Success> do_swapon_all(const BuiltinArguments& args) {
+static Result<void> do_swapon_all(const BuiltinArguments& args) {
Fstab fstab;
if (!ReadFstabFromFile(args[1], &fstab)) {
return Error() << "Could not read fstab '" << args[1] << "'";
}
- if (!fs_mgr_swapon_all(fstab)) {
- return Error() << "fs_mgr_swapon_all() failed";
+ auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
+ if (!result) {
+ return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
}
- return Success();
+ if (*result == 0) {
+ return Error() << "fs_mgr_swapon_all() failed.";
+ }
+
+ return {};
}
-static Result<Success> do_setprop(const BuiltinArguments& args) {
+static Result<void> do_setprop(const BuiltinArguments& args) {
property_set(args[1], args[2]);
- return Success();
+ return {};
}
-static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+static Result<void> do_setrlimit(const BuiltinArguments& args) {
auto rlimit = ParseRlimit(args.args);
if (!rlimit) return rlimit.error();
if (setrlimit(rlimit->first, &rlimit->second) == -1) {
return ErrnoError() << "setrlimit failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_start(const BuiltinArguments& args) {
+static Result<void> do_start(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
if (auto result = svc->Start(); !result) {
return Error() << "Could not start service: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_stop(const BuiltinArguments& args) {
+static Result<void> do_stop(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Stop();
- return Success();
+ return {};
}
-static Result<Success> do_restart(const BuiltinArguments& args) {
+static Result<void> do_restart(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Restart();
- return Success();
+ return {};
}
-static Result<Success> do_trigger(const BuiltinArguments& args) {
+static Result<void> do_trigger(const BuiltinArguments& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
- return Success();
+ return {};
}
static int MakeSymlink(const std::string& target, const std::string& linkpath) {
@@ -758,33 +710,33 @@
return rc;
}
-static Result<Success> do_symlink(const BuiltinArguments& args) {
+static Result<void> do_symlink(const BuiltinArguments& args) {
if (MakeSymlink(args[1], args[2]) < 0) {
// The symlink builtin is often used to create symlinks for older devices to be backwards
// compatible with new paths, therefore we skip reporting this error.
if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
- return Success();
+ return {};
}
return ErrnoError() << "symlink() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_rm(const BuiltinArguments& args) {
+static Result<void> do_rm(const BuiltinArguments& args) {
if (unlink(args[1].c_str()) < 0) {
return ErrnoError() << "unlink() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_rmdir(const BuiltinArguments& args) {
+static Result<void> do_rmdir(const BuiltinArguments& args) {
if (rmdir(args[1].c_str()) < 0) {
return ErrnoError() << "rmdir() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_sysclktz(const BuiltinArguments& args) {
+static Result<void> do_sysclktz(const BuiltinArguments& args) {
struct timezone tz = {};
if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
return Error() << "Unable to parse mins_west_of_gmt";
@@ -793,21 +745,10 @@
if (settimeofday(nullptr, &tz) == -1) {
return ErrnoError() << "settimeofday() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
- int mode = -1;
- bool loaded = fs_mgr_load_verity_state(&mode);
- if (loaded && mode != VERITY_MODE_DEFAULT) {
- ActionManager::GetInstance().QueueEventTrigger("verity-logging");
- }
- if (!loaded) return Error() << "Could not load verity state";
-
- return Success();
-}
-
-static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
+static Result<void> do_verity_update_state(const BuiltinArguments& args) {
int mode;
if (!fs_mgr_load_verity_state(&mode)) {
return Error() << "fs_mgr_load_verity_state() failed";
@@ -829,18 +770,18 @@
property_set("partition." + partition + ".verified", std::to_string(mode));
}
- return Success();
+ return {};
}
-static Result<Success> do_write(const BuiltinArguments& args) {
+static Result<void> do_write(const BuiltinArguments& args) {
if (auto result = WriteFile(args[1], args[2]); !result) {
return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> readahead_file(const std::string& filename, bool fully) {
+static Result<void> readahead_file(const std::string& filename, bool fully) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
if (fd == -1) {
return ErrnoError() << "Error opening file";
@@ -860,10 +801,10 @@
return ErrnoError() << "Error reading file";
}
}
- return Success();
+ return {};
}
-static Result<Success> do_readahead(const BuiltinArguments& args) {
+static Result<void> do_readahead(const BuiltinArguments& args) {
struct stat sb;
if (stat(args[1].c_str(), &sb)) {
@@ -919,10 +860,10 @@
} else if (pid < 0) {
return ErrnoError() << "Fork failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_copy(const BuiltinArguments& args) {
+static Result<void> do_copy(const BuiltinArguments& args) {
auto file_contents = ReadFile(args[1]);
if (!file_contents) {
return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
@@ -931,10 +872,10 @@
return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_chown(const BuiltinArguments& args) {
+static Result<void> do_chown(const BuiltinArguments& args) {
auto uid = DecodeUid(args[1]);
if (!uid) {
return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
@@ -955,7 +896,7 @@
return ErrnoError() << "lchown() failed";
}
- return Success();
+ return {};
}
static mode_t get_mode(const char *s) {
@@ -971,15 +912,15 @@
return mode;
}
-static Result<Success> do_chmod(const BuiltinArguments& args) {
+static Result<void> do_chmod(const BuiltinArguments& args) {
mode_t mode = get_mode(args[1].c_str());
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
return ErrnoError() << "fchmodat() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_restorecon(const BuiltinArguments& args) {
+static Result<void> do_restorecon(const BuiltinArguments& args) {
int ret = 0;
struct flag_type {const char* name; int value;};
@@ -1018,16 +959,16 @@
}
if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
- return Success();
+ return {};
}
-static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+static Result<void> do_restorecon_recursive(const BuiltinArguments& args) {
std::vector<std::string> non_const_args(args.args);
non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
return do_restorecon({std::move(non_const_args), args.context});
}
-static Result<Success> do_loglevel(const BuiltinArguments& args) {
+static Result<void> do_loglevel(const BuiltinArguments& args) {
// TODO: support names instead/as well?
int log_level = -1;
android::base::ParseInt(args[1], &log_level);
@@ -1045,20 +986,20 @@
return Error() << "invalid log level " << log_level;
}
android::base::SetMinimumLogSeverity(severity);
- return Success();
+ return {};
}
-static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
+static Result<void> do_load_persist_props(const BuiltinArguments& args) {
load_persist_props();
- return Success();
+ return {};
}
-static Result<Success> do_load_system_props(const BuiltinArguments& args) {
+static Result<void> do_load_system_props(const BuiltinArguments& args) {
LOG(INFO) << "deprecated action `load_system_props` called.";
- return Success();
+ return {};
}
-static Result<Success> do_wait(const BuiltinArguments& args) {
+static Result<void> do_wait(const BuiltinArguments& args) {
auto timeout = kCommandRetryTimeout;
if (args.size() == 3) {
int timeout_int;
@@ -1072,10 +1013,10 @@
return Error() << "wait_for_file() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
+static Result<void> do_wait_for_prop(const BuiltinArguments& args) {
const char* name = args[1].c_str();
const char* value = args[2].c_str();
size_t value_len = strlen(value);
@@ -1089,15 +1030,15 @@
if (!start_waiting_for_property(name, value)) {
return Error() << "already waiting for a property";
}
- return Success();
+ return {};
}
static bool is_file_crypto() {
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
- const BuiltinArguments& args) {
+static Result<void> ExecWithRebootOnFailure(const std::string& reboot_reason,
+ const BuiltinArguments& args) {
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
return Error() << "Could not create exec service";
@@ -1121,11 +1062,11 @@
return Error() << "Could not start exec service: " << result.error();
}
ServiceList::GetInstance().AddService(std::move(service));
- return Success();
+ return {};
}
-static Result<Success> do_installkey(const BuiltinArguments& args) {
- if (!is_file_crypto()) return Success();
+static Result<void> do_installkey(const BuiltinArguments& args) {
+ if (!is_file_crypto()) return {};
auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
@@ -1136,24 +1077,21 @@
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
}
-static Result<Success> do_init_user0(const BuiltinArguments& args) {
+static Result<void> do_init_user0(const BuiltinArguments& args) {
return ExecWithRebootOnFailure(
"init_user0_failed",
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
}
-static Result<Success> do_mark_post_data(const BuiltinArguments& args) {
+static Result<void> do_mark_post_data(const BuiltinArguments& args) {
ServiceList::GetInstance().MarkPostData();
- return Success();
+ return {};
}
-static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
+static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
glob_t glob_result;
- // @ is added to filter out the later paths, which are bind mounts of the places
- // where the APEXes are really mounted at. Otherwise, we will parse the
- // same file twice.
- static constexpr char glob_pattern[] = "/apex/*@*/etc/*.rc";
+ static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
if (ret != 0 && ret != GLOB_NOMATCH) {
globfree(&glob_result);
@@ -1162,7 +1100,15 @@
std::vector<std::string> configs;
Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance());
for (size_t i = 0; i < glob_result.gl_pathc; i++) {
- configs.emplace_back(glob_result.gl_pathv[i]);
+ std::string path = glob_result.gl_pathv[i];
+ // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will parse the
+ // same file twice.
+ std::vector<std::string> paths = android::base::Split(path, "/");
+ if (paths.size() >= 2 && paths[1].find('@') != std::string::npos) {
+ continue;
+ }
+ configs.push_back(path);
}
globfree(&glob_result);
@@ -1176,15 +1122,15 @@
}
ServiceList::GetInstance().MarkServicesUpdate();
if (success) {
- return Success();
+ return {};
} else {
return Error() << "Could not parse apex configs";
}
}
-static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) {
+static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
if (SwitchToDefaultMountNamespace()) {
- return Success();
+ return {};
} else {
return Error() << "Failed to enter into default mount namespace";
}
@@ -1248,7 +1194,6 @@
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},
{"trigger", {1, 1, {false, do_trigger}}},
- {"verity_load_state", {0, 0, {false, do_verity_load_state}}},
{"verity_update_state", {0, 0, {false, do_verity_update_state}}},
{"wait", {1, 2, {true, do_wait}}},
{"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},
diff --git a/init/builtins.h b/init/builtins.h
index 814b2d5..5db0d1c 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -29,7 +29,7 @@
namespace android {
namespace init {
-using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
+using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;
using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
class BuiltinFunctionMap : public KeywordFunctionMap {
diff --git a/init/devices.cpp b/init/devices.cpp
index 159c75e..5e760d0 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -36,7 +36,7 @@
#include <selinux/android.h>
#include <selinux/selinux.h>
-#include "selinux.h"
+#include "selabel.h"
#include "util.h"
#ifdef _INIT_INIT_H
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 94dd553..01d8867 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -28,17 +28,17 @@
Epoll::Epoll() {}
-Result<Success> Epoll::Open() {
- if (epoll_fd_ >= 0) return Success();
+Result<void> Epoll::Open() {
+ if (epoll_fd_ >= 0) return {};
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd_ == -1) {
return ErrnoError() << "epoll_create1 failed";
}
- return Success();
+ return {};
}
-Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
+Result<void> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
if (!events) {
return Error() << "Must specify events";
}
@@ -52,24 +52,24 @@
// pointer to the std::function in the map directly for epoll_ctl.
ev.data.ptr = reinterpret_cast<void*>(&it->second);
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
- Result<Success> result = ErrnoError() << "epoll_ctl failed to add fd";
+ Result<void> result = ErrnoError() << "epoll_ctl failed to add fd";
epoll_handlers_.erase(fd);
return result;
}
- return Success();
+ return {};
}
-Result<Success> Epoll::UnregisterHandler(int fd) {
+Result<void> Epoll::UnregisterHandler(int fd) {
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1) {
return ErrnoError() << "epoll_ctl failed to remove fd";
}
if (epoll_handlers_.erase(fd) != 1) {
return Error() << "Attempting to remove epoll handler for FD without an existing handler";
}
- return Success();
+ return {};
}
-Result<Success> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
+Result<void> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
int timeout_ms = -1;
if (timeout && timeout->count() < INT_MAX) {
timeout_ms = timeout->count();
@@ -81,7 +81,7 @@
} else if (nr == 1) {
std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
}
- return Success();
+ return {};
}
} // namespace init
diff --git a/init/epoll.h b/init/epoll.h
index 9789bef..ca84266 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -36,11 +36,10 @@
public:
Epoll();
- Result<Success> Open();
- Result<Success> RegisterHandler(int fd, std::function<void()> handler,
- uint32_t events = EPOLLIN);
- Result<Success> UnregisterHandler(int fd);
- Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
+ Result<void> Open();
+ Result<void> RegisterHandler(int fd, std::function<void()> handler, uint32_t events = EPOLLIN);
+ Result<void> UnregisterHandler(int fd);
+ Result<void> Wait(std::optional<std::chrono::milliseconds> timeout);
private:
android::base::unique_fd epoll_fd_;
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 740e82c..c067f6f 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -80,15 +80,26 @@
return;
}
+ std::vector<std::string> attempted_paths_and_errors;
+
try_loading_again:
+ attempted_paths_and_errors.clear();
for (const auto& firmware_directory : firmware_directories_) {
std::string file = firmware_directory + uevent.firmware;
unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
- struct stat sb;
- if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
- LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
- return;
+ if (fw_fd == -1) {
+ attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
+ ", open failed: " + strerror(errno));
+ continue;
}
+ struct stat sb;
+ if (fstat(fw_fd, &sb) == -1) {
+ attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
+ ", fstat failed: " + strerror(errno));
+ continue;
+ }
+ LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return;
}
if (booting) {
@@ -100,6 +111,9 @@
}
LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+ for (const auto& message : attempted_paths_and_errors) {
+ LOG(ERROR) << message;
+ }
// Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
write(loading_fd, "-1", 2);
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 2b89940..17387e2 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -33,6 +33,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
#include "debug_ramdisk.h"
@@ -192,6 +193,11 @@
old_root_dir.reset();
}
+ Modprobe m({"/lib/modules"});
+ if (!m.LoadListedModules()) {
+ LOG(FATAL) << "Failed to load kernel modules";
+ }
+
if (ForceNormalBoot()) {
mkdir("/first_stage_ramdisk", 0755);
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
@@ -231,9 +237,8 @@
SetInitAvbVersionInRecovery();
- static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
- uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
- setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
+ setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
+ 1);
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
index 0476e44..7de816f 100644
--- a/init/first_stage_init.h
+++ b/init/first_stage_init.h
@@ -21,5 +21,7 @@
int FirstStageMain(int argc, char** argv);
+static constexpr char kEnvFirstStageStartedAt[] = "FIRST_STAGE_STARTED_AT";
+
} // namespace init
} // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 3e76556..1a5ed28 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -276,14 +276,12 @@
// required_devices_partition_names_. Found partitions will then be removed from it
// for the subsequent member function to check which devices are NOT created.
bool FirstStageMount::InitRequiredDevices() {
- if (required_devices_partition_names_.empty()) {
- return true;
+ if (!InitDeviceMapper()) {
+ return false;
}
- if (IsDmLinearEnabled() || need_dm_verity_) {
- if (!InitDeviceMapper()) {
- return false;
- }
+ if (required_devices_partition_names_.empty()) {
+ return true;
}
auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
@@ -604,12 +602,6 @@
return;
}
- // Device-mapper might not be ready if the device doesn't use DAP or verity
- // (for example, hikey).
- if (access("/dev/device-mapper", F_OK) && !InitDeviceMapper()) {
- return;
- }
-
// Find the name of the super partition for the GSI. It will either be
// "userdata", or a block device such as an sdcard. There are no by-name
// partitions other than userdata that we support installing GSIs to.
@@ -644,7 +636,6 @@
}
bool FirstStageMountVBootV1::GetDmVerityDevices() {
- std::string verity_loc_device;
need_dm_verity_ = false;
for (const auto& fstab_entry : fstab_) {
@@ -657,21 +648,9 @@
if (fstab_entry.fs_mgr_flags.verify) {
need_dm_verity_ = true;
}
- // Checks if verity metadata is on a separate partition. Note that it is
- // not partition specific, so there must be only one additional partition
- // that carries verity state.
- if (!fstab_entry.verity_loc.empty()) {
- if (verity_loc_device.empty()) {
- verity_loc_device = fstab_entry.verity_loc;
- } else if (verity_loc_device != fstab_entry.verity_loc) {
- LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
- << fstab_entry.verity_loc;
- return false;
- }
- }
}
- // Includes the partition names of fstab records and verity_loc_device (if any).
+ // Includes the partition names of fstab records.
// Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
for (const auto& fstab_entry : fstab_) {
if (!fstab_entry.fs_mgr_flags.logical) {
@@ -679,10 +658,6 @@
}
}
- if (!verity_loc_device.empty()) {
- required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
- }
-
return true;
}
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
index 93e363f..aa80199 100644
--- a/init/host_import_parser.cpp
+++ b/init/host_import_parser.cpp
@@ -23,16 +23,16 @@
namespace android {
namespace init {
-Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
- int) {
+Result<void> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+ int) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
- return Success();
+ return {};
}
-Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+Result<void> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
return Error() << "Unexpected line found after import statement";
}
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
index 52b8891..d6f7286 100644
--- a/init/host_import_parser.h
+++ b/init/host_import_parser.h
@@ -27,8 +27,8 @@
class HostImportParser : public SectionParser {
public:
HostImportParser() {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
- Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+ Result<void> ParseLineSection(std::vector<std::string>&&, int) override;
};
} // namespace init
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 8407729..cb861f3 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -118,8 +118,8 @@
namespace android {
namespace init {
-static Result<Success> do_stub(const BuiltinArguments& args) {
- return Success();
+static Result<void> do_stub(const BuiltinArguments& args) {
+ return {};
}
#include "generated_stub_builtin_function_map.h"
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index fb3185e..c72b7d6 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,8 +23,8 @@
namespace android {
namespace init {
-Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<void> ImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
@@ -38,10 +38,10 @@
LOG(INFO) << "Added '" << conf_file << "' to import list";
if (filename_.empty()) filename_ = filename;
imports_.emplace_back(std::move(conf_file), line);
- return Success();
+ return {};
}
-Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+Result<void> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
return Error() << "Unexpected line found after import statement";
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 7bc72e6..5bf9c6c 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -28,9 +28,9 @@
class ImportParser : public SectionParser {
public:
ImportParser(Parser* parser) : parser_(parser) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&&, int) override;
void EndFile() override;
private:
diff --git a/init/init.cpp b/init/init.cpp
index 6b03bc9..0d3b99f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -35,6 +35,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -54,6 +55,7 @@
#include "action_parser.h"
#include "boringssl_self_test.h"
#include "epoll.h"
+#include "first_stage_init.h"
#include "first_stage_mount.h"
#include "import_parser.h"
#include "keychords.h"
@@ -63,6 +65,7 @@
#include "reboot.h"
#include "reboot_utils.h"
#include "security.h"
+#include "selabel.h"
#include "selinux.h"
#include "sigchld_handler.h"
#include "util.h"
@@ -195,6 +198,14 @@
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+ // We always record how long init waited for ueventd to tell us cold boot finished.
+ // If we aren't waiting on this property, it means that ueventd finished before we even started
+ // to wait.
+ if (name == kColdBootDoneProp) {
+ auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
+ property_set("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+ }
+
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
LOG(INFO) << "Wait for property '" << wait_prop_name << "=" << wait_prop_value
@@ -234,18 +245,18 @@
return next_process_action_time;
}
-static Result<Success> DoControlStart(Service* service) {
+static Result<void> DoControlStart(Service* service) {
return service->Start();
}
-static Result<Success> DoControlStop(Service* service) {
+static Result<void> DoControlStop(Service* service) {
service->Stop();
- return Success();
+ return {};
}
-static Result<Success> DoControlRestart(Service* service) {
+static Result<void> DoControlRestart(Service* service) {
service->Restart();
- return Success();
+ return {};
}
enum class ControlTarget {
@@ -255,16 +266,16 @@
struct ControlMessageFunction {
ControlTarget target;
- std::function<Result<Success>(Service*)> action;
+ std::function<Result<void>(Service*)> action;
};
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
// clang-format off
static const std::map<std::string, ControlMessageFunction> control_message_functions = {
{"sigstop_on", {ControlTarget::SERVICE,
- [](auto* service) { service->set_sigstop(true); return Success(); }}},
+ [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}},
{"sigstop_off", {ControlTarget::SERVICE,
- [](auto* service) { service->set_sigstop(false); return Success(); }}},
+ [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}},
{"start", {ControlTarget::SERVICE, DoControlStart}},
{"stop", {ControlTarget::SERVICE, DoControlStop}},
{"restart", {ControlTarget::SERVICE, DoControlRestart}},
@@ -277,13 +288,13 @@
return control_message_functions;
}
-void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
+bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
const auto& map = get_control_message_map();
const auto it = map.find(msg);
if (it == map.end()) {
LOG(ERROR) << "Unknown control msg '" << msg << "'";
- return;
+ return false;
}
std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
@@ -312,49 +323,38 @@
default:
LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
<< static_cast<std::underlying_type<ControlTarget>::type>(function.target);
- return;
+ return false;
}
if (svc == nullptr) {
LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
- return;
+ return false;
}
if (auto result = function.action(svc); !result) {
LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
+ return false;
}
+ return true;
}
-static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
- Timer t;
-
- LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
-
- // Historically we had a 1s timeout here because we weren't otherwise
- // tracking boot time, and many OEMs made their sepolicy regular
- // expressions too expensive (http://b/19899875).
-
- // Now we're tracking boot time, just log the time taken to a system
- // property. We still panic if it takes more than a minute though,
- // because any build that slow isn't likely to boot at all, and we'd
- // rather any test lab devices fail back to the bootloader.
- if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
- LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
+static Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {
+ if (!start_waiting_for_property(kColdBootDoneProp, "true")) {
+ LOG(FATAL) << "Could not wait for '" << kColdBootDoneProp << "'";
}
- property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
- return Success();
+ return {};
}
-static Result<Success> console_init_action(const BuiltinArguments& args) {
+static Result<void> console_init_action(const BuiltinArguments& args) {
std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
default_console = "/dev/" + console;
}
- return Success();
+ return {};
}
-static Result<Success> SetupCgroupsAction(const BuiltinArguments&) {
+static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
// Have to create <CGROUPS_RC_DIR> using make_dir function
// for appropriate sepolicy to be set for it
make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
@@ -362,7 +362,7 @@
return ErrnoError() << "Failed to setup cgroups";
}
- return Success();
+ return {};
}
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -446,19 +446,19 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
-static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
+static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
/* Enable property triggers. */
property_triggers_enabled = 1;
- return Success();
+ return {};
}
-static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
+static Result<void> queue_property_triggers_action(const BuiltinArguments& args) {
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
ActionManager::GetInstance().QueueAllPropertyActions();
- return Success();
+ return {};
}
-static Result<Success> InitBinder(const BuiltinArguments& args) {
+static Result<void> InitBinder(const BuiltinArguments& args) {
// init's use of binder is very limited. init cannot:
// - have any binder threads
// - receive incoming binder calls
@@ -473,7 +473,7 @@
android::ProcessState::self()->setCallRestriction(
ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY);
#endif
- return Success();
+ return {};
}
// Set the UDC controller for the ConfigFS USB Gadgets.
@@ -615,11 +615,38 @@
}
}
+static void RecordStageBoottimes(const boot_clock::time_point& second_stage_start_time) {
+ int64_t first_stage_start_time_ns = -1;
+ if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);
+ first_stage_start_time_str) {
+ property_set("ro.boottime.init", first_stage_start_time_str);
+ android::base::ParseInt(first_stage_start_time_str, &first_stage_start_time_ns);
+ }
+ unsetenv(kEnvFirstStageStartedAt);
+
+ int64_t selinux_start_time_ns = -1;
+ if (auto selinux_start_time_str = getenv(kEnvSelinuxStartedAt); selinux_start_time_str) {
+ android::base::ParseInt(selinux_start_time_str, &selinux_start_time_ns);
+ }
+ unsetenv(kEnvSelinuxStartedAt);
+
+ if (selinux_start_time_ns == -1) return;
+ if (first_stage_start_time_ns == -1) return;
+
+ property_set("ro.boottime.init.first_stage",
+ std::to_string(selinux_start_time_ns - first_stage_start_time_ns));
+ property_set("ro.boottime.init.selinux",
+ std::to_string(second_stage_start_time.time_since_epoch().count() -
+ selinux_start_time_ns));
+}
+
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
+ boot_clock::time_point start_time = boot_clock::now();
+
SetStdioToDevNull(argv);
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
@@ -651,9 +678,8 @@
// used by init as well as the current required properties.
export_kernel_boot_props();
- // Make the time that init started available for bootstat to log.
- property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
- property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
+ // Make the time that init stages started available for bootstat to log.
+ RecordStageBoottimes(start_time);
// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
@@ -666,8 +692,6 @@
}
// Clean up our environment.
- unsetenv("INIT_STARTED_AT");
- unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
unsetenv("INIT_FORCE_DEBUGGABLE");
@@ -728,14 +752,14 @@
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
Keychords keychords;
am.QueueBuiltinAction(
- [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
- for (const auto& svc : ServiceList::GetInstance()) {
- keychords.Register(svc->keycodes());
- }
- keychords.Start(&epoll, HandleKeychord);
- return Success();
- },
- "KeychordInit");
+ [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
+ for (const auto& svc : ServiceList::GetInstance()) {
+ keychords.Register(svc->keycodes());
+ }
+ keychords.Start(&epoll, HandleKeychord);
+ return {};
+ },
+ "KeychordInit");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
diff --git a/init/init.h b/init/init.h
index a76da20..90ead0e 100644
--- a/init/init.h
+++ b/init/init.h
@@ -40,7 +40,7 @@
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateServiceOnlyParser(ServiceList& service_list);
-void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
+bool HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
void property_changed(const std::string& name, const std::string& value);
diff --git a/init/init_test.cpp b/init/init_test.cpp
index c2f0c41..18c2b38 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -180,7 +180,7 @@
auto execute_command = [&num_executed](const BuiltinArguments& args) {
EXPECT_EQ(2U, args.size());
EXPECT_EQ(++num_executed, std::stoi(args[1]));
- return Success();
+ return Result<void>{};
};
TestFunctionMap test_function_map;
diff --git a/init/keychords.cpp b/init/keychords.cpp
index f5ac44f..d0ca3e7 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -41,7 +41,7 @@
Keychords::~Keychords() noexcept {
if (inotify_fd_ >= 0) {
- epoll_->UnregisterHandler(inotify_fd_).IgnoreError();
+ epoll_->UnregisterHandler(inotify_fd_);
::close(inotify_fd_);
}
while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
@@ -212,7 +212,7 @@
auto it = registration_.find(device);
if (it == registration_.end()) return;
auto fd = (*it).second;
- epoll_->UnregisterHandler(fd).IgnoreError();
+ epoll_->UnregisterHandler(fd);
registration_.erase(it);
::close(fd);
}
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index e5a6fd3..33373d4 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -29,7 +29,6 @@
#include <vector>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
@@ -213,7 +212,7 @@
}
void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
- epoll_.Wait(wait).IgnoreError();
+ epoll_.Wait(wait);
}
void TestFrame::SetChord(int key, bool value) {
diff --git a/init/keyword_map.h b/init/keyword_map.h
index c95fc73..7837bb3 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-#ifndef _INIT_KEYWORD_MAP_H_
-#define _INIT_KEYWORD_MAP_H_
+#pragma once
#include <map>
#include <string>
-#include <android-base/stringprintf.h>
-
#include "result.h"
namespace android {
@@ -37,8 +34,6 @@
}
const Result<Function> FindFunction(const std::vector<std::string>& args) const {
- using android::base::StringPrintf;
-
if (args.empty()) return Error() << "Keyword needed, but not provided";
auto& keyword = args[0];
@@ -46,7 +41,7 @@
auto function_info_it = map().find(keyword);
if (function_info_it == map().end()) {
- return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
+ return Errorf("Invalid keyword '{}'", keyword);
}
auto function_info = function_info_it->second;
@@ -54,17 +49,17 @@
auto min_args = std::get<0>(function_info);
auto max_args = std::get<1>(function_info);
if (min_args == max_args && num_args != min_args) {
- return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
- (min_args > 1 || min_args == 0) ? "s" : "");
+ return Errorf("{} requires {} argument{}", keyword, min_args,
+ (min_args > 1 || min_args == 0) ? "s" : "");
}
if (num_args < min_args || num_args > max_args) {
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
- return Error() << StringPrintf("%s requires at least %zu argument%s",
- keyword.c_str(), min_args, min_args > 1 ? "s" : "");
+ return Errorf("{} requires at least {} argument{}", keyword, min_args,
+ min_args > 1 ? "s" : "");
} else {
- return Error() << StringPrintf("%s requires between %zu and %zu arguments",
- keyword.c_str(), min_args, max_args);
+ return Errorf("{} requires between {} and {} arguments", keyword, min_args,
+ max_args);
}
}
@@ -79,5 +74,3 @@
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index c61c210..07b05d8 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -16,147 +16,20 @@
#include "modalias_handler.h"
-#include <fnmatch.h>
-#include <sys/syscall.h>
-
-#include <algorithm>
-#include <functional>
#include <string>
#include <vector>
-#include <android-base/chrono_utils.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-
-#include "parser.h"
+#include <modprobe/modprobe.h>
namespace android {
namespace init {
-Result<Success> ModaliasHandler::ParseDepCallback(std::vector<std::string>&& args) {
- std::vector<std::string> deps;
-
- // Set first item as our modules path
- std::string::size_type pos = args[0].find(':');
- if (pos != std::string::npos) {
- deps.emplace_back(args[0].substr(0, pos));
- } else {
- return Error() << "dependency lines must start with name followed by ':'";
- }
-
- // Remaining items are dependencies of our module
- for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
- deps.push_back(*arg);
- }
-
- // Key is striped module name to match names in alias file
- std::size_t start = args[0].find_last_of('/');
- std::size_t end = args[0].find(".ko:");
- if ((end - start) <= 1) return Error() << "malformed dependency line";
- auto mod_name = args[0].substr(start + 1, (end - start) - 1);
- // module names can have '-', but their file names will have '_'
- std::replace(mod_name.begin(), mod_name.end(), '-', '_');
- this->module_deps_[mod_name] = deps;
-
- return Success();
-}
-
-Result<Success> ModaliasHandler::ParseAliasCallback(std::vector<std::string>&& args) {
- auto it = args.begin();
- const std::string& type = *it++;
-
- if (type != "alias") {
- return Error() << "we only handle alias lines, got: " << type;
- }
-
- if (args.size() != 3) {
- return Error() << "alias lines must have 3 entries";
- }
-
- std::string& alias = *it++;
- std::string& module_name = *it++;
- this->module_aliases_.emplace_back(alias, module_name);
-
- return Success();
-}
-
-ModaliasHandler::ModaliasHandler() {
- using namespace std::placeholders;
-
- static const std::string base_paths[] = {
- "/vendor/lib/modules/",
- "/lib/modules/",
- "/odm/lib/modules/",
- };
-
- Parser alias_parser;
- auto alias_callback = std::bind(&ModaliasHandler::ParseAliasCallback, this, _1);
- alias_parser.AddSingleLineParser("alias", alias_callback);
- for (const auto& base_path : base_paths) alias_parser.ParseConfig(base_path + "modules.alias");
-
- Parser dep_parser;
- auto dep_callback = std::bind(&ModaliasHandler::ParseDepCallback, this, _1);
- dep_parser.AddSingleLineParser("", dep_callback);
- for (const auto& base_path : base_paths) dep_parser.ParseConfig(base_path + "modules.dep");
-}
-
-Result<Success> ModaliasHandler::Insmod(const std::string& path_name, const std::string& args) {
- base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
- if (fd == -1) return ErrnoError() << "Could not open module '" << path_name << "'";
-
- int ret = syscall(__NR_finit_module, fd.get(), args.c_str(), 0);
- if (ret != 0) {
- if (errno == EEXIST) {
- // Module already loaded
- return Success();
- }
- return ErrnoError() << "Failed to insmod '" << path_name << "' with args '" << args << "'";
- }
-
- LOG(INFO) << "Loaded kernel module " << path_name;
- return Success();
-}
-
-Result<Success> ModaliasHandler::InsmodWithDeps(const std::string& module_name,
- const std::string& args) {
- if (module_name.empty()) {
- return Error() << "Need valid module name";
- }
-
- auto it = module_deps_.find(module_name);
- if (it == module_deps_.end()) {
- return Error() << "Module '" << module_name << "' not in dependency file";
- }
- auto& dependencies = it->second;
-
- // load module dependencies in reverse order
- for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
- if (auto result = Insmod(*dep, ""); !result) return result;
- }
-
- // load target module itself with args
- return Insmod(dependencies[0], args);
-}
+ModaliasHandler::ModaliasHandler(const std::vector<std::string>& base_paths)
+ : modprobe_(base_paths) {}
void ModaliasHandler::HandleUevent(const Uevent& uevent) {
if (uevent.modalias.empty()) return;
-
- for (const auto& [alias, module] : module_aliases_) {
- if (fnmatch(alias.c_str(), uevent.modalias.c_str(), 0) != 0) continue; // Keep looking
-
- LOG(DEBUG) << "Loading kernel module '" << module << "' for alias '" << uevent.modalias
- << "'";
-
- if (auto result = InsmodWithDeps(module, ""); !result) {
- LOG(ERROR) << "Cannot load module: " << result.error();
- // try another one since there may be another match
- continue;
- }
-
- // loading was successful
- return;
- }
+ modprobe_.LoadWithAliases(uevent.modalias, true);
}
} // namespace init
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
index 3247c86..ce89a05 100644
--- a/init/modalias_handler.h
+++ b/init/modalias_handler.h
@@ -17,10 +17,10 @@
#pragma once
#include <string>
-#include <unordered_map>
#include <vector>
-#include "result.h"
+#include <modprobe/modprobe.h>
+
#include "uevent.h"
#include "uevent_handler.h"
@@ -29,20 +29,13 @@
class ModaliasHandler : public UeventHandler {
public:
- ModaliasHandler();
+ ModaliasHandler(const std::vector<std::string>&);
virtual ~ModaliasHandler() = default;
void HandleUevent(const Uevent& uevent) override;
private:
- Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
- Result<Success> Insmod(const std::string& path_name, const std::string& args);
-
- Result<Success> ParseDepCallback(std::vector<std::string>&& args);
- Result<Success> ParseAliasCallback(std::vector<std::string>&& args);
-
- std::vector<std::pair<std::string, std::string>> module_aliases_;
- std::unordered_map<std::string, std::vector<std::string>> module_deps_;
+ Modprobe modprobe_;
};
} // namespace init
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index c8f0e76..b0b63c5 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -121,7 +121,7 @@
}
MountHandler::~MountHandler() {
- if (fp_) epoll_->UnregisterHandler(fileno(fp_.get())).IgnoreError();
+ if (fp_) epoll_->UnregisterHandler(fileno(fp_.get()));
}
void MountHandler::MountHandlerFunction() {
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 5305dc7..12144c1 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -79,6 +79,38 @@
return updatable;
}
+static bool ActivateFlattenedApexesIfPossible() {
+ if (IsRecoveryMode() || IsApexUpdatable()) {
+ return true;
+ }
+
+ constexpr const char kSystemApex[] = "/system/apex";
+ constexpr const char kApexTop[] = "/apex";
+ if (mount(kSystemApex, kApexTop, nullptr, MS_BIND, nullptr) != 0) {
+ PLOG(ERROR) << "Could not bind mount " << kSystemApex << " to " << kApexTop;
+ return false;
+ }
+
+ // Special casing for the runtime APEX
+ constexpr const char kRuntimeApexMountPath[] = "/system/apex/com.android.runtime";
+ static const std::vector<std::string> kRuntimeApexDirNames = {"com.android.runtime.release",
+ "com.android.runtime.debug"};
+ bool success = false;
+ for (const auto& name : kRuntimeApexDirNames) {
+ std::string path = std::string(kSystemApex) + "/" + name;
+ if (access(path.c_str(), F_OK) == 0) {
+ if (mount(path.c_str(), kRuntimeApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
+ success = true;
+ break;
+ }
+ }
+ }
+ if (!success) {
+ PLOG(ERROR) << "Failed to bind mount the runtime APEX to " << kRuntimeApexMountPath;
+ }
+ return success;
+}
+
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -129,6 +161,8 @@
default_ns_id = GetMountNamespaceId();
}
+ success &= ActivateFlattenedApexesIfPossible();
+
LOG(INFO) << "SetupMountNamespaces done";
return success;
}
diff --git a/init/parser.h b/init/parser.h
index f30bda7..95b0cd7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,7 +27,7 @@
// SectionParser is an interface that can parse a given 'section' in init.
//
// You can implement up to 4 functions below, with ParseSection being mandatory. The first two
-// functions return Result<Success> indicating if they have an error. It will be reported along
+// functions return Result<void> indicating if they have an error. It will be reported along
// with the filename and line number of where the error occurred.
//
// 1) ParseSection
@@ -51,10 +51,10 @@
class SectionParser {
public:
virtual ~SectionParser() {}
- virtual Result<Success> ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) = 0;
- virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
- virtual Result<Success> EndSection() { return Success(); };
+ virtual Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) = 0;
+ virtual Result<void> ParseLineSection(std::vector<std::string>&&, int) { return {}; };
+ virtual Result<void> EndSection() { return {}; };
virtual void EndFile(){};
};
@@ -67,7 +67,7 @@
// Similar to ParseSection() and ParseLineSection(), this function returns bool with false
// indicating a failure and has an std::string* err parameter into which an error string can
// be written.
- using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
+ using LineCallback = std::function<Result<void>(std::vector<std::string>&&)>;
Parser();
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 21adce9..73787b9 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -169,7 +169,7 @@
return Error() << "Unable to parse persistent property file: Could not parse protobuf";
}
-Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
+Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
const std::string temp_filename = persistent_property_filename + ".tmp";
unique_fd fd(TEMP_FAILURE_RETRY(
open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
@@ -191,7 +191,7 @@
unlink(temp_filename.c_str());
return Error(saved_errno) << "Unable to rename persistent property file";
}
- return Success();
+ return {};
}
// Persistent properties are not written often, so we rather not keep any data in memory and read
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
index 5f4df85..3845a0d 100644
--- a/init/persistent_properties.h
+++ b/init/persistent_properties.h
@@ -30,7 +30,7 @@
// Exposed only for testing
Result<PersistentProperties> LoadPersistentPropertyFile();
-Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
+Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
extern std::string persistent_property_filename;
} // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index f2c7462..1520c9f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,9 +39,12 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
+#include <atomic>
#include <map>
#include <memory>
+#include <mutex>
#include <queue>
+#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -50,6 +53,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
@@ -57,7 +61,6 @@
#include <selinux/selinux.h>
#include "debug_ramdisk.h"
-#include "epoll.h"
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
@@ -74,6 +77,7 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
using android::properties::BuildTrie;
using android::properties::ParsePropertyInfoFile;
@@ -83,6 +87,8 @@
namespace android {
namespace init {
+static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+
static bool persistent_properties_loaded = false;
static int property_set_fd = -1;
@@ -100,7 +106,24 @@
const char* name;
};
+static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+ d->cr->gid);
+ return 0;
+}
+
void property_init() {
+ selinux_callback cb;
+ cb.func_audit = PropertyAuditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
if (__system_property_area_init()) {
@@ -187,88 +210,51 @@
return PROP_SUCCESS;
}
-typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+class AsyncRestorecon {
+ public:
+ void TriggerRestorecon(const std::string& path) {
+ auto guard = std::lock_guard{mutex_};
+ paths_.emplace(path);
-struct PropertyChildInfo {
- pid_t pid;
- PropertyAsyncFunc func;
- std::string name;
- std::string value;
+ if (!thread_started_) {
+ thread_started_ = true;
+ std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
+ }
+ }
+
+ private:
+ void ThreadFunction() {
+ auto lock = std::unique_lock{mutex_};
+
+ while (!paths_.empty()) {
+ auto path = paths_.front();
+ paths_.pop();
+
+ lock.unlock();
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
+ }
+ android::base::SetProperty(kRestoreconProperty, path);
+ lock.lock();
+ }
+
+ thread_started_ = false;
+ }
+
+ std::mutex mutex_;
+ std::queue<std::string> paths_;
+ bool thread_started_ = false;
};
-static std::queue<PropertyChildInfo> property_children;
-
-static void PropertyChildLaunch() {
- auto& info = property_children.front();
- pid_t pid = fork();
- if (pid < 0) {
- LOG(ERROR) << "Failed to fork for property_set_async";
- while (!property_children.empty()) {
- property_children.pop();
- }
- return;
- }
- if (pid != 0) {
- info.pid = pid;
- } else {
- if (info.func(info.name, info.value) != 0) {
- LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
- << "\") failed";
- }
- _exit(0);
- }
-}
-
-bool PropertyChildReap(pid_t pid) {
- if (property_children.empty()) {
- return false;
- }
- auto& info = property_children.front();
- if (info.pid != pid) {
- return false;
- }
- std::string error;
- if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
- LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
- << error;
- }
- property_children.pop();
- if (!property_children.empty()) {
- PropertyChildLaunch();
- }
- return true;
-}
-
-static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
- PropertyAsyncFunc func, std::string* error) {
- if (value.empty()) {
- return PropertySet(name, value, error);
- }
-
- PropertyChildInfo info;
- info.func = func;
- info.name = name;
- info.value = value;
- property_children.push(info);
- if (property_children.size() == 1) {
- PropertyChildLaunch();
- }
- return PROP_SUCCESS;
-}
-
-static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
- return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
if (StartsWith(name, "ctl.")) {
LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
"functions directly";
return PROP_ERROR_INVALID_NAME;
}
- if (name == "selinux.restorecon_recursive") {
- LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
- "restorecon builtin directly";
+ if (name == kRestoreconProperty) {
+ LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
+ << "' from init; use the restorecon builtin directly";
return PROP_ERROR_INVALID_NAME;
}
@@ -329,18 +315,20 @@
return result == sizeof(value);
}
+ bool GetSourceContext(std::string* source_context) const {
+ char* c_source_context = nullptr;
+ if (getpeercon(socket_, &c_source_context) != 0) {
+ return false;
+ }
+ *source_context = c_source_context;
+ freecon(c_source_context);
+ return true;
+ }
+
int socket() { return socket_; }
const ucred& cred() { return cred_; }
- std::string source_context() const {
- char* source_context = nullptr;
- getpeercon(socket_, &source_context);
- std::string result = source_context;
- freecon(source_context);
- return result;
- }
-
private:
bool PollIn(uint32_t* timeout_ms) {
struct pollfd ufds[1];
@@ -487,8 +475,9 @@
}
if (StartsWith(name, "ctl.")) {
- HandleControlMessage(name.c_str() + 4, value, cr.pid);
- return PROP_SUCCESS;
+ return HandleControlMessage(name.c_str() + 4, value, cr.pid)
+ ? PROP_SUCCESS
+ : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
// sys.powerctl is a special property that is used to make the device reboot. We want to log
@@ -506,8 +495,14 @@
<< process_log_string;
}
- if (name == "selinux.restorecon_recursive") {
- return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+ // If a process other than init is writing a non-empty value, it means that process is
+ // requesting that init performs a restorecon operation on the path specified by 'value'.
+ // We use a thread to do this restorecon operation to prevent holding up init, as it may take
+ // a long time to complete.
+ if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
+ static AsyncRestorecon async_restorecon;
+ async_restorecon.TriggerRestorecon(value);
+ return PROP_SUCCESS;
}
return PropertySet(name, value, error);
@@ -553,14 +548,18 @@
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
+ std::string source_context;
+ if (!socket.GetSourceContext(&source_context)) {
+ PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
+ return;
+ }
+
const auto& cr = socket.cred();
std::string error;
- uint32_t result =
- HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+ uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
break;
@@ -576,13 +575,19 @@
return;
}
+ std::string source_context;
+ if (!socket.GetSourceContext(&source_context)) {
+ PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
+ socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+ return;
+ }
+
const auto& cr = socket.cred();
std::string error;
- uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+ uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
socket.SendUint32(result);
break;
@@ -669,7 +674,7 @@
}
if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
- key == "selinux.restorecon_recursive"s) {
+ std::string{key} == kRestoreconProperty) {
LOG(ERROR) << "Ignoring disallowed property '" << key
<< "' with special meaning in prop file '" << filename << "'";
continue;
@@ -918,19 +923,6 @@
update_sys_usb_config();
}
-static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- auto* d = reinterpret_cast<PropertyAuditData*>(data);
-
- if (!d || !d->name || !d->cr) {
- LOG(ERROR) << "AuditCallback invoked with null data arguments!";
- return 0;
- }
-
- snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
- d->cr->gid);
- return 0;
-}
-
bool LoadPropertyInfoFromFile(const std::string& filename,
std::vector<PropertyInfoEntry>* property_infos) {
auto file_contents = std::string();
@@ -1001,10 +993,6 @@
}
void StartPropertyService(Epoll* epoll) {
- selinux_callback cb;
- cb.func_audit = SelinuxAuditCallback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
@@ -1020,5 +1008,42 @@
}
}
+Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
+ unique_fd reader;
+ unique_fd writer;
+ if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
+ return ErrnoError() << "Could not create socket pair";
+ }
+
+ int result = 0;
+ std::atomic<bool> end = false;
+ auto thread = std::thread{[&f, &result, &end, &writer] {
+ result = f();
+ end = true;
+ send(writer, "1", 1, 0);
+ }};
+
+ Epoll epoll;
+ if (auto result = epoll.Open(); !result) {
+ return Error() << "Could not create epoll: " << result.error();
+ }
+ if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+ return Error() << "Could not register epoll handler for property fd: " << result.error();
+ }
+
+ // No-op function, just used to break from loop.
+ if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
+ return Error() << "Could not register epoll handler for ending thread:" << result.error();
+ }
+
+ while (!end) {
+ epoll.Wait({});
+ }
+
+ thread.join();
+
+ return result;
+}
+
} // namespace init
} // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index 85e7bc0..dc47b4d 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#ifndef _INIT_PROPERTY_H
-#define _INIT_PROPERTY_H
+#pragma once
#include <sys/socket.h>
+#include <functional>
#include <string>
#include "epoll.h"
+#include "result.h"
namespace android {
namespace init {
@@ -33,15 +34,18 @@
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error);
-extern bool PropertyChildReap(pid_t pid);
-
-void property_init(void);
-void property_load_boot_defaults(bool);
-void load_persist_props(void);
-void load_system_props(void);
+void property_init();
+void property_load_boot_defaults(bool load_debug_prop);
+void load_persist_props();
void StartPropertyService(Epoll* epoll);
+template <typename F, typename... Args>
+Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
+ Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
+
+ auto func = [&] { return f(args...); };
+ return CallFunctionAndHandlePropertiesImpl(func);
+}
+
} // namespace init
} // namespace android
-
-#endif /* _INIT_PROPERTY_H */
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 5b90969..eaba3cc 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -19,13 +19,14 @@
#include <dirent.h>
#include <fcntl.h>
#include <linux/fs.h>
-#include <mntent.h>
#include <linux/loop.h>
+#include <mntent.h>
+#include <semaphore.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
-#include <sys/swap.h>
#include <sys/stat.h>
+#include <sys/swap.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -40,7 +41,6 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
@@ -57,11 +57,13 @@
#include "service.h"
#include "sigchld_handler.h"
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
using android::base::GetBoolProperty;
using android::base::Split;
-using android::base::StringPrintf;
using android::base::Timer;
using android::base::unique_fd;
+using android::base::WriteStringToFile;
namespace android {
namespace init {
@@ -207,8 +209,8 @@
}
FindPartitionsToUmount(nullptr, nullptr, true);
// dump current CPU stack traces and uninterruptible tasks
- android::base::WriteStringToFile("l", "/proc/sysrq-trigger");
- android::base::WriteStringToFile("w", "/proc/sysrq-trigger");
+ WriteStringToFile("l", PROC_SYSRQ);
+ WriteStringToFile("w", PROC_SYSRQ);
}
static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -248,7 +250,91 @@
}
}
-static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
+static void KillAllProcesses() {
+ WriteStringToFile("i", PROC_SYSRQ);
+}
+
+// Create reboot/shutdwon monitor thread
+void RebootMonitorThread(unsigned int cmd, const std::string& rebootTarget, sem_t* reboot_semaphore,
+ std::chrono::milliseconds shutdown_timeout, bool* reboot_monitor_run) {
+ unsigned int remaining_shutdown_time = 0;
+
+ // 30 seconds more than the timeout passed to the thread as there is a final Umount pass
+ // after the timeout is reached.
+ constexpr unsigned int shutdown_watchdog_timeout_default = 30;
+ auto shutdown_watchdog_timeout = android::base::GetUintProperty(
+ "ro.build.shutdown.watchdog.timeout", shutdown_watchdog_timeout_default);
+ remaining_shutdown_time = shutdown_watchdog_timeout + shutdown_timeout.count() / 1000;
+
+ while (*reboot_monitor_run == true) {
+ if (TEMP_FAILURE_RETRY(sem_wait(reboot_semaphore)) == -1) {
+ LOG(ERROR) << "sem_wait failed and exit RebootMonitorThread()";
+ return;
+ }
+
+ timespec shutdown_timeout_timespec;
+ if (clock_gettime(CLOCK_MONOTONIC, &shutdown_timeout_timespec) == -1) {
+ LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+ return;
+ }
+
+ // If there are some remaining shutdown time left from previous round, we use
+ // remaining time here.
+ shutdown_timeout_timespec.tv_sec += remaining_shutdown_time;
+
+ LOG(INFO) << "shutdown_timeout_timespec.tv_sec: " << shutdown_timeout_timespec.tv_sec;
+
+ int sem_return = 0;
+ while ((sem_return = sem_timedwait_monotonic_np(reboot_semaphore,
+ &shutdown_timeout_timespec)) == -1 &&
+ errno == EINTR) {
+ }
+
+ if (sem_return == -1) {
+ LOG(ERROR) << "Reboot thread timed out";
+
+ if (android::base::GetBoolProperty("ro.debuggable", false) == true) {
+ LOG(INFO) << "Try to dump init process call trace:";
+ const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
+ int status;
+ android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true,
+ LOG_KLOG, true, nullptr, nullptr, 0);
+
+ LOG(INFO) << "Show stack for all active CPU:";
+ WriteStringToFile("l", PROC_SYSRQ);
+
+ LOG(INFO) << "Show tasks that are in disk sleep(uninterruptable sleep), which are "
+ "like "
+ "blocked in mutex or hardware register access:";
+ WriteStringToFile("w", PROC_SYSRQ);
+ }
+
+ // In shutdown case,notify kernel to sync and umount fs to read-only before shutdown.
+ if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
+ WriteStringToFile("s", PROC_SYSRQ);
+
+ WriteStringToFile("u", PROC_SYSRQ);
+
+ RebootSystem(cmd, rebootTarget);
+ }
+
+ LOG(ERROR) << "Trigger crash at last!";
+ WriteStringToFile("c", PROC_SYSRQ);
+ } else {
+ timespec current_time_timespec;
+
+ if (clock_gettime(CLOCK_MONOTONIC, ¤t_time_timespec) == -1) {
+ LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+ return;
+ }
+
+ remaining_shutdown_time =
+ shutdown_timeout_timespec.tv_sec - current_time_timespec.tv_sec;
+
+ LOG(INFO) << "remaining_shutdown_time: " << remaining_shutdown_time;
+ }
+ }
+}
/* Try umounting all emulated file systems R/W block device cfile systems.
* This will just try umount and give it up if it fails.
@@ -259,7 +345,8 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
+static UmountStat TryUmountAndFsck(unsigned int cmd, const std::string& rebootTarget, bool runFsck,
+ std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
Timer t;
std::vector<MountEntry> block_devices;
std::vector<MountEntry> emulated_devices;
@@ -279,11 +366,17 @@
}
if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+ LOG(INFO) << "Pause reboot monitor thread before fsck";
+ sem_post(reboot_semaphore);
+
// fsck part is excluded from timeout check. It only runs for user initiated shutdown
// and should not affect reboot time.
for (auto& entry : block_devices) {
entry.DoFsck();
}
+
+ LOG(INFO) << "Resume reboot monitor thread after fsck";
+ sem_post(reboot_semaphore);
}
return stat;
}
@@ -311,7 +404,7 @@
}
LOG(INFO) << "swapoff() took " << swap_timer;;
- if (!android::base::WriteStringToFile("1", ZRAM_RESET)) {
+ if (!WriteStringToFile("1", ZRAM_RESET)) {
LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
return;
}
@@ -369,6 +462,23 @@
}
LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
+ sem_t reboot_semaphore;
+ if (sem_init(&reboot_semaphore, false, 0) == -1) {
+ // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
+ LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
+ RebootSystem(cmd, rebootTarget);
+ }
+
+ // Start a thread to monitor init shutdown process
+ LOG(INFO) << "Create reboot monitor thread.";
+ bool reboot_monitor_run = true;
+ std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, rebootTarget, &reboot_semaphore,
+ shutdown_timeout, &reboot_monitor_run);
+ reboot_monitor_thread.detach();
+
+ // Start reboot monitor thread
+ sem_post(&reboot_semaphore);
+
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
@@ -415,13 +525,13 @@
// start all animation classes if stopped.
if (do_shutdown_animation) {
- service->Start().IgnoreError();
+ service->Start();
}
service->SetShutdownCritical(); // will not check animation class separately
}
if (do_shutdown_animation) {
- bootAnim->Start().IgnoreError();
+ bootAnim->Start();
surfaceFlinger->SetShutdownCritical();
bootAnim->SetShutdownCritical();
}
@@ -497,7 +607,8 @@
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice();
- UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
+ UmountStat stat = TryUmountAndFsck(cmd, rebootTarget, runFsck, shutdown_timeout - t.duration(),
+ &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
{
Timer sync_timer;
@@ -507,6 +618,11 @@
}
if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
+
+ // Send signal to terminate reboot monitor thread.
+ reboot_monitor_run = false;
+ sem_post(&reboot_semaphore);
+
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
abort();
@@ -589,7 +705,7 @@
// Queue built-in shutdown_done
auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
DoReboot(cmd, command, reboot_target, run_fsck);
- return Success();
+ return Result<void>{};
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
diff --git a/init/result.h b/init/result.h
index 0e3fd3d..b70dd1b 100644
--- a/init/result.h
+++ b/init/result.h
@@ -14,200 +14,16 @@
* limitations under the License.
*/
-// This file contains classes for returning a successful result along with an optional
-// arbitrarily typed return value or for returning a failure result along with an optional string
-// indicating why the function failed.
+#pragma once
-// There are 3 classes that implement this functionality and one additional helper type.
-//
-// Result<T> either contains a member of type T that can be accessed using similar semantics as
-// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
-// Result<T>::error().
-//
-// ResultError is a type that contains both a std::string describing the error and a copy of errno
-// from when the error occurred. ResultError can be used in an ostream directly to print its
-// string value.
-//
-// Success is a typedef that aids in creating Result<T> that do not contain a return value.
-// Result<Success> is the correct return type for a function that either returns successfully or
-// returns an error value. Returning Success() from a function that returns Result<Success> is the
-// correct way to indicate that a function without a return type has completed successfully.
-//
-// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
-// to T or from the constructor arguments for T. This allows you to return a type T directly from
-// a function that returns Result<T>.
-//
-// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
-// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
-// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
-// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
-// value can be directly specified via the Error() constructor.
-//
-// ResultError can be used in the ostream when using Error to construct a Result<T>. In this case,
-// the string that the ResultError takes is passed through the stream normally, but the errno is
-// passed to the Result<T>. This can be used to pass errno from a failing C function up multiple
-// callers.
-//
-// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
-// function that return Result<T> but you have a Result<U> and want to return its error. In this
-// case, you can return the .error() from the Result<U> to construct the Result<T>.
+// The implementation of this file has moved to android-base. This file remains since historically,
+// these classes were a part of init.
-// An example of how to use these is below:
-// Result<U> CalculateResult(const T& input) {
-// U output;
-// if (!SomeOtherCppFunction(input, &output)) {
-// return Error() << "SomeOtherCppFunction(" << input << ") failed";
-// }
-// if (!c_api_function(output)) {
-// return ErrnoError() << "c_api_function(" << output << ") failed";
-// }
-// return output;
-// }
-//
-// auto output = CalculateResult(input);
-// if (!output) return Error() << "CalculateResult failed: " << output.error();
-// UseOutput(*output);
+#include <android-base/result.h>
-#ifndef _INIT_RESULT_H
-#define _INIT_RESULT_H
-
-#include <errno.h>
-
-#include <sstream>
-#include <string>
-#include <variant>
-
-namespace android {
-namespace init {
-
-struct ResultError {
- template <typename T>
- ResultError(T&& error_string, int error_errno)
- : error_string(std::forward<T>(error_string)), error_errno(error_errno) {}
-
- std::string error_string;
- int error_errno;
-};
-
-inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
- os << t.error_string;
- return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, ResultError&& t) {
- os << std::move(t.error_string);
- return os;
-}
-
-class Error {
- public:
- Error() : errno_(0), append_errno_(false) {}
- Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
-
- template <typename T>
- Error&& operator<<(T&& t) {
- ss_ << std::forward<T>(t);
- return std::move(*this);
- }
-
- Error&& operator<<(const ResultError& result_error) {
- ss_ << result_error.error_string;
- errno_ = result_error.error_errno;
- return std::move(*this);
- }
-
- Error&& operator<<(ResultError&& result_error) {
- ss_ << std::move(result_error.error_string);
- errno_ = result_error.error_errno;
- return std::move(*this);
- }
-
- const std::string str() const {
- std::string str = ss_.str();
- if (append_errno_) {
- if (str.empty()) {
- return strerror(errno_);
- }
- return str + ": " + strerror(errno_);
- }
- return str;
- }
-
- int get_errno() const { return errno_; }
-
- Error(const Error&) = delete;
- Error(Error&&) = delete;
- Error& operator=(const Error&) = delete;
- Error& operator=(Error&&) = delete;
-
- private:
- std::stringstream ss_;
- int errno_;
- bool append_errno_;
-};
-
-inline Error ErrnoError() {
- return Error(errno);
-}
-
-template <typename T>
-class [[nodiscard]] Result {
- public:
- Result() {}
-
- template <typename U, typename... V,
- typename = std::enable_if_t<!(std::is_same_v<std::decay_t<U>, Result<T>> &&
- sizeof...(V) == 0)>>
- Result(U&& result, V&&... results)
- : contents_(std::in_place_index_t<0>(), std::forward<U>(result),
- std::forward<V>(results)...) {}
-
- Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
- Result(const ResultError& result_error)
- : contents_(std::in_place_index_t<1>(), result_error.error_string,
- result_error.error_errno) {}
- Result(ResultError&& result_error)
- : contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
- result_error.error_errno) {}
-
- void IgnoreError() const {}
-
- bool has_value() const { return contents_.index() == 0; }
-
- T& value() & { return std::get<0>(contents_); }
- const T& value() const & { return std::get<0>(contents_); }
- T&& value() && { return std::get<0>(std::move(contents_)); }
- const T&& value() const && { return std::get<0>(std::move(contents_)); }
-
- const ResultError& error() const & { return std::get<1>(contents_); }
- ResultError&& error() && { return std::get<1>(std::move(contents_)); }
- const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
-
- const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
- std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
- const std::string&& error_string() const && {
- return std::get<1>(std::move(contents_)).error_string;
- }
-
- int error_errno() const { return std::get<1>(contents_).error_errno; }
-
- explicit operator bool() const { return has_value(); }
-
- T& operator*() & { return value(); }
- const T& operator*() const & { return value(); }
- T&& operator*() && { return std::move(value()); }
- const T&& operator*() const && { return std::move(value()); }
-
- T* operator->() { return &value(); }
- const T* operator->() const { return &value(); }
-
- private:
- std::variant<T, ResultError> contents_;
-};
-
-using Success = std::monostate;
-
-} // namespace init
-} // namespace android
-
-#endif
+using android::base::ErrnoError;
+using android::base::ErrnoErrorf;
+using android::base::Error;
+using android::base::Errorf;
+using android::base::Result;
+using android::base::ResultError;
diff --git a/init/result_test.cpp b/init/result_test.cpp
deleted file mode 100644
index 327b444..0000000
--- a/init/result_test.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2017 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 "result.h"
-
-#include "errno.h"
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-using namespace std::string_literals;
-
-namespace android {
-namespace init {
-
-TEST(result, result_accessors) {
- Result<std::string> result = "success";
- ASSERT_TRUE(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("success", *result);
- EXPECT_EQ("success", result.value());
-
- EXPECT_EQ('s', result->data()[0]);
-}
-
-TEST(result, result_accessors_rvalue) {
- ASSERT_TRUE(Result<std::string>("success"));
- ASSERT_TRUE(Result<std::string>("success").has_value());
-
- EXPECT_EQ("success", *Result<std::string>("success"));
- EXPECT_EQ("success", Result<std::string>("success").value());
-
- EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
-}
-
-TEST(result, result_success) {
- Result<Success> result = Success();
- ASSERT_TRUE(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ(Success(), *result);
- EXPECT_EQ(Success(), result.value());
-}
-
-TEST(result, result_success_rvalue) {
- // Success() doesn't actually create a Result<Success> object, but rather an object that can be
- // implicitly constructed into a Result<Success> object.
-
- auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
- ASSERT_TRUE(MakeRvalueSuccessResult());
- ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
-
- EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
- EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
-}
-
-TEST(result, result_error) {
- Result<Success> result = Error() << "failure" << 1;
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ("failure1", result.error_string());
-}
-
-TEST(result, result_error_empty) {
- Result<Success> result = Error();
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ("", result.error_string());
-}
-
-TEST(result, result_error_rvalue) {
- // Error() and ErrnoError() aren't actually used to create a Result<T> object.
- // Under the hood, they are an intermediate class that can be implicitly constructed into a
- // Result<T>. This is needed both to create the ostream and because Error() itself, by
- // definition will not know what the type, T, of the underlying Result<T> object that it would
- // create is.
-
- auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
- ASSERT_FALSE(MakeRvalueErrorResult());
- ASSERT_FALSE(MakeRvalueErrorResult().has_value());
-
- EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
- EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
-}
-
-TEST(result, result_errno_error) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<Success> result = ErrnoError() << "failure" << 1;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error_errno());
- EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
-}
-
-TEST(result, result_errno_error_no_text) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<Success> result = ErrnoError();
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error_errno());
- EXPECT_EQ(strerror(test_errno), result.error_string());
-}
-
-TEST(result, result_error_from_other_result) {
- auto error_text = "test error"s;
- Result<Success> result = Error() << error_text;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = result.error();
-
- ASSERT_FALSE(result2);
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ(error_text, result.error_string());
-}
-
-TEST(result, result_error_through_ostream) {
- auto error_text = "test error"s;
- Result<Success> result = Error() << error_text;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2);
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ(error_text, result.error_string());
-}
-
-TEST(result, result_errno_error_through_ostream) {
- auto error_text = "test error"s;
- constexpr int test_errno = 6;
- errno = 6;
- Result<Success> result = ErrnoError() << error_text;
-
- errno = 0;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2);
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(test_errno, result.error_errno());
- EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
-}
-
-TEST(result, constructor_forwarding) {
- auto result = Result<std::string>(5, 'a');
-
- ASSERT_TRUE(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("aaaaa", *result);
-}
-
-struct ConstructorTracker {
- static size_t constructor_called;
- static size_t copy_constructor_called;
- static size_t move_constructor_called;
- static size_t copy_assignment_called;
- static size_t move_assignment_called;
-
- template <typename T>
- ConstructorTracker(T&& string) : string(string) {
- ++constructor_called;
- }
-
- ConstructorTracker(const ConstructorTracker& ct) {
- ++copy_constructor_called;
- string = ct.string;
- }
- ConstructorTracker(ConstructorTracker&& ct) noexcept {
- ++move_constructor_called;
- string = std::move(ct.string);
- }
- ConstructorTracker& operator=(const ConstructorTracker& ct) {
- ++copy_assignment_called;
- string = ct.string;
- return *this;
- }
- ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
- ++move_assignment_called;
- string = std::move(ct.string);
- return *this;
- }
-
- std::string string;
-};
-
-size_t ConstructorTracker::constructor_called = 0;
-size_t ConstructorTracker::copy_constructor_called = 0;
-size_t ConstructorTracker::move_constructor_called = 0;
-size_t ConstructorTracker::copy_assignment_called = 0;
-size_t ConstructorTracker::move_assignment_called = 0;
-
-Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
- if (in.empty()) {
- return "literal string";
- }
- if (in == "test2") {
- return ConstructorTracker(in + in + "2");
- }
- ConstructorTracker result(in + " " + in);
- return result;
-};
-
-TEST(result, no_copy_on_return) {
- // If returning parameters that may be used to implicitly construct the type T of Result<T>,
- // then those parameters are forwarded to the construction of Result<T>.
-
- // If returning an prvalue or xvalue, it will be move constructed during the construction of
- // Result<T>.
-
- // This check ensures that that is the case, and particularly that no copy constructors
- // are called.
-
- auto result1 = ReturnConstructorTracker("");
- ASSERT_TRUE(result1);
- EXPECT_EQ("literal string", result1->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result2 = ReturnConstructorTracker("test2");
- ASSERT_TRUE(result2);
- EXPECT_EQ("test2test22", result2->string);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result3 = ReturnConstructorTracker("test3");
- ASSERT_TRUE(result3);
- EXPECT_EQ("test3 test3", result3->string);
- EXPECT_EQ(3U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-}
-
-// Below two tests require that we do not hide the move constructor with our forwarding reference
-// constructor. This is done with by disabling the forwarding reference constructor if its first
-// and only type is Result<T>.
-TEST(result, result_result_with_success) {
- auto return_result_result_with_success = []() -> Result<Result<Success>> {
- return Result<Success>();
- };
- auto result = return_result_result_with_success();
- ASSERT_TRUE(result);
- ASSERT_TRUE(*result);
-
- auto inner_result = result.value();
- ASSERT_TRUE(inner_result);
-}
-
-TEST(result, result_result_with_failure) {
- auto return_result_result_with_error = []() -> Result<Result<Success>> {
- return Result<Success>(ResultError("failure string", 6));
- };
- auto result = return_result_result_with_error();
- ASSERT_TRUE(result);
- ASSERT_FALSE(*result);
- EXPECT_EQ("failure string", result->error_string());
- EXPECT_EQ(6, result->error_errno());
-}
-
-// This test requires that we disable the forwarding reference constructor if Result<T> is the
-// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
-// construct a Result<T>, then we still need the constructor.
-TEST(result, result_two_parameter_constructor_same_type) {
- struct TestStruct {
- TestStruct(int value) : value_(value) {}
- TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
- int value_;
- };
-
- auto return_test_struct = []() -> Result<TestStruct> { return {Result<TestStruct>(6), 6}; };
-
- auto result = return_test_struct();
- ASSERT_TRUE(result);
- EXPECT_EQ(36, result->value_);
-}
-
-TEST(result, die_on_access_failed_result) {
- Result<std::string> result = Error();
- ASSERT_DEATH(*result, "");
-}
-
-TEST(result, die_on_get_error_succesful_result) {
- Result<std::string> result = "success";
- ASSERT_DEATH(result.error_string(), "");
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
index 1e0754a..476a46a 100644
--- a/init/rlimit_parser.cpp
+++ b/init/rlimit_parser.cpp
@@ -77,7 +77,7 @@
return Error() << "Could not parse hard limit '" << args[3] << "'";
}
- return {resource, limit};
+ return std::pair{resource, limit};
}
} // namespace init
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index 659ba8a..6a16d3b 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -43,8 +43,8 @@
auto result = ParseRlimit(input);
ASSERT_FALSE(result) << "input: " << input[1];
- EXPECT_EQ(expected_result, result.error_string());
- EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ(expected_result, result.error().message());
+ EXPECT_EQ(0, result.error().code());
}
TEST(rlimit, RlimitSuccess) {
diff --git a/init/security.cpp b/init/security.cpp
index a3494a2..5d87f3c 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -43,14 +43,14 @@
// devices/configurations where these I/O operations are blocking for a long
// time. We do not reboot or halt on failures, as this is a best-effort
// attempt.
-Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
+Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
unique_fd hwrandom_fd(
TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (hwrandom_fd == -1) {
if (errno == ENOENT) {
LOG(INFO) << "/dev/hw_random not found";
// It's not an error to not have a Hardware RNG.
- return Success();
+ return {};
}
return ErrnoError() << "Failed to open /dev/hw_random";
}
@@ -80,7 +80,7 @@
}
LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
- return Success();
+ return {};
}
static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
@@ -147,31 +147,31 @@
// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
// ec9ee4acd97c drivers: char: random: add get_random_long()
// 5ef11c35ce86 mm: ASLR: use get_random_long()
-Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
+Result<void> SetMmapRndBitsAction(const BuiltinArguments&) {
// values are arch-dependent
#if defined(USER_MODE_LINUX)
// uml does not support mmap_rnd_bits
- return Success();
+ return {};
#elif defined(__aarch64__)
// arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
- return Success();
+ return {};
}
#elif defined(__x86_64__)
// x86_64 supports 28 - 32 bits
if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
- return Success();
+ return {};
}
#elif defined(__arm__) || defined(__i386__)
// check to see if we're running on 64-bit kernel
bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
// supported 32-bit architecture must have 16 bits set
if (SetMmapRndBitsMin(16, 16, h64)) {
- return Success();
+ return {};
}
#elif defined(__mips__) || defined(__mips64__)
// TODO: add mips support b/27788820
- return Success();
+ return {};
#else
LOG(ERROR) << "Unknown architecture";
#endif
@@ -187,14 +187,14 @@
// Set kptr_restrict to the highest available level.
//
// Aborts if unable to set this to an acceptable value.
-Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
+Result<void> SetKptrRestrictAction(const BuiltinArguments&) {
std::string path = KPTR_RESTRICT_PATH;
if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
LOG(FATAL) << "Unable to set adequate kptr_restrict value!";
return Error();
}
- return Success();
+ return {};
}
} // namespace init
diff --git a/init/security.h b/init/security.h
index 6f6b944..b081a05 100644
--- a/init/security.h
+++ b/init/security.h
@@ -26,9 +26,9 @@
namespace android {
namespace init {
-Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
-Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
-Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
+Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<void> SetKptrRestrictAction(const BuiltinArguments&);
} // namespace init
} // namespace android
diff --git a/init/selabel.cpp b/init/selabel.cpp
new file mode 100644
index 0000000..daeb832
--- /dev/null
+++ b/init/selabel.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "selabel.h"
+
+#include <selinux/android.h>
+
+namespace android {
+namespace init {
+
+namespace {
+
+selabel_handle* sehandle = nullptr;
+}
+
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ char* context;
+ if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ std::vector<const char*> c_aliases;
+ for (const auto& alias : aliases) {
+ c_aliases.emplace_back(alias.c_str());
+ }
+ c_aliases.emplace_back(nullptr);
+
+ char* context;
+ if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/selabel.h b/init/selabel.h
new file mode 100644
index 0000000..5d590b2
--- /dev/null
+++ b/init/selabel.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result);
+
+} // namespace init
+} // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 86238b4..54be086 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -79,8 +79,6 @@
namespace {
-selabel_handle* sehandle = nullptr;
-
enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
EnforcingStatus StatusFromCmdline() {
@@ -330,6 +328,12 @@
}
std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+ std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +
+ ".compat.cil");
+ if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {
+ plat_compat_cil_file.clear();
+ }
+
std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
product_policy_cil_file.clear();
@@ -375,6 +379,9 @@
};
// clang-format on
+ if (!plat_compat_cil_file.empty()) {
+ compile_args.push_back(plat_compat_cil_file.c_str());
+ }
if (!product_policy_cil_file.empty()) {
compile_args.push_back(product_policy_cil_file.c_str());
}
@@ -421,8 +428,6 @@
}
void SelinuxInitialize() {
- Timer t;
-
LOG(INFO) << "Loading SELinux policy";
if (!LoadPolicy()) {
LOG(FATAL) << "Unable to load SELinux policy";
@@ -439,9 +444,6 @@
if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
}
-
- // init's first stage can't set properties, so pass the time to the second stage.
- setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
} // namespace
@@ -523,6 +525,8 @@
InstallRebootSignalHandlers();
}
+ boot_clock::time_point start_time = boot_clock::now();
+
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
SelinuxInitialize();
@@ -535,6 +539,8 @@
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
+ setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
+
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
@@ -546,54 +552,5 @@
return 1;
}
-// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
-// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
-// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
-// one, thus eliminating an extra call to selinux_android_file_context_handle().
-void SelabelInitialize() {
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
-}
-
-// A C++ wrapper around selabel_lookup() using the cached sehandle.
-// If sehandle is null, this returns success with an empty context.
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
- result->clear();
-
- if (!sehandle) return true;
-
- char* context;
- if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
- return false;
- }
- *result = context;
- free(context);
- return true;
-}
-
-// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
-// If sehandle is null, this returns success with an empty context.
-bool SelabelLookupFileContextBestMatch(const std::string& key,
- const std::vector<std::string>& aliases, int type,
- std::string* result) {
- result->clear();
-
- if (!sehandle) return true;
-
- std::vector<const char*> c_aliases;
- for (const auto& alias : aliases) {
- c_aliases.emplace_back(alias.c_str());
- }
- c_aliases.emplace_back(nullptr);
-
- char* context;
- if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
- return false;
- }
- *result = context;
- free(context);
- return true;
-}
-
} // namespace init
} // namespace android
diff --git a/init/selinux.h b/init/selinux.h
index 3aa9406..63ad470 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-#ifndef _INIT_SELINUX_H
-#define _INIT_SELINUX_H
-
-#include <string>
-#include <vector>
+#pragma once
namespace android {
namespace init {
@@ -29,13 +25,7 @@
void SelinuxSetupKernelLogging();
int SelinuxGetVendorAndroidVersion();
-void SelabelInitialize();
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
-bool SelabelLookupFileContextBestMatch(const std::string& key,
- const std::vector<std::string>& aliases, int type,
- std::string* result);
+static constexpr char kEnvSelinuxStartedAt[] = "SELINUX_STARTED_AT";
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/service.cpp b/init/service.cpp
index ccc37b7..4fe374c 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -21,12 +21,10 @@
#include <linux/input.h>
#include <linux/securebits.h>
#include <sched.h>
-#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
-#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
@@ -36,7 +34,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
@@ -46,6 +43,7 @@
#include "util.h"
#if defined(__ANDROID__)
+#include <ApexProperties.sysprop.h>
#include <android/api-level.h>
#include <sys/system_properties.h>
@@ -64,7 +62,6 @@
using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
-using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
@@ -106,87 +103,6 @@
return computed_context;
}
-Result<Success> Service::SetUpMountNamespace() const {
- constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
-
- // Recursively remount / as slave like zygote does so unmounting and mounting /proc
- // doesn't interfere with the parent namespace's /proc mount. This will also
- // prevent any other mounts/unmounts initiated by the service from interfering
- // with the parent namespace but will still allow mount events from the parent
- // namespace to propagate to the child.
- if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- return ErrnoError() << "Could not remount(/) recursively as slave";
- }
-
- // umount() then mount() /proc and/or /sys
- // Note that it is not sufficient to mount with MS_REMOUNT.
- if (namespace_flags_ & CLONE_NEWPID) {
- if (umount("/proc") == -1) {
- return ErrnoError() << "Could not umount(/proc)";
- }
- if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
- return ErrnoError() << "Could not mount(/proc)";
- }
- }
- bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
- [](const auto& entry) { return entry.first == CLONE_NEWNET; });
- if (remount_sys) {
- if (umount2("/sys", MNT_DETACH) == -1) {
- return ErrnoError() << "Could not umount(/sys)";
- }
- if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
- return ErrnoError() << "Could not mount(/sys)";
- }
- }
- return Success();
-}
-
-Result<Success> Service::SetUpPidNamespace() const {
- if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
- return ErrnoError() << "Could not set name";
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- return ErrnoError() << "Could not fork init inside the PID namespace";
- }
-
- if (child_pid > 0) {
- // So that we exit with the right status.
- static int init_exitstatus = 0;
- signal(SIGTERM, [](int) { _exit(init_exitstatus); });
-
- pid_t waited_pid;
- int status;
- while ((waited_pid = wait(&status)) > 0) {
- // This loop will end when there are no processes left inside the
- // PID namespace or when the init process inside the PID namespace
- // gets a signal.
- if (waited_pid == child_pid) {
- init_exitstatus = status;
- }
- }
- if (!WIFEXITED(init_exitstatus)) {
- _exit(EXIT_FAILURE);
- }
- _exit(WEXITSTATUS(init_exitstatus));
- }
- return Success();
-}
-
-Result<Success> Service::EnterNamespaces() const {
- for (const auto& [nstype, path] : namespaces_to_enter_) {
- auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
- if (fd == -1) {
- return ErrnoError() << "Could not open namespace at " << path;
- }
- if (setns(fd, nstype) == -1) {
- return ErrnoError() << "Could not setns() namespace at " << path;
- }
- }
- return Success();
-}
-
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
std::vector<std::string> expanded_args;
std::vector<char*> c_strings;
@@ -229,16 +145,16 @@
flags_(flags),
pid_(0),
crash_count_(0),
- uid_(uid),
- gid_(gid),
- supp_gids_(supp_gids),
- namespace_flags_(namespace_flags),
+ proc_attr_{.ioprio_class = IoSchedClass_NONE,
+ .ioprio_pri = 0,
+ .uid = uid,
+ .gid = gid,
+ .supp_gids = supp_gids,
+ .priority = 0},
+ namespaces_{.flags = namespace_flags},
seclabel_(seclabel),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
- ioprio_class_(IoSchedClass_NONE),
- ioprio_pri_(0),
- priority_(0),
oom_score_adjust_(-1000),
start_order_(0),
args_(args) {}
@@ -271,24 +187,18 @@
<< ") process group...";
int r;
if (signal == SIGTERM) {
- r = killProcessGroupOnce(uid_, pid_, signal);
+ r = killProcessGroupOnce(proc_attr_.uid, pid_, signal);
} else {
- r = killProcessGroup(uid_, pid_, signal);
+ r = killProcessGroup(proc_attr_.uid, pid_, signal);
}
if (r == 0) process_cgroup_empty_ = true;
}
}
-void Service::SetProcessAttributes() {
- for (const auto& rlimit : rlimits_) {
- if (setrlimit(rlimit.first, &rlimit.second) == -1) {
- LOG(FATAL) << StringPrintf("setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed",
- rlimit.first, rlimit.second.rlim_cur, rlimit.second.rlim_max);
- }
- }
+void Service::SetProcessAttributesAndCaps() {
// Keep capabilites on uid change.
- if (capabilities_ && uid_) {
+ if (capabilities_ && proc_attr_.uid) {
// If Android is running in a container, some securebits might already
// be locked, so don't change those.
unsigned long securebits = prctl(PR_GET_SECUREBITS);
@@ -301,37 +211,21 @@
}
}
- // TODO: work out why this fails for `console` then upgrade to FATAL.
- if (setpgid(0, getpid()) == -1) PLOG(ERROR) << "setpgid failed for " << name_;
+ if (auto result = SetProcessAttributes(proc_attr_); !result) {
+ LOG(FATAL) << "cannot set attribute for " << name_ << ": " << result.error();
+ }
- if (gid_) {
- if (setgid(gid_) != 0) {
- PLOG(FATAL) << "setgid failed for " << name_;
- }
- }
- if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
- PLOG(FATAL) << "setgroups failed for " << name_;
- }
- if (uid_) {
- if (setuid(uid_) != 0) {
- PLOG(FATAL) << "setuid failed for " << name_;
- }
- }
if (!seclabel_.empty()) {
if (setexeccon(seclabel_.c_str()) < 0) {
PLOG(FATAL) << "cannot setexeccon('" << seclabel_ << "') for " << name_;
}
}
- if (priority_ != 0) {
- if (setpriority(PRIO_PROCESS, 0, priority_) != 0) {
- PLOG(FATAL) << "setpriority failed for " << name_;
- }
- }
+
if (capabilities_) {
if (!SetCapsForExec(*capabilities_)) {
LOG(FATAL) << "cannot set capabilities for " << name_;
}
- } else if (uid_) {
+ } else if (proc_attr_.uid) {
// Inheritable caps can be non-zero when running in a container.
if (!DropInheritableCaps()) {
LOG(FATAL) << "cannot drop inheritable caps for " << name_;
@@ -372,10 +266,17 @@
return;
}
+#if defined(__ANDROID__)
+ static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+#else
+ static bool is_apex_updatable = false;
+#endif
+ const bool is_process_updatable = !pre_apexd_ && is_apex_updatable;
+
// If we crash > 4 times in 4 minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
- if (((flags_ & SVC_CRITICAL) || !pre_apexd_) && !(flags_ & SVC_RESTART)) {
+ if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
if (now < time_crashed_ + 4min || !boot_completed) {
if (++crash_count_ > 4) {
@@ -414,7 +315,7 @@
[] (const auto& info) { LOG(INFO) << *info; });
}
-Result<Success> Service::ParseCapabilities(std::vector<std::string>&& args) {
+Result<void> Service::ParseCapabilities(std::vector<std::string>&& args) {
capabilities_ = 0;
if (!CapAmbientSupported()) {
@@ -431,83 +332,82 @@
const std::string& arg = args[i];
int res = LookupCap(arg);
if (res < 0) {
- return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
+ return Errorf("invalid capability '{}'", arg);
}
unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
if (cap > last_valid_cap) {
- return Error() << StringPrintf("capability '%s' not supported by the kernel",
- arg.c_str());
+ return Errorf("capability '{}' not supported by the kernel", arg);
}
(*capabilities_)[cap] = true;
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseClass(std::vector<std::string>&& args) {
+Result<void> Service::ParseClass(std::vector<std::string>&& args) {
classnames_ = std::set<std::string>(args.begin() + 1, args.end());
- return Success();
+ return {};
}
-Result<Success> Service::ParseConsole(std::vector<std::string>&& args) {
+Result<void> Service::ParseConsole(std::vector<std::string>&& args) {
flags_ |= SVC_CONSOLE;
- console_ = args.size() > 1 ? "/dev/" + args[1] : "";
- return Success();
+ proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : "";
+ return {};
}
-Result<Success> Service::ParseCritical(std::vector<std::string>&& args) {
+Result<void> Service::ParseCritical(std::vector<std::string>&& args) {
flags_ |= SVC_CRITICAL;
- return Success();
+ return {};
}
-Result<Success> Service::ParseDisabled(std::vector<std::string>&& args) {
+Result<void> Service::ParseDisabled(std::vector<std::string>&& args) {
flags_ |= SVC_DISABLED;
flags_ |= SVC_RC_DISABLED;
- return Success();
+ return {};
}
-Result<Success> Service::ParseEnterNamespace(std::vector<std::string>&& args) {
+Result<void> Service::ParseEnterNamespace(std::vector<std::string>&& args) {
if (args[1] != "net") {
return Error() << "Init only supports entering network namespaces";
}
- if (!namespaces_to_enter_.empty()) {
+ if (!namespaces_.namespaces_to_enter.empty()) {
return Error() << "Only one network namespace may be entered";
}
// Network namespaces require that /sys is remounted, otherwise the old adapters will still be
// present. Therefore, they also require mount namespaces.
- namespace_flags_ |= CLONE_NEWNS;
- namespaces_to_enter_.emplace_back(CLONE_NEWNET, std::move(args[2]));
- return Success();
+ namespaces_.flags |= CLONE_NEWNS;
+ namespaces_.namespaces_to_enter.emplace_back(CLONE_NEWNET, std::move(args[2]));
+ return {};
}
-Result<Success> Service::ParseGroup(std::vector<std::string>&& args) {
+Result<void> Service::ParseGroup(std::vector<std::string>&& args) {
auto gid = DecodeUid(args[1]);
if (!gid) {
return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
}
- gid_ = *gid;
+ proc_attr_.gid = *gid;
for (std::size_t n = 2; n < args.size(); n++) {
gid = DecodeUid(args[n]);
if (!gid) {
return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
}
- supp_gids_.emplace_back(*gid);
+ proc_attr_.supp_gids.emplace_back(*gid);
}
- return Success();
+ return {};
}
-Result<Success> Service::ParsePriority(std::vector<std::string>&& args) {
- priority_ = 0;
- if (!ParseInt(args[1], &priority_,
- static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
+Result<void> Service::ParsePriority(std::vector<std::string>&& args) {
+ proc_attr_.priority = 0;
+ if (!ParseInt(args[1], &proc_attr_.priority,
+ static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- return Error() << StringPrintf("process priority value must be range %d - %d",
- ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
+ return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
+ ANDROID_PRIORITY_LOWEST);
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseInterface(std::vector<std::string>&& args) {
+Result<void> Service::ParseInterface(std::vector<std::string>&& args) {
const std::string& interface_name = args[1];
const std::string& instance_name = args[2];
@@ -535,28 +435,28 @@
interfaces_.insert(fullname);
- return Success();
+ return {};
}
-Result<Success> Service::ParseIoprio(std::vector<std::string>&& args) {
- if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
+Result<void> Service::ParseIoprio(std::vector<std::string>&& args) {
+ if (!ParseInt(args[2], &proc_attr_.ioprio_pri, 0, 7)) {
return Error() << "priority value must be range 0 - 7";
}
if (args[1] == "rt") {
- ioprio_class_ = IoSchedClass_RT;
+ proc_attr_.ioprio_class = IoSchedClass_RT;
} else if (args[1] == "be") {
- ioprio_class_ = IoSchedClass_BE;
+ proc_attr_.ioprio_class = IoSchedClass_BE;
} else if (args[1] == "idle") {
- ioprio_class_ = IoSchedClass_IDLE;
+ proc_attr_.ioprio_class = IoSchedClass_IDLE;
} else {
return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseKeycodes(std::vector<std::string>&& args) {
+Result<void> Service::ParseKeycodes(std::vector<std::string>&& args) {
auto it = args.begin() + 1;
if (args.size() == 2 && StartsWith(args[1], "$")) {
std::string expanded;
@@ -567,7 +467,7 @@
// If the property is not set, it defaults to none, in which case there are no keycodes
// for this service.
if (expanded == "none") {
- return Success();
+ return {};
}
args = Split(expanded, ",");
@@ -585,134 +485,134 @@
return Error() << "invalid keycode: " << *it;
}
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseOneshot(std::vector<std::string>&& args) {
+Result<void> Service::ParseOneshot(std::vector<std::string>&& args) {
flags_ |= SVC_ONESHOT;
- return Success();
+ return {};
}
-Result<Success> Service::ParseOnrestart(std::vector<std::string>&& args) {
+Result<void> Service::ParseOnrestart(std::vector<std::string>&& args) {
args.erase(args.begin());
int line = onrestart_.NumCommands() + 1;
if (auto result = onrestart_.AddCommand(std::move(args), line); !result) {
return Error() << "cannot add Onrestart command: " << result.error();
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseNamespace(std::vector<std::string>&& args) {
+Result<void> Service::ParseNamespace(std::vector<std::string>&& args) {
for (size_t i = 1; i < args.size(); i++) {
if (args[i] == "pid") {
- namespace_flags_ |= CLONE_NEWPID;
+ namespaces_.flags |= CLONE_NEWPID;
// PID namespaces require mount namespaces.
- namespace_flags_ |= CLONE_NEWNS;
+ namespaces_.flags |= CLONE_NEWNS;
} else if (args[i] == "mnt") {
- namespace_flags_ |= CLONE_NEWNS;
+ namespaces_.flags |= CLONE_NEWNS;
} else {
return Error() << "namespace must be 'pid' or 'mnt'";
}
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseOomScoreAdjust(std::vector<std::string>&& args) {
+Result<void> Service::ParseOomScoreAdjust(std::vector<std::string>&& args) {
if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
return Error() << "oom_score_adjust value must be in range -1000 - +1000";
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseOverride(std::vector<std::string>&& args) {
+Result<void> Service::ParseOverride(std::vector<std::string>&& args) {
override_ = true;
- return Success();
+ return {};
}
-Result<Success> Service::ParseMemcgSwappiness(std::vector<std::string>&& args) {
+Result<void> Service::ParseMemcgSwappiness(std::vector<std::string>&& args) {
if (!ParseInt(args[1], &swappiness_, 0)) {
return Error() << "swappiness value must be equal or greater than 0";
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
+Result<void> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
return Error() << "limit_in_bytes value must be equal or greater than 0";
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+Result<void> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
if (!ParseInt(args[1], &limit_percent_, 0)) {
return Error() << "limit_percent value must be equal or greater than 0";
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+Result<void> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
limit_property_ = std::move(args[1]);
- return Success();
+ return {};
}
-Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
+Result<void> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
}
- return Success();
+ return {};
}
-Result<Success> Service::ParseProcessRlimit(std::vector<std::string>&& args) {
+Result<void> Service::ParseProcessRlimit(std::vector<std::string>&& args) {
auto rlimit = ParseRlimit(args);
if (!rlimit) return rlimit.error();
- rlimits_.emplace_back(*rlimit);
- return Success();
+ proc_attr_.rlimits.emplace_back(*rlimit);
+ return {};
}
-Result<Success> Service::ParseRestartPeriod(std::vector<std::string>&& args) {
+Result<void> Service::ParseRestartPeriod(std::vector<std::string>&& args) {
int period;
if (!ParseInt(args[1], &period, 5)) {
return Error() << "restart_period value must be an integer >= 5";
}
restart_period_ = std::chrono::seconds(period);
- return Success();
+ return {};
}
-Result<Success> Service::ParseSeclabel(std::vector<std::string>&& args) {
+Result<void> Service::ParseSeclabel(std::vector<std::string>&& args) {
seclabel_ = std::move(args[1]);
- return Success();
+ return {};
}
-Result<Success> Service::ParseSigstop(std::vector<std::string>&& args) {
+Result<void> Service::ParseSigstop(std::vector<std::string>&& args) {
sigstop_ = true;
- return Success();
+ return {};
}
-Result<Success> Service::ParseSetenv(std::vector<std::string>&& args) {
+Result<void> Service::ParseSetenv(std::vector<std::string>&& args) {
environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
- return Success();
+ return {};
}
-Result<Success> Service::ParseShutdown(std::vector<std::string>&& args) {
+Result<void> Service::ParseShutdown(std::vector<std::string>&& args) {
if (args[1] == "critical") {
flags_ |= SVC_SHUTDOWN_CRITICAL;
- return Success();
+ return {};
}
return Error() << "Invalid shutdown option";
}
-Result<Success> Service::ParseTimeoutPeriod(std::vector<std::string>&& args) {
+Result<void> Service::ParseTimeoutPeriod(std::vector<std::string>&& args) {
int period;
if (!ParseInt(args[1], &period, 1)) {
return Error() << "timeout_period value must be an integer >= 1";
}
timeout_period_ = std::chrono::seconds(period);
- return Success();
+ return {};
}
template <typename T>
-Result<Success> Service::AddDescriptor(std::vector<std::string>&& args) {
+Result<void> Service::AddDescriptor(std::vector<std::string>&& args) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
Result<uid_t> uid = 0;
Result<gid_t> gid = 0;
@@ -743,11 +643,11 @@
}
descriptors_.emplace_back(std::move(descriptor));
- return Success();
+ return {};
}
// name type perm [ uid gid context ]
-Result<Success> Service::ParseSocket(std::vector<std::string>&& args) {
+Result<void> Service::ParseSocket(std::vector<std::string>&& args) {
if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
!StartsWith(args[2], "seqpacket")) {
return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
@@ -756,7 +656,7 @@
}
// name type perm [ uid gid context ]
-Result<Success> Service::ParseFile(std::vector<std::string>&& args) {
+Result<void> Service::ParseFile(std::vector<std::string>&& args) {
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
return Error() << "file type must be 'r', 'w' or 'rw'";
}
@@ -771,24 +671,24 @@
return AddDescriptor<FileInfo>(std::move(args));
}
-Result<Success> Service::ParseUser(std::vector<std::string>&& args) {
+Result<void> Service::ParseUser(std::vector<std::string>&& args) {
auto uid = DecodeUid(args[1]);
if (!uid) {
return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
}
- uid_ = *uid;
- return Success();
+ proc_attr_.uid = *uid;
+ return {};
}
-Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) {
+Result<void> Service::ParseWritepid(std::vector<std::string>&& args) {
args.erase(args.begin());
writepid_files_ = std::move(args);
- return Success();
+ return {};
}
-Result<Success> Service::ParseUpdatable(std::vector<std::string>&& args) {
+Result<void> Service::ParseUpdatable(std::vector<std::string>&& args) {
updatable_ = true;
- return Success();
+ return {};
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
@@ -851,7 +751,7 @@
return option_parsers;
}
-Result<Success> Service::ParseLine(std::vector<std::string>&& args) {
+Result<void> Service::ParseLine(std::vector<std::string>&& args) {
static const OptionParserMap parser_map;
auto parser = parser_map.FindFunction(args);
@@ -860,7 +760,7 @@
return std::invoke(*parser, this, std::move(args));
}
-Result<Success> Service::ExecStart() {
+Result<void> Service::ExecStart() {
if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
// Don't delay the service for ExecStart() as the semantic is that
// the caller might depend on the side effect of the execution.
@@ -877,14 +777,14 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
- LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
- << gid_ << "+" << supp_gids_.size() << " context "
+ LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
+ << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
<< (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
- return Success();
+ return {};
}
-Result<Success> Service::Start() {
+Result<void> Service::Start() {
if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
ServiceList::GetInstance().DelayService(*this);
return Error() << "Cannot start an updatable service '" << name_
@@ -907,21 +807,21 @@
flags_ |= SVC_RESTART;
}
// It is not an error to try to start a service that is already running.
- return Success();
+ return {};
}
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console) {
- if (console_.empty()) {
- console_ = default_console;
+ if (proc_attr_.console.empty()) {
+ proc_attr_.console = default_console;
}
// Make sure that open call succeeds to ensure a console driver is
// properly registered for the device node
- int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
+ int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
if (console_fd < 0) {
flags_ |= SVC_DISABLED;
- return ErrnoError() << "Couldn't open console '" << console_ << "'";
+ return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
}
close(console_fd);
}
@@ -956,8 +856,8 @@
LOG(INFO) << "starting service '" << name_ << "'...";
pid_t pid = -1;
- if (namespace_flags_) {
- pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
+ if (namespaces_.flags) {
+ pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
} else {
pid = fork();
}
@@ -965,33 +865,9 @@
if (pid == 0) {
umask(077);
- if (auto result = EnterNamespaces(); !result) {
- LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
- }
-
-#if defined(__ANDROID__)
- if (pre_apexd_) {
- if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
- LOG(FATAL) << "Service '" << name_ << "' could not enter "
- << "into the bootstrap mount namespace";
- }
- }
-#endif
-
- if (namespace_flags_ & CLONE_NEWNS) {
- if (auto result = SetUpMountNamespace(); !result) {
- LOG(FATAL) << "Service '" << name_
- << "' could not set up mount namespace: " << result.error();
- }
- }
-
- if (namespace_flags_ & CLONE_NEWPID) {
- // This will fork again to run an init process inside the PID
- // namespace.
- if (auto result = SetUpPidNamespace(); !result) {
- LOG(FATAL) << "Service '" << name_
- << "' could not set up PID namespace: " << result.error();
- }
+ if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' failed to set up namespaces: " << result.error();
}
for (const auto& [key, value] : environment_vars_) {
@@ -1001,58 +877,13 @@
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
- // See if there were "writepid" instructions to write to files under cpuset path.
- std::string cpuset_path;
- if (CgroupGetControllerPath("cpuset", &cpuset_path)) {
- auto cpuset_predicate = [&cpuset_path](const std::string& path) {
- return StartsWith(path, cpuset_path + "/");
- };
- auto iter =
- std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
- if (iter == writepid_files_.end()) {
- // There were no "writepid" instructions for cpusets, check if the system default
- // cpuset is specified to be used for the process.
- std::string default_cpuset = GetProperty("ro.cpuset.default", "");
- if (!default_cpuset.empty()) {
- // Make sure the cpuset name starts and ends with '/'.
- // A single '/' means the 'root' cpuset.
- if (default_cpuset.front() != '/') {
- default_cpuset.insert(0, 1, '/');
- }
- if (default_cpuset.back() != '/') {
- default_cpuset.push_back('/');
- }
- writepid_files_.push_back(
- StringPrintf("%s%stasks", cpuset_path.c_str(), default_cpuset.c_str()));
- }
- }
- } else {
- LOG(ERROR) << "cpuset cgroup controller is not mounted!";
- }
- std::string pid_str = std::to_string(getpid());
- for (const auto& file : writepid_files_) {
- if (!WriteStringToFile(pid_str, file)) {
- PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
- }
- }
-
- if (ioprio_class_ != IoSchedClass_NONE) {
- if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
- PLOG(ERROR) << "failed to set pid " << getpid()
- << " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
- }
- }
-
- if (needs_console) {
- setsid();
- OpenConsole();
- } else {
- ZapStdio();
+ if (auto result = WritePidToFiles(&writepid_files_); !result) {
+ LOG(ERROR) << "failed to write pid to files: " << result.error();
}
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
- SetProcessAttributes();
+ SetProcessAttributesAndCaps();
if (!ExpandArgsAndExecv(args_, sigstop_)) {
PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
@@ -1082,19 +913,19 @@
bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
limit_percent_ != -1 || !limit_property_.empty();
- errno = -createProcessGroup(uid_, pid_, use_memcg);
+ errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
if (errno != 0) {
- PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
- << name_ << "'";
+ PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+ << ") failed for service '" << name_ << "'";
} else if (use_memcg) {
if (swappiness_ != -1) {
- if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
+ if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
PLOG(ERROR) << "setProcessGroupSwappiness failed";
}
}
if (soft_limit_in_bytes_ != -1) {
- if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
+ if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
PLOG(ERROR) << "setProcessGroupSoftLimit failed";
}
}
@@ -1121,31 +952,31 @@
}
if (computed_limit_in_bytes != size_t(-1)) {
- if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) {
+ if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
PLOG(ERROR) << "setProcessGroupLimit failed";
}
}
}
NotifyStateChange("running");
- return Success();
+ return {};
}
-Result<Success> Service::StartIfNotDisabled() {
+Result<void> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
- return Success();
+ return {};
}
-Result<Success> Service::Enable() {
+Result<void> Service::Enable() {
flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
if (flags_ & SVC_DISABLED_START) {
return Start();
}
- return Success();
+ return {};
}
void Service::Reset() {
@@ -1161,14 +992,14 @@
}
}
-Result<Success> Service::StartIfPostData() {
+Result<void> Service::StartIfPostData() {
// Start the service, but only if it was started after /data was mounted,
// and it was still running when we reset the post-data services.
if (running_at_post_data_reset_) {
return Start();
}
- return Success();
+ return {};
}
void Service::Stop() {
@@ -1241,25 +1072,6 @@
}
}
-void Service::ZapStdio() const {
- int fd;
- fd = open("/dev/null", O_RDWR);
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- close(fd);
-}
-
-void Service::OpenConsole() const {
- int fd = open(console_.c_str(), O_RDWR);
- if (fd == -1) fd = open("/dev/null", O_RDWR);
- ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- close(fd);
-}
-
ServiceList::ServiceList() {}
ServiceList& ServiceList::GetInstance() {
@@ -1383,7 +1195,7 @@
continue;
}
if (auto result = service->Start(); !result) {
- LOG(ERROR) << result.error_string();
+ LOG(ERROR) << result.error().message();
}
}
delayed_service_names_.clear();
@@ -1398,8 +1210,8 @@
delayed_service_names_.emplace_back(service.name());
}
-Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<void> ServiceParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() < 3) {
return Error() << "services must have a name and a program";
}
@@ -1430,14 +1242,14 @@
}
service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
- return Success();
+ return {};
}
-Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
- return service_ ? service_->ParseLine(std::move(args)) : Success();
+Result<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return service_ ? service_->ParseLine(std::move(args)) : Result<void>{};
}
-Result<Success> ServiceParser::EndSection() {
+Result<void> ServiceParser::EndSection() {
if (service_) {
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
@@ -1458,7 +1270,7 @@
service_list_->AddService(std::move(service_));
}
- return Success();
+ return {};
}
bool ServiceParser::IsValidName(const std::string& name) const {
diff --git a/init/service.h b/init/service.h
index ae29f28..b4356c8 100644
--- a/init/service.h
+++ b/init/service.h
@@ -36,6 +36,7 @@
#include "descriptors.h"
#include "keyword_map.h"
#include "parser.h"
+#include "service_utils.h"
#include "subcontext.h"
#define SVC_DISABLED 0x001 // do not autostart with class
@@ -75,12 +76,12 @@
static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
- Result<Success> ParseLine(std::vector<std::string>&& args);
- Result<Success> ExecStart();
- Result<Success> Start();
- Result<Success> StartIfNotDisabled();
- Result<Success> StartIfPostData();
- Result<Success> Enable();
+ Result<void> ParseLine(std::vector<std::string>&& args);
+ Result<void> ExecStart();
+ Result<void> Start();
+ Result<void> StartIfNotDisabled();
+ Result<void> StartIfPostData();
+ Result<void> Enable();
void Reset();
void ResetIfPostData();
void Stop();
@@ -107,16 +108,16 @@
pid_t pid() const { return pid_; }
android::base::boot_clock::time_point time_started() const { return time_started_; }
int crash_count() const { return crash_count_; }
- uid_t uid() const { return uid_; }
- gid_t gid() const { return gid_; }
- unsigned namespace_flags() const { return namespace_flags_; }
- const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
+ uid_t uid() const { return proc_attr_.uid; }
+ gid_t gid() const { return proc_attr_.gid; }
+ unsigned namespace_flags() const { return namespaces_.flags; }
+ const std::vector<gid_t>& supp_gids() const { return proc_attr_.supp_gids; }
const std::string& seclabel() const { return seclabel_; }
const std::vector<int>& keycodes() const { return keycodes_; }
- IoSchedClass ioprio_class() const { return ioprio_class_; }
- int ioprio_pri() const { return ioprio_pri_; }
+ IoSchedClass ioprio_class() const { return proc_attr_.ioprio_class; }
+ int ioprio_pri() const { return proc_attr_.ioprio_pri; }
const std::set<std::string>& interfaces() const { return interfaces_; }
- int priority() const { return priority_; }
+ int priority() const { return proc_attr_.priority; }
int oom_score_adjust() const { return oom_score_adjust_; }
bool is_override() const { return override_; }
bool process_cgroup_empty() const { return process_cgroup_empty_; }
@@ -129,62 +130,56 @@
bool is_post_data() const { return post_data_; }
private:
- using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
+ using OptionParser = Result<void> (Service::*)(std::vector<std::string>&& args);
class OptionParserMap;
- Result<Success> SetUpMountNamespace() const;
- Result<Success> SetUpPidNamespace() const;
- Result<Success> EnterNamespaces() const;
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
- void ZapStdio() const;
- void OpenConsole() const;
void KillProcessGroup(int signal);
- void SetProcessAttributes();
+ void SetProcessAttributesAndCaps();
- Result<Success> ParseCapabilities(std::vector<std::string>&& args);
- Result<Success> ParseClass(std::vector<std::string>&& args);
- Result<Success> ParseConsole(std::vector<std::string>&& args);
- Result<Success> ParseCritical(std::vector<std::string>&& args);
- Result<Success> ParseDisabled(std::vector<std::string>&& args);
- Result<Success> ParseEnterNamespace(std::vector<std::string>&& args);
- Result<Success> ParseGroup(std::vector<std::string>&& args);
- Result<Success> ParsePriority(std::vector<std::string>&& args);
- Result<Success> ParseInterface(std::vector<std::string>&& args);
- Result<Success> ParseIoprio(std::vector<std::string>&& args);
- Result<Success> ParseKeycodes(std::vector<std::string>&& args);
- Result<Success> ParseOneshot(std::vector<std::string>&& args);
- Result<Success> ParseOnrestart(std::vector<std::string>&& args);
- Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
- Result<Success> ParseOverride(std::vector<std::string>&& args);
- Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
- Result<Success> ParseMemcgLimitPercent(std::vector<std::string>&& args);
- Result<Success> ParseMemcgLimitProperty(std::vector<std::string>&& args);
- Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
- Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
- Result<Success> ParseNamespace(std::vector<std::string>&& args);
- Result<Success> ParseProcessRlimit(std::vector<std::string>&& args);
- Result<Success> ParseRestartPeriod(std::vector<std::string>&& args);
- Result<Success> ParseSeclabel(std::vector<std::string>&& args);
- Result<Success> ParseSetenv(std::vector<std::string>&& args);
- Result<Success> ParseShutdown(std::vector<std::string>&& args);
- Result<Success> ParseSigstop(std::vector<std::string>&& args);
- Result<Success> ParseSocket(std::vector<std::string>&& args);
- Result<Success> ParseTimeoutPeriod(std::vector<std::string>&& args);
- Result<Success> ParseFile(std::vector<std::string>&& args);
- Result<Success> ParseUser(std::vector<std::string>&& args);
- Result<Success> ParseWritepid(std::vector<std::string>&& args);
- Result<Success> ParseUpdatable(std::vector<std::string>&& args);
+ Result<void> ParseCapabilities(std::vector<std::string>&& args);
+ Result<void> ParseClass(std::vector<std::string>&& args);
+ Result<void> ParseConsole(std::vector<std::string>&& args);
+ Result<void> ParseCritical(std::vector<std::string>&& args);
+ Result<void> ParseDisabled(std::vector<std::string>&& args);
+ Result<void> ParseEnterNamespace(std::vector<std::string>&& args);
+ Result<void> ParseGroup(std::vector<std::string>&& args);
+ Result<void> ParsePriority(std::vector<std::string>&& args);
+ Result<void> ParseInterface(std::vector<std::string>&& args);
+ Result<void> ParseIoprio(std::vector<std::string>&& args);
+ Result<void> ParseKeycodes(std::vector<std::string>&& args);
+ Result<void> ParseOneshot(std::vector<std::string>&& args);
+ Result<void> ParseOnrestart(std::vector<std::string>&& args);
+ Result<void> ParseOomScoreAdjust(std::vector<std::string>&& args);
+ Result<void> ParseOverride(std::vector<std::string>&& args);
+ Result<void> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+ Result<void> ParseMemcgLimitPercent(std::vector<std::string>&& args);
+ Result<void> ParseMemcgLimitProperty(std::vector<std::string>&& args);
+ Result<void> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
+ Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);
+ Result<void> ParseNamespace(std::vector<std::string>&& args);
+ Result<void> ParseProcessRlimit(std::vector<std::string>&& args);
+ Result<void> ParseRestartPeriod(std::vector<std::string>&& args);
+ Result<void> ParseSeclabel(std::vector<std::string>&& args);
+ Result<void> ParseSetenv(std::vector<std::string>&& args);
+ Result<void> ParseShutdown(std::vector<std::string>&& args);
+ Result<void> ParseSigstop(std::vector<std::string>&& args);
+ Result<void> ParseSocket(std::vector<std::string>&& args);
+ Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
+ Result<void> ParseFile(std::vector<std::string>&& args);
+ Result<void> ParseUser(std::vector<std::string>&& args);
+ Result<void> ParseWritepid(std::vector<std::string>&& args);
+ Result<void> ParseUpdatable(std::vector<std::string>&& args);
template <typename T>
- Result<Success> AddDescriptor(std::vector<std::string>&& args);
+ Result<void> AddDescriptor(std::vector<std::string>&& args);
static unsigned long next_start_order_;
static bool is_exec_service_running_;
std::string name_;
std::set<std::string> classnames_;
- std::string console_;
unsigned flags_;
pid_t pid_;
@@ -192,13 +187,9 @@
android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
int crash_count_; // number of times crashed within window
- uid_t uid_;
- gid_t gid_;
- std::vector<gid_t> supp_gids_;
std::optional<CapSet> capabilities_;
- unsigned namespace_flags_;
- // Pair of namespace type, path to namespace.
- std::vector<std::pair<int, std::string>> namespaces_to_enter_;
+ ProcessAttributes proc_attr_;
+ NamespaceInfo namespaces_;
std::string seclabel_;
@@ -214,10 +205,6 @@
// keycodes for triggering this service via /dev/input/input*
std::vector<int> keycodes_;
- IoSchedClass ioprio_class_;
- int ioprio_pri_;
- int priority_;
-
int oom_score_adjust_;
int swappiness_ = -1;
@@ -233,8 +220,6 @@
unsigned long start_order_;
- std::vector<std::pair<int, rlimit>> rlimits_;
-
bool sigstop_ = false;
std::chrono::seconds restart_period_ = 5s;
@@ -310,10 +295,10 @@
public:
ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
: service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
- Result<Success> EndSection() override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ Result<void> EndSection() override;
void EndFile() override { filename_ = ""; }
private:
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
new file mode 100644
index 0000000..f88ea97
--- /dev/null
+++ b/init/service_utils.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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 "service_utils.h"
+
+#include <grp.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <processgroup/processgroup.h>
+
+#include "mount_namespace.h"
+
+using android::base::GetProperty;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
+namespace android {
+namespace init {
+
+namespace {
+
+Result<void> EnterNamespace(int nstype, const char* path) {
+ auto fd = unique_fd{open(path, O_RDONLY | O_CLOEXEC)};
+ if (fd == -1) {
+ return ErrnoError() << "Could not open namespace at " << path;
+ }
+ if (setns(fd, nstype) == -1) {
+ return ErrnoError() << "Could not setns() namespace at " << path;
+ }
+ return {};
+}
+
+Result<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {
+ constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
+
+ // Recursively remount / as slave like zygote does so unmounting and mounting /proc
+ // doesn't interfere with the parent namespace's /proc mount. This will also
+ // prevent any other mounts/unmounts initiated by the service from interfering
+ // with the parent namespace but will still allow mount events from the parent
+ // namespace to propagate to the child.
+ if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
+ return ErrnoError() << "Could not remount(/) recursively as slave";
+ }
+
+ // umount() then mount() /proc and/or /sys
+ // Note that it is not sufficient to mount with MS_REMOUNT.
+ if (remount_proc) {
+ if (umount("/proc") == -1) {
+ return ErrnoError() << "Could not umount(/proc)";
+ }
+ if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/proc)";
+ }
+ }
+ if (remount_sys) {
+ if (umount2("/sys", MNT_DETACH) == -1) {
+ return ErrnoError() << "Could not umount(/sys)";
+ }
+ if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/sys)";
+ }
+ }
+ return {};
+}
+
+Result<void> SetUpPidNamespace(const char* name) {
+ if (prctl(PR_SET_NAME, name) == -1) {
+ return ErrnoError() << "Could not set name";
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ return ErrnoError() << "Could not fork init inside the PID namespace";
+ }
+
+ if (child_pid > 0) {
+ // So that we exit with the right status.
+ static int init_exitstatus = 0;
+ signal(SIGTERM, [](int) { _exit(init_exitstatus); });
+
+ pid_t waited_pid;
+ int status;
+ while ((waited_pid = wait(&status)) > 0) {
+ // This loop will end when there are no processes left inside the
+ // PID namespace or when the init process inside the PID namespace
+ // gets a signal.
+ if (waited_pid == child_pid) {
+ init_exitstatus = status;
+ }
+ }
+ if (!WIFEXITED(init_exitstatus)) {
+ _exit(EXIT_FAILURE);
+ }
+ _exit(WEXITSTATUS(init_exitstatus));
+ }
+ return {};
+}
+
+void ZapStdio() {
+ int fd;
+ fd = open("/dev/null", O_RDWR);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+}
+
+void OpenConsole(const std::string& console) {
+ int fd = open(console.c_str(), O_RDWR);
+ if (fd == -1) fd = open("/dev/null", O_RDWR);
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+}
+
+} // namespace
+
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
+ for (const auto& [nstype, path] : info.namespaces_to_enter) {
+ if (auto result = EnterNamespace(nstype, path.c_str()); !result) {
+ return result;
+ }
+ }
+
+#if defined(__ANDROID__)
+ if (pre_apexd) {
+ if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ return Error() << "could not enter into the bootstrap mount namespace";
+ }
+ }
+#endif
+
+ if (info.flags & CLONE_NEWNS) {
+ bool remount_proc = info.flags & CLONE_NEWPID;
+ bool remount_sys =
+ std::any_of(info.namespaces_to_enter.begin(), info.namespaces_to_enter.end(),
+ [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+ if (auto result = SetUpMountNamespace(remount_proc, remount_sys); !result) {
+ return result;
+ }
+ }
+
+ if (info.flags & CLONE_NEWPID) {
+ // This will fork again to run an init process inside the PID namespace.
+ if (auto result = SetUpPidNamespace(name.c_str()); !result) {
+ return result;
+ }
+ }
+
+ return {};
+}
+
+Result<void> SetProcessAttributes(const ProcessAttributes& attr) {
+ if (attr.ioprio_class != IoSchedClass_NONE) {
+ if (android_set_ioprio(getpid(), attr.ioprio_class, attr.ioprio_pri)) {
+ PLOG(ERROR) << "failed to set pid " << getpid() << " ioprio=" << attr.ioprio_class
+ << "," << attr.ioprio_pri;
+ }
+ }
+
+ if (!attr.console.empty()) {
+ setsid();
+ OpenConsole(attr.console);
+ } else {
+ if (setpgid(0, getpid()) == -1) {
+ return ErrnoError() << "setpgid failed";
+ }
+ ZapStdio();
+ }
+
+ for (const auto& rlimit : attr.rlimits) {
+ if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+ return ErrnoError() << StringPrintf(
+ "setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed", rlimit.first,
+ rlimit.second.rlim_cur, rlimit.second.rlim_max);
+ }
+ }
+
+ if (attr.gid) {
+ if (setgid(attr.gid) != 0) {
+ return ErrnoError() << "setgid failed";
+ }
+ }
+ if (setgroups(attr.supp_gids.size(), const_cast<gid_t*>(&attr.supp_gids[0])) != 0) {
+ return ErrnoError() << "setgroups failed";
+ }
+ if (attr.uid) {
+ if (setuid(attr.uid) != 0) {
+ return ErrnoError() << "setuid failed";
+ }
+ }
+
+ if (attr.priority != 0) {
+ if (setpriority(PRIO_PROCESS, 0, attr.priority) != 0) {
+ return ErrnoError() << "setpriority failed";
+ }
+ }
+ return {};
+}
+
+Result<void> WritePidToFiles(std::vector<std::string>* files) {
+ // See if there were "writepid" instructions to write to files under cpuset path.
+ std::string cpuset_path;
+ if (CgroupGetControllerPath("cpuset", &cpuset_path)) {
+ auto cpuset_predicate = [&cpuset_path](const std::string& path) {
+ return StartsWith(path, cpuset_path + "/");
+ };
+ auto iter = std::find_if(files->begin(), files->end(), cpuset_predicate);
+ if (iter == files->end()) {
+ // There were no "writepid" instructions for cpusets, check if the system default
+ // cpuset is specified to be used for the process.
+ std::string default_cpuset = GetProperty("ro.cpuset.default", "");
+ if (!default_cpuset.empty()) {
+ // Make sure the cpuset name starts and ends with '/'.
+ // A single '/' means the 'root' cpuset.
+ if (default_cpuset.front() != '/') {
+ default_cpuset.insert(0, 1, '/');
+ }
+ if (default_cpuset.back() != '/') {
+ default_cpuset.push_back('/');
+ }
+ files->push_back(
+ StringPrintf("%s%stasks", cpuset_path.c_str(), default_cpuset.c_str()));
+ }
+ }
+ } else {
+ LOG(ERROR) << "cpuset cgroup controller is not mounted!";
+ }
+ std::string pid_str = std::to_string(getpid());
+ for (const auto& file : *files) {
+ if (!WriteStringToFile(pid_str, file)) {
+ return ErrnoError() << "couldn't write " << pid_str << " to " << file;
+ }
+ }
+ return {};
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service_utils.h b/init/service_utils.h
new file mode 100644
index 0000000..c26b123
--- /dev/null
+++ b/init/service_utils.h
@@ -0,0 +1,54 @@
+/*
+ * 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/resource.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <cutils/iosched_policy.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+struct NamespaceInfo {
+ unsigned flags;
+ // Pair of namespace type, path to name.
+ std::vector<std::pair<int, std::string>> namespaces_to_enter;
+};
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd);
+
+struct ProcessAttributes {
+ std::string console;
+ IoSchedClass ioprio_class;
+ int ioprio_pri;
+ std::vector<std::pair<int, rlimit>> rlimits;
+ uid_t uid;
+ gid_t gid;
+ std::vector<gid_t> supp_gids;
+ int priority;
+};
+Result<void> SetProcessAttributes(const ProcessAttributes& attr);
+
+Result<void> WritePidToFiles(std::vector<std::string>* files);
+
+} // namespace init
+} // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 0b03324..987b2f9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -29,7 +29,6 @@
#include <android-base/stringprintf.h>
#include "init.h"
-#include "property_service.h"
#include "service.h"
using android::base::StringPrintf;
@@ -61,9 +60,7 @@
std::string wait_string;
Service* service = nullptr;
- if (PropertyChildReap(pid)) {
- name = "Async property child";
- } else if (SubcontextChildReap(pid)) {
+ if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
service = ServiceList::GetInstance().FindService(pid, &Service::pid);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 092c51c..02ed507 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -32,6 +32,7 @@
#if defined(__ANDROID__)
#include <android/api-level.h>
#include "property_service.h"
+#include "selabel.h"
#include "selinux.h"
#else
#include "host_init_stubs.h"
@@ -71,7 +72,7 @@
}
template <typename T>
-Result<Success> SendMessage(int socket, const T& message) {
+Result<void> SendMessage(int socket, const T& message) {
std::string message_string;
if (!message.SerializeToString(&message_string)) {
return Error() << "Unable to serialize message";
@@ -86,7 +87,7 @@
result != static_cast<long>(message_string.size())) {
return ErrnoError() << "send() failed to send message contents";
}
- return Success();
+ return {};
}
std::vector<std::pair<std::string, std::string>> properties_to_set;
@@ -122,7 +123,7 @@
}
auto map_result = function_map_->FindFunction(args);
- Result<Success> result;
+ Result<void> result;
if (!map_result) {
result = Error() << "Cannot find command: " << map_result.error();
} else {
@@ -141,8 +142,8 @@
reply->set_success(true);
} else {
auto* failure = reply->mutable_failure();
- failure->set_error_string(result.error_string());
- failure->set_error_errno(result.error_errno());
+ failure->set_error_string(result.error().message());
+ failure->set_error_errno(result.error().code());
}
}
@@ -177,7 +178,7 @@
auto init_message = ReadMessage(init_fd_);
if (!init_message) {
- if (init_message.error_errno() == 0) {
+ if (init_message.error().code() == 0) {
// If the init file descriptor was closed, let's exit quietly. If
// this was accidental, init will restart us. If init died, this
// avoids calling abort(3) unnecessarily.
@@ -298,7 +299,7 @@
return subcontext_reply;
}
-Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+Result<void> Subcontext::Execute(const std::vector<std::string>& args) {
auto subcontext_command = SubcontextCommand();
std::copy(
args.begin(), args.end(),
@@ -328,7 +329,7 @@
<< subcontext_reply->reply_case();
}
- return Success();
+ return {};
}
Result<std::vector<std::string>> Subcontext::ExpandArgs(const std::vector<std::string>& args) {
diff --git a/init/subcontext.h b/init/subcontext.h
index 628fd50..16bd870 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -42,7 +42,7 @@
Fork();
}
- Result<Success> Execute(const std::vector<std::string>& args);
+ Result<void> Execute(const std::vector<std::string>& args);
Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
void Restart();
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index eae03e3..fdbbc41 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -39,7 +39,7 @@
free(context);
while (state.KeepRunning()) {
- subcontext.Execute(std::vector<std::string>{"return_success"}).IgnoreError();
+ subcontext.Execute(std::vector<std::string>{"return_success"});
}
if (subcontext.pid() > 0) {
@@ -53,7 +53,7 @@
TestFunctionMap BuildTestFunctionMap() {
TestFunctionMap test_function_map;
test_function_map.Add("return_success", 0, 0, true,
- [](const BuiltinArguments& args) { return Success(); });
+ [](const BuiltinArguments& args) { return Result<void>{}; });
return test_function_map;
}
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 230203a..55912d6 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -69,7 +69,7 @@
auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
ASSERT_FALSE(result);
- auto pids = Split(result.error_string(), " ");
+ auto pids = Split(result.error().message(), " ");
ASSERT_EQ(2U, pids.size());
auto our_pid = std::to_string(getpid());
EXPECT_NE(our_pid, pids[0]);
@@ -116,7 +116,7 @@
auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
ASSERT_FALSE(result);
- EXPECT_EQ(Join(expected_words, " "), result.error_string());
+ EXPECT_EQ(Join(expected_words, " "), result.error().message());
EXPECT_EQ(first_pid, subcontext.pid());
});
}
@@ -130,7 +130,7 @@
auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
ASSERT_FALSE(result2);
- EXPECT_EQ("Sane error!", result2.error_string());
+ EXPECT_EQ("Sane error!", result2.error().message());
EXPECT_NE(subcontext.pid(), first_pid);
});
}
@@ -139,7 +139,7 @@
RunTest([](auto& subcontext, auto& context_string) {
auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
ASSERT_FALSE(result);
- ASSERT_EQ(context_string, result.error_string());
+ ASSERT_EQ(context_string, result.error().message());
});
}
@@ -167,7 +167,7 @@
};
auto result = subcontext.ExpandArgs(args);
ASSERT_FALSE(result);
- EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error_string());
+ EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error().message());
});
}
@@ -175,14 +175,14 @@
TestFunctionMap test_function_map;
// For CheckDifferentPid
test_function_map.Add("return_pids_as_error", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> {
+ [](const BuiltinArguments& args) -> Result<void> {
return Error() << getpid() << " " << getppid();
});
// For SetProp
test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
android::base::SetProperty(args[1], args[2]);
- return Success();
+ return Result<void>{};
});
// For MultipleCommands
@@ -190,26 +190,26 @@
auto words = std::make_shared<std::vector<std::string>>();
test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
words->emplace_back(args[1]);
- return Success();
+ return Result<void>{};
});
test_function_map.Add("return_words_as_error", 0, 0, true,
- [words](const BuiltinArguments& args) -> Result<Success> {
+ [words](const BuiltinArguments& args) -> Result<void> {
return Error() << Join(*words, " ");
});
// For RecoverAfterAbort
test_function_map.Add("cause_log_fatal", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> {
+ [](const BuiltinArguments& args) -> Result<void> {
return Error() << std::string(4097, 'f');
});
test_function_map.Add(
- "generate_sane_error", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
+ "generate_sane_error", 0, 0, true,
+ [](const BuiltinArguments& args) -> Result<void> { return Error() << "Sane error!"; });
// For ContextString
test_function_map.Add(
- "return_context_as_error", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> { return Error() << args.context; });
+ "return_context_as_error", 0, 0, true,
+ [](const BuiltinArguments& args) -> Result<void> { return Error() << args.context; });
return test_function_map;
}
diff --git a/init/test_function_map.h b/init/test_function_map.h
index 583df1a..293f1f9 100644
--- a/init/test_function_map.h
+++ b/init/test_function_map.h
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef _INIT_TEST_FUNCTION_MAP_H
-#define _INIT_TEST_FUNCTION_MAP_H
+#pragma once
#include <string>
#include <vector>
#include "builtin_arguments.h"
+#include "builtins.h"
#include "keyword_map.h"
namespace android {
@@ -33,7 +33,7 @@
void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
Add(name, 0, 0, false, [function](const BuiltinArguments&) {
function();
- return Success();
+ return Result<void>{};
});
}
@@ -51,5 +51,3 @@
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 399ea4c..3b9de0f 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -37,6 +37,7 @@
#include "devices.h"
#include "firmware_handler.h"
#include "modalias_handler.h"
+#include "selabel.h"
#include "selinux.h"
#include "uevent_handler.h"
#include "uevent_listener.h"
@@ -213,7 +214,7 @@
WaitForSubProcesses();
- close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ android::base::SetProperty(kColdBootDoneProp, "true");
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}
@@ -250,11 +251,12 @@
std::move(ueventd_configuration.firmware_directories)));
if (ueventd_configuration.enable_modalias_handling) {
- uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
+ std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
+ uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths));
}
UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
- if (access(COLDBOOT_DONE, F_OK) != 0) {
+ if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
ColdBoot cold_boot(uevent_listener, uevent_handlers);
cold_boot.Run();
}
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index aac3fe5..25bab93 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -29,9 +29,9 @@
namespace android {
namespace init {
-Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
- std::vector<SysfsPermissions>* out_sysfs_permissions,
- std::vector<Permissions>* out_dev_permissions) {
+Result<void> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions) {
bool is_sysfs = out_sysfs_permissions != nullptr;
if (is_sysfs && args.size() != 5) {
return Error() << "/sys/ lines must have 5 entries";
@@ -74,22 +74,22 @@
} else {
out_dev_permissions->emplace_back(name, perm, uid, gid);
}
- return Success();
+ return {};
}
-Result<Success> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
- std::vector<std::string>* firmware_directories) {
+Result<void> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
+ std::vector<std::string>* firmware_directories) {
if (args.size() < 2) {
return Error() << "firmware_directories must have at least 1 entry";
}
std::move(std::next(args.begin()), args.end(), std::back_inserter(*firmware_directories));
- return Success();
+ return {};
}
-Result<Success> ParseModaliasHandlingLine(std::vector<std::string>&& args,
- bool* enable_modalias_handling) {
+Result<void> ParseModaliasHandlingLine(std::vector<std::string>&& args,
+ bool* enable_modalias_handling) {
if (args.size() != 2) {
return Error() << "modalias_handling lines take exactly one parameter";
}
@@ -102,11 +102,11 @@
return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
}
- return Success();
+ return {};
}
-Result<Success> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
- size_t* uevent_socket_rcvbuf_size) {
+Result<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
+ size_t* uevent_socket_rcvbuf_size) {
if (args.size() != 2) {
return Error() << "uevent_socket_rcvbuf_size lines take exactly one parameter";
}
@@ -118,27 +118,27 @@
*uevent_socket_rcvbuf_size = parsed_size;
- return Success();
+ return {};
}
class SubsystemParser : public SectionParser {
public:
SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
- Result<Success> EndSection() override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ Result<void> EndSection() override;
private:
- Result<Success> ParseDevName(std::vector<std::string>&& args);
- Result<Success> ParseDirName(std::vector<std::string>&& args);
+ Result<void> ParseDevName(std::vector<std::string>&& args);
+ Result<void> ParseDirName(std::vector<std::string>&& args);
Subsystem subsystem_;
std::vector<Subsystem>* subsystems_;
};
-Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<void> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
return Error() << "subsystems must have exactly one name";
}
@@ -149,33 +149,33 @@
subsystem_ = Subsystem(std::move(args[1]));
- return Success();
+ return {};
}
-Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
+Result<void> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
if (args[1] == "uevent_devname") {
subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;
- return Success();
+ return {};
}
if (args[1] == "uevent_devpath") {
subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
- return Success();
+ return {};
}
return Error() << "invalid devname '" << args[1] << "'";
}
-Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
+Result<void> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
if (args[1].front() != '/') {
return Error() << "dirname '" << args[1] << " ' does not start with '/'";
}
subsystem_.dir_name_ = args[1];
- return Success();
+ return {};
}
-Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
- using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+Result<void> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ using OptionParser = Result<void> (SubsystemParser::*)(std::vector<std::string> && args);
static class OptionParserMap : public KeywordMap<OptionParser> {
private:
@@ -197,10 +197,10 @@
return std::invoke(*parser, this, std::move(args));
}
-Result<Success> SubsystemParser::EndSection() {
+Result<void> SubsystemParser::EndSection() {
subsystems_->emplace_back(std::move(subsystem_));
- return Success();
+ return {};
}
UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
diff --git a/init/util.cpp b/init/util.cpp
index 63d2d44..14acaa2 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -41,7 +41,7 @@
#if defined(__ANDROID__)
#include "reboot_utils.h"
-#include "selinux.h"
+#include "selabel.h"
#else
#include "host_init_stubs.h"
#endif
@@ -197,7 +197,7 @@
return rc;
}
-Result<Success> WriteFile(const std::string& path, const std::string& content) {
+Result<void> WriteFile(const std::string& path, const std::string& content) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
@@ -206,7 +206,7 @@
if (!android::base::WriteStringToFd(content, fd)) {
return ErrnoError() << "Unable to write file contents";
}
- return Success();
+ return {};
}
bool mkdir_recursive(const std::string& path, mode_t mode) {
diff --git a/init/util.h b/init/util.h
index 767620b..1929cb5 100644
--- a/init/util.h
+++ b/init/util.h
@@ -30,19 +30,19 @@
#include "result.h"
-#define COLDBOOT_DONE "/dev/.coldboot_done"
-
using android::base::boot_clock;
using namespace std::chrono_literals;
namespace android {
namespace init {
+static const char kColdBootDoneProp[] = "ro.cold_boot_done";
+
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
const char* socketcon);
Result<std::string> ReadFile(const std::string& path);
-Result<Success> WriteFile(const std::string& path, const std::string& content);
+Result<void> WriteFile(const std::string& path, const std::string& content);
Result<uid_t> DecodeUid(const std::string& name);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 1b5afba..8947256 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -34,7 +34,7 @@
auto file_contents = ReadFile("/proc/does-not-exist");
EXPECT_EQ(ENOENT, errno);
ASSERT_FALSE(file_contents);
- EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
+ EXPECT_EQ("open() failed: No such file or directory", file_contents.error().message());
}
TEST(util, ReadFileGroupWriteable) {
@@ -45,7 +45,7 @@
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
auto file_contents = ReadFile(tf.path);
ASSERT_FALSE(file_contents) << strerror(errno);
- EXPECT_EQ("Skipping insecure file", file_contents.error_string());
+ EXPECT_EQ("Skipping insecure file", file_contents.error().message());
}
TEST(util, ReadFileWorldWiteable) {
@@ -56,7 +56,7 @@
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
auto file_contents = ReadFile(tf.path);
ASSERT_FALSE(file_contents) << strerror(errno);
- EXPECT_EQ("Skipping insecure file", file_contents.error_string());
+ EXPECT_EQ("Skipping insecure file", file_contents.error().message());
}
TEST(util, ReadFileSymbolicLink) {
@@ -65,7 +65,8 @@
auto file_contents = ReadFile("/charger");
EXPECT_EQ(ELOOP, errno);
ASSERT_FALSE(file_contents);
- EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
+ EXPECT_EQ("open() failed: Too many symbolic links encountered",
+ file_contents.error().message());
}
TEST(util, ReadFileSuccess) {
@@ -130,7 +131,7 @@
decoded_uid = DecodeUid("toot");
EXPECT_FALSE(decoded_uid);
- EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
+ EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error().message());
decoded_uid = DecodeUid("123");
EXPECT_TRUE(decoded_uid);
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7f9a18a..9ece847 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -42,6 +42,7 @@
name: "libbacktrace_headers",
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
}
@@ -134,7 +135,6 @@
defaults: ["libbacktrace_common"],
host_supported: true,
srcs: [
- "backtrace_offline_test.cpp",
"backtrace_test.cpp",
],
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 36640cd..a128623 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -129,22 +129,6 @@
return true;
}
-bool Backtrace::UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
- const backtrace_stackinfo_t& stack,
- std::vector<backtrace_frame_data_t>* frames,
- BacktraceUnwindError* error) {
- UnwindStackOfflineMap* offline_map = reinterpret_cast<UnwindStackOfflineMap*>(back_map);
- // Create the process memory from the stack data since this will almost
- // always be different each unwind.
- if (!offline_map->CreateProcessMemory(stack)) {
- if (error != nullptr) {
- error->error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
- }
- return false;
- }
- return Backtrace::Unwind(regs, back_map, frames, 0U, nullptr, error);
-}
-
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
: BacktraceCurrent(pid, tid, map) {}
@@ -171,7 +155,7 @@
}
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
- : BacktracePtrace(pid, tid, map), memory_(pid) {}
+ : BacktracePtrace(pid, tid, map), memory_(unwindstack::Memory::CreateProcessMemory(pid)) {}
std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
return GetMap()->GetFunctionName(pc, offset);
@@ -189,73 +173,5 @@
}
size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
- return memory_.Read(addr, buffer, bytes);
-}
-
-UnwindStackOffline::UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map,
- bool map_shared)
- : Backtrace(pid, tid, map), arch_(arch) {
- map_shared_ = map_shared;
-}
-
-bool UnwindStackOffline::Unwind(size_t num_ignore_frames, void* ucontext) {
- if (ucontext == nullptr) {
- return false;
- }
-
- unwindstack::ArchEnum arch;
- switch (arch_) {
- case ARCH_ARM:
- arch = unwindstack::ARCH_ARM;
- break;
- case ARCH_ARM64:
- arch = unwindstack::ARCH_ARM64;
- break;
- case ARCH_X86:
- arch = unwindstack::ARCH_X86;
- break;
- case ARCH_X86_64:
- arch = unwindstack::ARCH_X86_64;
- break;
- default:
- return false;
- }
-
- std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
-
- return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
-}
-
-std::string UnwindStackOffline::GetFunctionNameRaw(uint64_t, uint64_t*) {
- return "";
-}
-
-size_t UnwindStackOffline::Read(uint64_t, uint8_t*, size_t) {
- return 0;
-}
-
-bool UnwindStackOffline::ReadWord(uint64_t, word_t*) {
- return false;
-}
-
-Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
- const std::vector<backtrace_map_t>& maps,
- const backtrace_stackinfo_t& stack) {
- std::unique_ptr<UnwindStackOfflineMap> map(
- reinterpret_cast<UnwindStackOfflineMap*>(BacktraceMap::CreateOffline(pid, maps)));
- if (map.get() == nullptr || !map->CreateProcessMemory(stack)) {
- return nullptr;
- }
- return new UnwindStackOffline(arch, pid, tid, map.release(), false);
-}
-
-Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
- if (map == nullptr) {
- return nullptr;
- }
- return new UnwindStackOffline(arch, pid, tid, map, true);
-}
-
-void Backtrace::SetGlobalElfCache(bool enable) {
- unwindstack::Elf::SetCachingEnabled(enable);
+ return memory_->Read(addr, buffer, bytes);
}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
index 4ec591d..47f6757 100644
--- a/libbacktrace/UnwindStack.h
+++ b/libbacktrace/UnwindStack.h
@@ -19,6 +19,7 @@
#include <stdint.h>
+#include <memory>
#include <string>
#include <backtrace/BacktraceMap.h>
@@ -49,23 +50,7 @@
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
private:
- unwindstack::MemoryRemote memory_;
-};
-
-class UnwindStackOffline : public Backtrace {
- public:
- UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map, bool map_shared);
-
- bool Unwind(size_t num_ignore_frames, void* context) override;
-
- std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
-
- size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
-
- bool ReadWord(uint64_t ptr, word_t* out_value) override;
-
- private:
- ArchEnum arch_;
+ std::shared_ptr<unwindstack::Memory> memory_;
};
#endif // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 4518891..aa0b17c 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -132,43 +132,6 @@
return process_memory_;
}
-UnwindStackOfflineMap::UnwindStackOfflineMap(pid_t pid) : UnwindStackMap(pid) {}
-
-bool UnwindStackOfflineMap::Build() {
- return false;
-}
-
-bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps) {
- for (const backtrace_map_t& map : backtrace_maps) {
- maps_.push_back(map);
- }
-
- std::sort(maps_.begin(), maps_.end(),
- [](const backtrace_map_t& a, const backtrace_map_t& b) { return a.start < b.start; });
-
- unwindstack::Maps* maps = new unwindstack::Maps;
- stack_maps_.reset(maps);
- for (const backtrace_map_t& map : maps_) {
- maps->Add(map.start, map.end, map.offset, map.flags, map.name, map.load_bias);
- }
- return true;
-}
-
-bool UnwindStackOfflineMap::CreateProcessMemory(const backtrace_stackinfo_t& stack) {
- if (stack.start >= stack.end) {
- return false;
- }
-
- // Create the process memory from the stack data.
- if (memory_ == nullptr) {
- memory_ = new unwindstack::MemoryOfflineBuffer(stack.data, stack.start, stack.end);
- process_memory_.reset(memory_);
- } else {
- memory_->Reset(stack.data, stack.start, stack.end);
- }
- return true;
-}
-
//-------------------------------------------------------------------------
// BacktraceMap create function.
//-------------------------------------------------------------------------
@@ -189,15 +152,3 @@
}
return map;
}
-
-//-------------------------------------------------------------------------
-// BacktraceMap create offline function.
-//-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps) {
- UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
- if (!map->Build(maps)) {
- delete map;
- return nullptr;
- }
- return map;
-}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index e19b605..f0e7d8b 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -33,6 +33,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
// Forward declarations.
class UnwindDexFile;
@@ -74,19 +75,4 @@
unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
};
-class UnwindStackOfflineMap : public UnwindStackMap {
- public:
- UnwindStackOfflineMap(pid_t pid);
- ~UnwindStackOfflineMap() = default;
-
- bool Build() override;
-
- bool Build(const std::vector<backtrace_map_t>& maps);
-
- bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
-
- private:
- unwindstack::MemoryOfflineBuffer* memory_ = nullptr;
-};
-
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
deleted file mode 100644
index 662fb99..0000000
--- a/libbacktrace/backtrace_offline_test.cpp
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright (C) 2015 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 <inttypes.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/macros.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/threads.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include <gtest/gtest.h>
-
-#include "BacktraceTest.h"
-
-struct FunctionSymbol {
- std::string name;
- uint64_t start;
- uint64_t end;
-};
-
-static std::vector<FunctionSymbol> GetFunctionSymbols() {
- std::vector<FunctionSymbol> symbols = {
- {"unknown_start", 0, 0},
- {"test_level_one", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_one_), 0},
- {"test_level_two", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_two_), 0},
- {"test_level_three", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_three_), 0},
- {"test_level_four", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_four_), 0},
- {"test_recursive_call", reinterpret_cast<uint64_t>(&BacktraceTest::test_recursive_call_), 0},
- {"test_get_context_and_wait",
- reinterpret_cast<uint64_t>(&BacktraceTest::test_get_context_and_wait_), 0},
- {"unknown_end", static_cast<uint64_t>(-1), static_cast<uint64_t>(-1)},
- };
- std::sort(
- symbols.begin(), symbols.end(),
- [](const FunctionSymbol& s1, const FunctionSymbol& s2) { return s1.start < s2.start; });
- for (size_t i = 0; i + 1 < symbols.size(); ++i) {
- symbols[i].end = symbols[i + 1].start;
- }
- return symbols;
-}
-
-static std::string RawDataToHexString(const void* data, size_t size) {
- const uint8_t* p = static_cast<const uint8_t*>(data);
- std::string s;
- for (size_t i = 0; i < size; ++i) {
- s += android::base::StringPrintf("%02x", p[i]);
- }
- return s;
-}
-
-static void HexStringToRawData(const char* s, std::vector<uint8_t>* data, size_t size) {
- for (size_t i = 0; i < size; ++i) {
- int value;
- sscanf(s, "%02x", &value);
- data->push_back(value);
- s += 2;
- }
-}
-
-struct OfflineThreadArg {
- std::vector<uint8_t> ucontext;
- pid_t tid;
- volatile int exit_flag;
-};
-
-static void* OfflineThreadFunc(void* arg) {
- OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
- fn_arg->tid = android::base::GetThreadId();
- BacktraceTest::test_get_context_and_wait_(&fn_arg->ucontext, &fn_arg->exit_flag);
- return nullptr;
-}
-
-std::string GetTestPath(const std::string& arch, const std::string& path) {
- return android::base::GetExecutableDirectory() + "/testdata/" + arch + '/' + path;
-}
-
-// This test is disable because it is for generating test data.
-TEST_F(BacktraceTest, DISABLED_generate_offline_testdata) {
- // Create a thread to generate the needed stack and registers information.
- const size_t stack_size = 16 * 1024;
- void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- ASSERT_NE(MAP_FAILED, stack);
- uint64_t stack_addr = reinterpret_cast<uint64_t>(stack);
- pthread_attr_t attr;
- ASSERT_EQ(0, pthread_attr_init(&attr));
- ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<void*>(stack), stack_size));
- pthread_t thread;
- OfflineThreadArg arg;
- arg.exit_flag = 0;
- ASSERT_EQ(0, pthread_create(&thread, &attr, OfflineThreadFunc, &arg));
- // Wait for the offline thread to generate the stack and context information.
- sleep(1);
- // Copy the stack information.
- std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
- reinterpret_cast<uint8_t*>(stack) + stack_size);
- arg.exit_flag = 1;
- ASSERT_EQ(0, pthread_join(thread, nullptr));
- ASSERT_EQ(0, munmap(stack, stack_size));
-
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
- ASSERT_TRUE(map != nullptr);
-
- backtrace_stackinfo_t stack_info;
- stack_info.start = stack_addr;
- stack_info.end = stack_addr + stack_size;
- stack_info.data = stack_data.data();
-
- // Generate offline testdata.
- std::string testdata;
- // 1. Dump pid, tid
- testdata += android::base::StringPrintf("pid: %d tid: %d\n", getpid(), arg.tid);
- // 2. Dump maps
- for (auto it = map->begin(); it != map->end(); ++it) {
- const backtrace_map_t* entry = *it;
- testdata +=
- android::base::StringPrintf("map: start: %" PRIx64 " end: %" PRIx64 " offset: %" PRIx64
- " load_bias: %" PRIx64 " flags: %d name: %s\n",
- entry->start, entry->end, entry->offset, entry->load_bias,
- entry->flags, entry->name.c_str());
- }
- // 3. Dump ucontext
- testdata += android::base::StringPrintf("ucontext: %zu ", arg.ucontext.size());
- testdata += RawDataToHexString(arg.ucontext.data(), arg.ucontext.size());
- testdata.push_back('\n');
-
- // 4. Dump stack
- testdata += android::base::StringPrintf(
- "stack: start: %" PRIx64 " end: %" PRIx64 " size: %zu ",
- stack_info.start, stack_info.end, stack_data.size());
- testdata += RawDataToHexString(stack_data.data(), stack_data.size());
- testdata.push_back('\n');
-
- // 5. Dump function symbols
- std::vector<FunctionSymbol> function_symbols = GetFunctionSymbols();
- for (const auto& symbol : function_symbols) {
- testdata +=
- android::base::StringPrintf("function: start: %" PRIx64 " end: %" PRIx64 " name: %s\n",
- symbol.start, symbol.end, symbol.name.c_str());
- }
-
- ASSERT_TRUE(android::base::WriteStringToFile(testdata, "offline_testdata"));
-}
-
-// Return the name of the function which matches the address. Although we don't know the
-// exact end of each function, it is accurate enough for the tests.
-static std::string FunctionNameForAddress(uint64_t addr,
- const std::vector<FunctionSymbol>& symbols) {
- for (auto& symbol : symbols) {
- if (addr >= symbol.start && addr < symbol.end) {
- return symbol.name;
- }
- }
- return "";
-}
-
-struct OfflineTestData {
- int pid;
- int tid;
- std::vector<backtrace_map_t> maps;
- std::vector<uint8_t> ucontext;
- backtrace_stackinfo_t stack_info;
- std::vector<uint8_t> stack;
- std::vector<FunctionSymbol> symbols;
-};
-
-bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestData* testdata) {
- std::string s;
- if (!android::base::ReadFileToString(offline_testdata_path, &s)) {
- return false;
- }
- // Parse offline_testdata.
- std::vector<std::string> lines = android::base::Split(s, "\n");
- for (const auto& line : lines) {
- if (android::base::StartsWith(line, "pid:")) {
- sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
- } else if (android::base::StartsWith(line, "map:")) {
- testdata->maps.resize(testdata->maps.size() + 1);
- backtrace_map_t& map = testdata->maps.back();
- int pos;
- sscanf(line.c_str(),
- "map: start: %" SCNx64 " end: %" SCNx64 " offset: %" SCNx64 " load_bias: %" SCNx64
- " flags: %d name: %n",
- &map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos);
- map.name = android::base::Trim(line.substr(pos));
- } else if (android::base::StartsWith(line, "ucontext:")) {
- size_t size;
- int pos;
- testdata->ucontext.clear();
- sscanf(line.c_str(), "ucontext: %zu %n", &size, &pos);
- HexStringToRawData(&line[pos], &testdata->ucontext, size);
- } else if (android::base::StartsWith(line, "stack:")) {
- size_t size;
- int pos;
- sscanf(line.c_str(),
- "stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
- &testdata->stack_info.start, &testdata->stack_info.end, &size, &pos);
- CHECK_EQ(testdata->stack_info.end - testdata->stack_info.start, size);
- testdata->stack.clear();
- HexStringToRawData(&line[pos], &testdata->stack, size);
- testdata->stack_info.data = testdata->stack.data();
- } else if (android::base::StartsWith(line, "function:")) {
- testdata->symbols.resize(testdata->symbols.size() + 1);
- FunctionSymbol& symbol = testdata->symbols.back();
- int pos;
- sscanf(line.c_str(), "function: start: %" SCNx64 " end: %" SCNx64 " name: %n", &symbol.start,
- &symbol.end, &pos);
- symbol.name = line.substr(pos);
- }
- }
- return true;
-}
-
-static void BacktraceOfflineTest(std::string arch_str, const std::string& testlib_name) {
- const std::string testlib_path(GetTestPath(arch_str, testlib_name));
- const std::string offline_testdata_path(GetTestPath(arch_str, "offline_testdata"));
- OfflineTestData testdata;
- ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata)) << "Failed " << arch_str;
-
- // Fix path of libbacktrace_testlib.so.
- for (auto& map : testdata.maps) {
- if (map.name.find("libbacktrace_test.so") != std::string::npos) {
- map.name = testlib_path;
- }
- }
-
- Backtrace::ArchEnum arch;
- if (arch_str == "arm") {
- arch = Backtrace::ARCH_ARM;
- } else if (arch_str == "arm64") {
- arch = Backtrace::ARCH_ARM64;
- } else if (arch_str == "x86") {
- arch = Backtrace::ARCH_X86;
- } else if (arch_str == "x86_64") {
- arch = Backtrace::ARCH_X86_64;
- } else {
- abort();
- }
-
- std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
- arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
- ASSERT_TRUE(backtrace != nullptr) << "Failed " << arch_str;
-
- ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data())) << "Failed " << arch_str;
-
- // Collect pc values of the call stack frames.
- std::vector<uint64_t> pc_values;
- for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
- pc_values.push_back(backtrace->GetFrame(i)->pc);
- }
-
- size_t test_one_index = 0;
- for (size_t i = 0; i < pc_values.size(); ++i) {
- if (FunctionNameForAddress(pc_values[i], testdata.symbols) == "test_level_one") {
- test_one_index = i;
- break;
- }
- }
-
- ASSERT_GE(test_one_index, 3u) << "Failed " << arch_str;
- ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols))
- << "Failed " << arch_str;
- ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1], testdata.symbols))
- << "Failed " << arch_str;
- ASSERT_EQ("test_level_three",
- FunctionNameForAddress(pc_values[test_one_index - 2], testdata.symbols))
- << "Failed " << arch_str;
- ASSERT_EQ("test_level_four",
- FunctionNameForAddress(pc_values[test_one_index - 3], testdata.symbols))
- << "Failed " << arch_str;
-}
-
-// For now, these tests can only run on the given architectures.
-TEST_F(BacktraceTest, offline_eh_frame) {
- BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
- BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
-}
-
-TEST_F(BacktraceTest, offline_debug_frame) {
- BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
- BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
-}
-
-TEST_F(BacktraceTest, offline_gnu_debugdata) {
- BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
- BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
-}
-
-TEST_F(BacktraceTest, offline_arm_exidx) {
- BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
-}
-
-static void LibUnwindingTest(const std::string& arch_str, const std::string& testdata_name,
- const std::string& testlib_name) {
- const std::string testlib_path(GetTestPath(arch_str, testlib_name));
- struct stat st;
- ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
-
- const std::string offline_testdata_path(GetTestPath(arch_str, testdata_name));
- OfflineTestData testdata;
- ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
-
- // Fix path of the testlib.
- for (auto& map : testdata.maps) {
- if (map.name.find(testlib_name) != std::string::npos) {
- map.name = testlib_path;
- }
- }
-
- Backtrace::ArchEnum arch;
- if (arch_str == "arm") {
- arch = Backtrace::ARCH_ARM;
- } else if (arch_str == "arm64") {
- arch = Backtrace::ARCH_ARM64;
- } else if (arch_str == "x86") {
- arch = Backtrace::ARCH_X86;
- } else if (arch_str == "x86_64") {
- arch = Backtrace::ARCH_X86_64;
- } else {
- ASSERT_TRUE(false) << "Unsupported arch " << arch_str;
- abort();
- }
-
- // Do offline backtrace.
- std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
- arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
- ASSERT_TRUE(backtrace != nullptr);
-
- ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data()));
-
- ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
- for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
- std::string name = FunctionNameForAddress(backtrace->GetFrame(i)->rel_pc, testdata.symbols);
- ASSERT_EQ(name, testdata.symbols[i].name);
- }
- ASSERT_TRUE(backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED ||
- backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING ||
- backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_REPEATED_FRAME);
-}
-
-// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
-// overlap with each other, which appears in /system/lib/libart.so.
-TEST_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
- LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
-}
-
-TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
- LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
-}
-
-TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
- LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
-}
-
-TEST_F(BacktraceTest, offline_cie_with_P_augmentation) {
- // Make sure we can unwind through functions with CIE entry containing P augmentation, which
- // makes unwinding library reading personality handler from memory. One example is
- // /system/lib64/libskia.so.
- LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
-}
-
-TEST_F(BacktraceTest, offline_empty_eh_frame_hdr) {
- // Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
- // /vendor/lib64/egl/eglSubDriverAndroid.so.
- LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
-}
-
-TEST_F(BacktraceTest, offline_max_frames_limit) {
- // The length of callchain can reach 256 when recording an application.
- ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
-}
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 404e7e8..664b531 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -126,24 +126,6 @@
// If map is not NULL, the map is still owned by the caller.
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
- // Create an offline Backtrace object that can be used to do an unwind without a process
- // that is still running. By default, information is only cached in the map
- // file. If the calling code creates the map, data can be cached between
- // unwinds. If not, all cached data will be destroyed when the Backtrace
- // object is destroyed.
- static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
- const std::vector<backtrace_map_t>& maps,
- const backtrace_stackinfo_t& stack);
- static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map);
-
- // Create an offline Backtrace object that can be used to do an unwind without a process
- // that is still running. If cache_file is set to true, then elf information will be cached
- // for this call. The cached information survives until the calling process ends. This means
- // that subsequent calls to create offline Backtrace objects will continue to use the same
- // cache. It also assumes that the elf files used for each offline unwind are the same.
- static Backtrace* CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
- const backtrace_stackinfo_t& stack, bool cache_file = false);
-
virtual ~Backtrace();
// Get the current stack trace and store in the backtrace_ structure.
@@ -153,11 +135,6 @@
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
- static bool UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
- const backtrace_stackinfo_t& stack_info,
- std::vector<backtrace_frame_data_t>* frames,
- BacktraceUnwindError* error = nullptr);
-
// Get the function name and offset into the function given the pc.
// If the string is empty, then no valid function name was found,
// or the pc is not in any valid map.
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index c564271..f8d5058 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -69,8 +69,6 @@
// is unsupported.
static BacktraceMap* Create(pid_t pid, bool uncached = false);
- static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps);
-
virtual ~BacktraceMap();
class iterator : public std::iterator<std::bidirectional_iterator_tag, backtrace_map_t*> {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 619bc56..319a73a 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -34,6 +34,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
vendor: {
@@ -57,6 +58,7 @@
},
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
srcs: [
"config_utils.cpp",
"canned_fs_config.cpp",
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a5f4f0e..897a169 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -39,6 +39,8 @@
#include <private/android_filesystem_config.h>
#include <utils/Compat.h>
+#include "fs_config.h"
+
#ifndef O_BINARY
#define O_BINARY 0
#endif
@@ -46,20 +48,8 @@
using android::base::EndsWith;
using android::base::StartsWith;
-// My kingdom for <endian.h>
-static inline uint16_t get2LE(const uint8_t* src) {
- return src[0] | (src[1] << 8);
-}
-
-static inline uint64_t get8LE(const uint8_t* src) {
- uint32_t low, high;
-
- low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
- high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
- return ((uint64_t)high << 32) | (uint64_t)low;
-}
-
#define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
// Rules for directories.
// These rules are applied based on "first match", so they
@@ -88,7 +78,6 @@
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
{ 00751, AID_ROOT, AID_SHELL, 0, "product/bin" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
@@ -172,7 +161,6 @@
{ 00600, AID_ROOT, AID_ROOT, 0, "product_services/build.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, product_services_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, product_services_conf_file + 1 },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
@@ -217,8 +205,8 @@
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "odm/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" },
@@ -296,20 +284,21 @@
const int fnm_flags = FNM_NOESCAPE;
if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;
- static constexpr const char* kSystem = "system/";
- if (StartsWith(input, kSystem)) {
- input.erase(0, strlen(kSystem));
- } else if (input.size() <= strlen(kSystem)) {
- return false;
- } else if (StartsWith(pattern, kSystem)) {
- pattern.erase(0, strlen(kSystem));
- } else {
- return false;
+ // Check match between logical partition's files and patterns.
+ static constexpr const char* kLogicalPartitions[] = {"system/product/",
+ "system/product_services/",
+ "system/vendor/",
+ "vendor/odm/"};
+ for (auto& logical_partition : kLogicalPartitions) {
+ if (StartsWith(input, logical_partition)) {
+ std::string input_in_partition = input.substr(input.find('/') + 1);
+ if (!is_partition(input_in_partition)) continue;
+ if (fnmatch(pattern.c_str(), input_in_partition.c_str(), fnm_flags) == 0) {
+ return true;
+ }
+ }
}
-
- if (!is_partition(pattern)) return false;
- if (!is_partition(input)) return false;
- return fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0;
+ return false;
}
#ifndef __ANDROID_VNDK__
auto __for_testing_only__fs_config_cmp = fs_config_cmp;
@@ -334,7 +323,7 @@
while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) {
char* prefix;
- uint16_t host_len = get2LE((const uint8_t*)&header.len);
+ uint16_t host_len = header.len;
ssize_t len, remainder = host_len - sizeof(header);
if (remainder <= 0) {
ALOGE("%s len is corrupted", conf[which][dir]);
@@ -359,10 +348,10 @@
if (fs_config_cmp(dir, prefix, len, path, plen)) {
free(prefix);
close(fd);
- *uid = get2LE((const uint8_t*)&(header.uid));
- *gid = get2LE((const uint8_t*)&(header.gid));
- *mode = (*mode & (~07777)) | get2LE((const uint8_t*)&(header.mode));
- *capabilities = get8LE((const uint8_t*)&(header.capabilities));
+ *uid = header.uid;
+ *gid = header.gid;
+ *mode = (*mode & (~07777)) | header.mode;
+ *capabilities = header.capabilities;
return;
}
free(prefix);
@@ -380,21 +369,3 @@
*mode = (*mode & (~07777)) | pc->mode;
*capabilities = pc->capabilities;
}
-
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc) {
- struct fs_path_config_from_file* p = (struct fs_path_config_from_file*)buffer;
- size_t len = ALIGN(sizeof(*p) + strlen(pc->prefix) + 1, sizeof(uint64_t));
-
- if ((length < len) || (len > UINT16_MAX)) {
- return -ENOSPC;
- }
- memset(p, 0, len);
- uint16_t host_len = len;
- p->len = get2LE((const uint8_t*)&host_len);
- p->mode = get2LE((const uint8_t*)&(pc->mode));
- p->uid = get2LE((const uint8_t*)&(pc->uid));
- p->gid = get2LE((const uint8_t*)&(pc->gid));
- p->capabilities = get8LE((const uint8_t*)&(pc->capabilities));
- strcpy(p->prefix, pc->prefix);
- return len;
-}
diff --git a/libcutils/fs_config.h b/libcutils/fs_config.h
new file mode 100644
index 0000000..66ad48b
--- /dev/null
+++ b/libcutils/fs_config.h
@@ -0,0 +1,37 @@
+/*
+ * 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>
+
+// Binary format for the runtime <partition>/etc/fs_config_(dirs|files) filesystem override files.
+struct fs_path_config_from_file {
+ uint16_t len;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t gid;
+ uint64_t capabilities;
+ char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+struct fs_path_config {
+ unsigned mode;
+ unsigned uid;
+ unsigned gid;
+ uint64_t capabilities;
+ const char* prefix;
+};
diff --git a/libcutils/fs_config_test.cpp b/libcutils/fs_config_test.cpp
index c26315f..9627152 100644
--- a/libcutils/fs_config_test.cpp
+++ b/libcutils/fs_config_test.cpp
@@ -25,7 +25,8 @@
#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
+
+#include "fs_config.h"
extern const fs_path_config* __for_testing_only__android_dirs;
extern const fs_path_config* __for_testing_only__android_files;
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 8926491..8a9a1ff 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -19,44 +19,17 @@
** by the device side of adb.
*/
-#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
-#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#pragma once
#include <stdint.h>
#include <sys/cdefs.h>
-#include <sys/types.h>
#if defined(__BIONIC__)
#include <linux/capability.h>
#else // defined(__BIONIC__)
-#include "android_filesystem_capability.h"
+#include <private/android_filesystem_capability.h>
#endif // defined(__BIONIC__)
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
-/*
- * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
- * filesystem override files.
- */
-
-/* The following structure is stored little endian */
-struct fs_path_config_from_file {
- uint16_t len;
- uint16_t mode;
- uint16_t uid;
- uint16_t gid;
- uint64_t capabilities;
- char prefix[];
-} __attribute__((__aligned__(sizeof(uint64_t))));
-
-struct fs_path_config {
- unsigned mode;
- unsigned uid;
- unsigned gid;
- uint64_t capabilities;
- const char* prefix;
-};
-
/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
__BEGIN_DECLS
@@ -74,8 +47,4 @@
void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
unsigned* mode, uint64_t* capabilities);
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
-
__END_DECLS
-
-#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
index 6ae79d4..614510c 100644
--- a/libion/ion_4.12.h
+++ b/libion/ion_4.12.h
@@ -1,125 +1,50 @@
-/*
- * Adapted from drivers/staging/android/uapi/ion.h
- *
- * Copyright (C) 2011 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
#ifndef _UAPI_LINUX_ION_NEW_H
#define _UAPI_LINUX_ION_NEW_H
-
#include <linux/ioctl.h>
#include <linux/types.h>
-
#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
-
-/**
- * DOC: Ion Userspace API
- *
- * create a client by opening /dev/ion
- * most operations handled via following ioctls
- *
- */
-
-/**
- * struct ion_new_allocation_data - metadata passed from userspace for allocations
- * @len: size of the allocation
- * @heap_id_mask: mask of heap ids to allocate from
- * @flags: flags passed to heap
- * @handle: pointer that will be populated with a cookie to use to
- * refer to this allocation
- *
- * Provided by userspace as an argument to the ioctl - added _new to denote
- * this belongs to the new ION interface.
- */
struct ion_new_allocation_data {
- __u64 len;
- __u32 heap_id_mask;
- __u32 flags;
- __u32 fd;
- __u32 unused;
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
};
-
#define MAX_HEAP_NAME 32
-
-/**
- * struct ion_heap_data - data about a heap
- * @name - first 32 characters of the heap name
- * @type - heap type
- * @heap_id - heap id for the heap
- */
struct ion_heap_data {
- char name[MAX_HEAP_NAME];
- __u32 type;
- __u32 heap_id;
- __u32 reserved0;
- __u32 reserved1;
- __u32 reserved2;
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
};
-
-/**
- * struct ion_heap_query - collection of data about all heaps
- * @cnt - total number of heaps to be copied
- * @heaps - buffer to copy heap data
- */
struct ion_heap_query {
- __u32 cnt; /* Total number of heaps to be copied */
- __u32 reserved0; /* align to 64bits */
- __u64 heaps; /* buffer to be populated */
- __u32 reserved1;
- __u32 reserved2;
+ __u32 cnt;
+ __u32 reserved0;
+ __u64 heaps;
+ __u32 reserved1;
+ __u32 reserved2;
};
-
#define ION_IOC_MAGIC 'I'
-
-/**
- * DOC: ION_IOC_NEW_ALLOC - allocate memory
- *
- * Takes an ion_allocation_data struct and returns it with the handle field
- * populated with the opaque handle for the allocation.
- * TODO: This IOCTL will clash by design; however, only one of
- * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
- * so this should not conflict.
- */
#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
-
-/**
- * DOC: ION_IOC_FREE - free memory
- *
- * Takes an ion_handle_data struct and frees the handle.
- *
- * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
- *
- * Takes an ion_fd_data struct with the handle field populated with a valid
- * opaque handle. Returns the struct with the fd field set to a file
- * descriptor open in the current address space. This file descriptor
- * can then be passed to another process. The corresponding opaque handle can
- * be retrieved via ION_IOC_IMPORT.
- *
- * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_HEAP_QUERY - information about available heaps
- *
- * Takes an ion_heap_query structure and populates information about
- * available Ion heaps.
- */
#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
-
-#endif /* _UAPI_LINUX_ION_NEW_H */
+#endif
diff --git a/libion/original-kernel-headers/linux/ion_4.12.h b/libion/original-kernel-headers/linux/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion_4.12.h
@@ -0,0 +1,125 @@
+/*
+ * Adapted from drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_new_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @heap_id_mask: mask of heap ids to allocate from
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to
+ * refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl - added _new to denote
+ * this belongs to the new ION interface.
+ */
+struct ion_new_allocation_data {
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
+};
+
+#define MAX_HEAP_NAME 32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+ __u32 cnt; /* Total number of heaps to be copied */
+ __u32 reserved0; /* align to 64bits */
+ __u64 heaps; /* buffer to be populated */
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_NEW_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ * TODO: This IOCTL will clash by design; however, only one of
+ * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
+ * so this should not conflict.
+ */
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ *
+ * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ *
+ * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
index 56afea4..79b4680 100644
--- a/libkeyutils/mini_keyctl_utils.cpp
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
#include <errno.h>
+#include <error.h>
#include <sys/types.h>
#include <unistd.h>
@@ -29,7 +30,6 @@
#include <vector>
#include <android-base/file.h>
-#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
@@ -47,22 +47,18 @@
// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
// information in the descritption section depending on the key type, only the first word in the
// keyring description is used for searching.
-static bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
- if (!keyring_id) {
- LOG(ERROR) << "keyring_id is null";
- return false;
- }
-
+static key_serial_t GetKeyringIdOrDie(const std::string& keyring_desc) {
// If the keyring id is already a hex number, directly convert it to keyring id
- if (android::base::ParseInt(keyring_desc.c_str(), keyring_id)) {
- return true;
+ key_serial_t keyring_id;
+ if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {
+ return keyring_id;
}
// Only keys allowed by SELinux rules will be shown here.
std::ifstream proc_keys_file("/proc/keys");
if (!proc_keys_file.is_open()) {
- PLOG(ERROR) << "Failed to open /proc/keys";
- return false;
+ error(1, errno, "Failed to open /proc/keys");
+ return -1;
}
std::string line;
@@ -71,7 +67,7 @@
if (tokens.size() < 9) {
continue;
}
- std::string key_id = tokens[0];
+ std::string key_id = "0x" + tokens[0];
std::string key_type = tokens[7];
// The key description may contain space.
std::string key_desc_prefix = tokens[8];
@@ -80,21 +76,19 @@
if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
continue;
}
- *keyring_id = std::stoi(key_id, nullptr, 16);
- return true;
+ if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {
+ error(1, 0, "Unexpected key format in /proc/keys: %s", key_id.c_str());
+ return -1;
+ }
+ return keyring_id;
}
- return false;
+ return -1;
}
int Unlink(key_serial_t key, const std::string& keyring) {
- key_serial_t keyring_id;
- if (!GetKeyringId(keyring, &keyring_id)) {
- LOG(ERROR) << "Can't find keyring " << keyring;
- return 1;
- }
-
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
if (keyctl_unlink(key, keyring_id) < 0) {
- PLOG(ERROR) << "Failed to unlink key 0x" << std::hex << key << " from keyring " << keyring_id;
+ error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str());
return 1;
}
return 0;
@@ -103,63 +97,49 @@
int Add(const std::string& type, const std::string& desc, const std::string& data,
const std::string& keyring) {
if (data.size() > kMaxCertSize) {
- LOG(ERROR) << "Certificate too large";
+ error(1, 0, "Certificate too large");
return 1;
}
- key_serial_t keyring_id;
- if (!GetKeyringId(keyring, &keyring_id)) {
- LOG(ERROR) << "Can not find keyring id";
- return 1;
- }
-
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
if (key < 0) {
- PLOG(ERROR) << "Failed to add key";
+ error(1, errno, "Failed to add key");
return 1;
}
- LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+ std::cout << key << std::endl;
return 0;
}
int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
- key_serial_t keyring_id;
- if (!GetKeyringId(keyring, &keyring_id)) {
- LOG(ERROR) << "Can not find keyring id";
- return 1;
- }
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
// read from stdin to get the certificates
std::istreambuf_iterator<char> begin(std::cin), end;
std::string data(begin, end);
if (data.size() > kMaxCertSize) {
- LOG(ERROR) << "Certificate too large";
+ error(1, 0, "Certificate too large");
return 1;
}
key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
if (key < 0) {
- PLOG(ERROR) << "Failed to add key";
+ error(1, errno, "Failed to add key");
return 1;
}
- LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+ std::cout << key << std::endl;
return 0;
}
int RestrictKeyring(const std::string& keyring) {
- key_serial_t keyring_id;
- if (!GetKeyringId(keyring, &keyring_id)) {
- LOG(ERROR) << "Cannot find keyring id";
- return 1;
- }
-
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
- PLOG(ERROR) << "Cannot restrict keyring " << keyring;
+ error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str());
return 1;
}
return 0;
@@ -172,11 +152,11 @@
context.resize(kMaxSupportedSize);
long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
if (retval < 0) {
- PLOG(ERROR) << "Cannot get security context of key 0x" << std::hex << key;
+ error(1, errno, "Cannot get security context of key %x", key);
return std::string();
}
if (retval > kMaxSupportedSize) {
- LOG(ERROR) << "The key has unexpectedly long security context than " << kMaxSupportedSize;
+ error(1, 0, "The key has unexpectedly long security context than %d", kMaxSupportedSize);
return std::string();
}
context.resize(retval);
diff --git a/liblog/Android.bp b/liblog/Android.bp
index da475cb..53d3ab3 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -45,6 +45,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
stl: "none",
@@ -67,6 +68,7 @@
name: "liblog",
host_supported: true,
recovery_available: true,
+ native_bridge_supported: true,
srcs: liblog_sources,
target: {
@@ -138,6 +140,7 @@
llndk_library {
name: "liblog",
+ native_bridge_supported: true,
symbol_file: "liblog.map.txt",
export_include_dirs: ["include_vndk"],
}
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index b2f0ed9..935590d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -122,9 +122,10 @@
*
* Most callers should use
* [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
- * `<assert.h>` instead, or the `__assert` and `__assert2` functions provided by
- * bionic if more control is needed. They support automatically including the
- * source filename and line number more conveniently than this function.
+ * `<assert.h>` instead, or the `__assert` and `__assert2` functions
+ * provided by bionic if more control is needed. They support automatically
+ * including the source filename and line number more conveniently than this
+ * function.
*/
void __android_log_assert(const char* cond, const char* tag, const char* fmt,
...)
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
index 468a498..b7279d1 100644
--- a/liblog/log_portability.h
+++ b/liblog/log_portability.h
@@ -19,16 +19,6 @@
#include <sys/cdefs.h>
#include <unistd.h>
-/*
- * Declare this library function as reimplementation.
- * Prevent circular dependencies, but allow _real_ library to hijack
- */
-#if defined(_WIN32)
-#define LIBLOG_WEAK static /* Accept that it is totally private */
-#else
-#define LIBLOG_WEAK extern "C" __attribute__((weak, visibility("default")))
-#endif
-
/* possible missing definitions in sys/cdefs.h */
/* DECLS */
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index bc056cb..6b5ea4c 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -33,6 +33,7 @@
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
+#include <wchar.h>
#include <cutils/list.h>
#include <log/log.h>
@@ -1134,66 +1135,13 @@
}
/*
- * One utf8 character at a time
- *
- * Returns the length of the utf8 character in the buffer,
- * or -1 if illegal or truncated
- *
- * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
- * can not remove from here because of library circular dependencies.
- * Expect one-day utf8_character_length with the same signature could
- * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
- * propagate globally.
- */
-LIBLOG_WEAK ssize_t utf8_character_length(const char* src, size_t len) {
- const char* cur = src;
- const char first_char = *cur++;
- static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
- int32_t mask, to_ignore_mask;
- size_t num_to_read;
- uint32_t utf32;
-
- if ((first_char & 0x80) == 0) { /* ASCII */
- return first_char ? 1 : -1;
- }
-
- /*
- * (UTF-8's character must not be like 10xxxxxx,
- * but 110xxxxx, 1110xxxx, ... or 1111110x)
- */
- if ((first_char & 0x40) == 0) {
- return -1;
- }
-
- for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
- num_to_read < 5 && (first_char & mask); num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
- if (num_to_read > len) {
- return -1;
- }
- if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
- return -1;
- }
- utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
- }
- /* "first_char" must be (110xxxxx - 11110xxx) */
- if (num_to_read >= 5) {
- return -1;
- }
- to_ignore_mask |= mask;
- utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
- if (utf32 > kUnicodeMaxCodepoint) {
- return -1;
- }
- return num_to_read;
-}
-
-/*
* Convert to printable from message to p buffer, return string length. If p is
* NULL, do not copy, but still return the expected string length.
*/
-static size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+size_t convertPrintable(char* p, const char* message, size_t messageLen) {
char* begin = p;
bool print = p != NULL;
+ mbstate_t mb_state = {};
while (messageLen) {
char buf[6];
@@ -1201,11 +1149,10 @@
if ((size_t)len > messageLen) {
len = messageLen;
}
- len = utf8_character_length(message, len);
+ len = mbrtowc(nullptr, message, len, &mb_state);
if (len < 0) {
- snprintf(buf, sizeof(buf), ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
- *message & 0377);
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
len = 1;
} else {
buf[0] = '\0';
@@ -1225,7 +1172,7 @@
} else if (*message == '\\') {
strcpy(buf, "\\\\");
} else if ((*message < ' ') || (*message & 0x80)) {
- snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
}
}
if (!buf[0]) {
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 50755ce..d9d1a21 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -62,6 +62,7 @@
"log_system_test.cpp",
"log_time_test.cpp",
"log_wrap_test.cpp",
+ "logprint_test.cpp",
],
shared_libs: [
"libcutils",
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 7d11ccf..21d12a1 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -56,8 +56,8 @@
*/
static void BM_log_maximum_retry(benchmark::State& state) {
while (state.KeepRunning()) {
- LOG_FAILURE_RETRY(__android_log_print(
- ANDROID_LOG_INFO, "BM_log_maximum_retry", "%zu", state.iterations()));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_INFO, "BM_log_maximum_retry", "%" PRIu64,
+ state.iterations()));
}
}
BENCHMARK(BM_log_maximum_retry);
@@ -69,8 +69,7 @@
*/
static void BM_log_maximum(benchmark::State& state) {
while (state.KeepRunning()) {
- __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%zu",
- state.iterations());
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%" PRIu64, state.iterations());
}
}
BENCHMARK(BM_log_maximum);
@@ -286,7 +285,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
if (((uintptr_t)&buffer->pmsg_header) & 7) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -361,7 +360,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -436,7 +435,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
if (((uintptr_t)&buffer->pmsg_header) & 7) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -509,7 +508,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -560,7 +559,7 @@
/* performance test */
static void BM_sprintf_overhead(benchmark::State& state) {
while (state.KeepRunning()) {
- test_print("BM_sprintf_overhead:%zu", state.iterations());
+ test_print("BM_sprintf_overhead:%" PRIu64, state.iterations());
state.PauseTiming();
logd_yield();
state.ResumeTiming();
@@ -575,8 +574,7 @@
*/
static void BM_log_print_overhead(benchmark::State& state) {
while (state.KeepRunning()) {
- __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%zu",
- state.iterations());
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%" PRIu64, state.iterations());
state.PauseTiming();
logd_yield();
state.ResumeTiming();
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
new file mode 100644
index 0000000..7ca02ac
--- /dev/null
+++ b/liblog/tests/logprint_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 <gtest/gtest.h>
+
+size_t convertPrintable(char* p, const char* message, size_t messageLen);
+
+TEST(liblog, convertPrintable_ascii) {
+ auto input = "easy string, output same";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_escapes) {
+ // Note that \t is not escaped.
+ auto input = "escape\a\b\t\v\f\r\\";
+ auto expected_output = "escape\\a\\b\t\\v\\f\\r\\\\";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_validutf8) {
+ auto input = u8"¢ह€𐍈";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_invalidutf8) {
+ auto input = "\x80\xC2\x01\xE0\xA4\x06\xE0\x06\xF0\x90\x8D\x06\xF0\x90\x06\xF0\x0E";
+ auto expected_output =
+ "\\x80\\xC2\\x01\\xE0\\xA4\\x06\\xE0\\x06\\xF0\\x90\\x8D\\x06\\xF0\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_mixed) {
+ auto input =
+ u8"\x80\xC2¢ह€𐍈\x01\xE0\xA4\x06¢ह€𐍈\xE0\x06\a\b\xF0\x90¢ह€𐍈\x8D\x06\xF0\t\t\x90\x06\xF0\x0E";
+ auto expected_output =
+ u8"\\x80\\xC2¢ह€𐍈\\x01\\xE0\\xA4\\x06¢ह€𐍈\\xE0\\x06\\a\\b\\xF0\\x90¢ह€𐍈\\x8D\\x06\\xF0\t\t"
+ u8"\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 8f01e50..f1abdd2 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -22,6 +22,7 @@
cc_library {
name: "libmemunreachable",
+ vendor_available: true,
defaults: ["libmemunreachable_defaults"],
srcs: [
"Allocator.cpp",
@@ -48,10 +49,23 @@
},
export_include_dirs: ["include"],
local_include_dirs: ["include"],
+ version_script: "libmemunreachable.map",
+}
+
+// Integration test that runs against the public API of libmemunreachable
+cc_test {
+ name: "memunreachable_test",
+ defaults: ["libmemunreachable_defaults"],
+ srcs: [
+ "tests/MemUnreachable_test.cpp",
+ ],
+ shared_libs: ["libmemunreachable"],
+
+ test_suites: ["device-tests"],
}
cc_test {
- name: "memunreachable_test",
+ name: "memunreachable_unit_test",
defaults: ["libmemunreachable_defaults"],
host_supported: true,
srcs: [
@@ -67,8 +81,9 @@
"tests/MemUnreachable_test.cpp",
"tests/ThreadCapture_test.cpp",
],
- shared_libs: [
+ static_libs: [
"libmemunreachable",
+ "libc_malloc_debug_backtrace",
],
},
host: {
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 299c320..ce937fd 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -280,6 +280,12 @@
}
bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
+ if (info.version > 0) {
+ MEM_ALOGE("unsupported UnreachableMemoryInfo.version %zu in GetUnreachableMemory",
+ info.version);
+ return false;
+ }
+
int parent_pid = getpid();
int parent_tid = gettid();
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index c028eab..011443f 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -28,38 +28,45 @@
namespace android {
struct Leak {
- uintptr_t begin;
- size_t size;
+ uintptr_t begin = 0;
+ size_t size = 0;
- size_t referenced_count;
- size_t referenced_size;
+ size_t referenced_count = 0;
+ size_t referenced_size = 0;
- size_t similar_count;
- size_t similar_size;
- size_t similar_referenced_count;
- size_t similar_referenced_size;
+ size_t similar_count = 0;
+ size_t similar_size = 0;
+ size_t similar_referenced_count = 0;
+ size_t similar_referenced_size = 0;
- size_t total_size;
+ size_t total_size = 0;
static const size_t contents_length = 32;
- char contents[contents_length];
+ char contents[contents_length] = {};
struct Backtrace {
- size_t num_frames;
+ size_t num_frames = 0;
static const size_t max_frames = 16;
- uintptr_t frames[max_frames];
+ uintptr_t frames[max_frames] = {};
+
+ size_t reserved[8] = {};
} backtrace;
+ size_t reserved[8] = {};
+
std::string ToString(bool log_contents) const;
};
struct UnreachableMemoryInfo {
std::vector<Leak> leaks;
- size_t num_leaks;
- size_t leak_bytes;
- size_t num_allocations;
- size_t allocation_bytes;
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ size_t num_allocations = 0;
+ size_t allocation_bytes = 0;
+
+ size_t version = 0; // Must be 0
+ size_t reserved[8] = {};
UnreachableMemoryInfo() {}
~UnreachableMemoryInfo();
diff --git a/libmemunreachable/libmemunreachable.map b/libmemunreachable/libmemunreachable.map
new file mode 100644
index 0000000..0d0d954
--- /dev/null
+++ b/libmemunreachable/libmemunreachable.map
@@ -0,0 +1,13 @@
+LIBMEMUNREACHABLE {
+ global:
+ LogUnreachableMemory;
+ NoLeaks;
+ extern "C++" {
+ android::GetUnreachableMemory*;
+ android::GetUnreachableMemoryString*;
+ android::Leak::*;
+ android::UnreachableMemoryInfo::*;
+ };
+ local:
+ *;
+};
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index bba0c6d..9cb1623 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -47,7 +47,8 @@
// Trick the compiler into thinking a value on the stack is still referenced.
static void Ref(void** ptr) {
- write(0, ptr, 0);
+ void** volatile storage;
+ storage = ptr;
}
class MemunreachableTest : public ::testing::Test {
@@ -264,4 +265,12 @@
ASSERT_TRUE(LogUnreachableMemory(true, 100));
}
+TEST_F(MemunreachableTest, version) {
+ UnreachableMemoryInfo info;
+ info.version = 1;
+
+ ASSERT_FALSE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+}
+
} // namespace android
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
new file mode 100644
index 0000000..a2824d1
--- /dev/null
+++ b/libmodprobe/Android.bp
@@ -0,0 +1,30 @@
+cc_library_static {
+ name: "libmodprobe",
+ cflags: [
+ "-Werror",
+ ],
+ recovery_available: true,
+ srcs: [
+ "libmodprobe.cpp",
+ "libmodprobe_ext.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ export_include_dirs: ["include/"],
+}
+
+cc_test {
+ name: "libmodprobe_tests",
+ cflags: ["-Werror"],
+ shared_libs: [
+ "libbase",
+ ],
+ local_include_dirs: ["include/"],
+ srcs: [
+ "libmodprobe_test.cpp",
+ "libmodprobe.cpp",
+ "libmodprobe_ext_test.cpp",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
new file mode 100644
index 0000000..0ec766a
--- /dev/null
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+class Modprobe {
+ public:
+ Modprobe(const std::vector<std::string>&);
+
+ bool LoadListedModules();
+ bool LoadWithAliases(const std::string& module_name, bool strict);
+
+ private:
+ std::string MakeCanonical(const std::string& module_path);
+ bool InsmodWithDeps(const std::string& module_name);
+ bool Insmod(const std::string& path_name);
+ std::vector<std::string> GetDependencies(const std::string& module);
+ bool ModuleExists(const std::string& module_name);
+
+ bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
+ bool ParseAliasCallback(const std::vector<std::string>& args);
+ bool ParseSoftdepCallback(const std::vector<std::string>& args);
+ bool ParseLoadCallback(const std::vector<std::string>& args);
+ bool ParseOptionsCallback(const std::vector<std::string>& args);
+ void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
+
+ std::vector<std::pair<std::string, std::string>> module_aliases_;
+ std::unordered_map<std::string, std::vector<std::string>> module_deps_;
+ std::vector<std::pair<std::string, std::string>> module_pre_softdep_;
+ std::vector<std::pair<std::string, std::string>> module_post_softdep_;
+ std::vector<std::string> module_load_;
+ std::unordered_map<std::string, std::string> module_options_;
+};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
new file mode 100644
index 0000000..01cf2e3
--- /dev/null
+++ b/libmodprobe/libmodprobe.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <modprobe/modprobe.h>
+
+#include <fnmatch.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+std::string Modprobe::MakeCanonical(const std::string& module_path) {
+ auto start = module_path.find_last_of('/');
+ if (start == std::string::npos) {
+ start = 0;
+ } else {
+ start += 1;
+ }
+ auto end = module_path.size();
+ if (android::base::EndsWith(module_path, ".ko")) {
+ end -= 3;
+ }
+ if ((end - start) <= 1) {
+ LOG(ERROR) << "malformed module name: " << module_path;
+ return "";
+ }
+ std::string module_name = module_path.substr(start, end - start);
+ // module names can have '-', but their file names will have '_'
+ std::replace(module_name.begin(), module_name.end(), '-', '_');
+ return module_name;
+}
+
+bool Modprobe::ParseDepCallback(const std::string& base_path,
+ const std::vector<std::string>& args) {
+ std::vector<std::string> deps;
+ std::string prefix = "";
+
+ // Set first item as our modules path
+ std::string::size_type pos = args[0].find(':');
+ if (args[0][0] != '/') {
+ prefix = base_path + "/";
+ }
+ if (pos != std::string::npos) {
+ deps.emplace_back(prefix + args[0].substr(0, pos));
+ } else {
+ LOG(ERROR) << "dependency lines must start with name followed by ':'";
+ }
+
+ // Remaining items are dependencies of our module
+ for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
+ if ((*arg)[0] != '/') {
+ prefix = base_path + "/";
+ } else {
+ prefix = "";
+ }
+ deps.push_back(prefix + *arg);
+ }
+
+ std::string canonical_name = MakeCanonical(args[0].substr(0, pos));
+ if (canonical_name.empty()) {
+ return false;
+ }
+ this->module_deps_[canonical_name] = deps;
+
+ return true;
+}
+
+bool Modprobe::ParseAliasCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& type = *it++;
+
+ if (type != "alias") {
+ LOG(ERROR) << "non-alias line encountered in modules.alias, found " << type;
+ return false;
+ }
+
+ if (args.size() != 3) {
+ LOG(ERROR) << "alias lines in modules.alias must have 3 entries, not " << args.size();
+ return false;
+ }
+
+ const std::string& alias = *it++;
+ const std::string& module_name = *it++;
+ this->module_aliases_.emplace_back(alias, module_name);
+
+ return true;
+}
+
+bool Modprobe::ParseSoftdepCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& type = *it++;
+ std::string state = "";
+
+ if (type != "softdep") {
+ LOG(ERROR) << "non-softdep line encountered in modules.softdep, found " << type;
+ return false;
+ }
+
+ if (args.size() < 4) {
+ LOG(ERROR) << "softdep lines in modules.softdep must have at least 4 entries";
+ return false;
+ }
+
+ const std::string& module = *it++;
+ while (it != args.end()) {
+ const std::string& token = *it++;
+ if (token == "pre:" || token == "post:") {
+ state = token;
+ continue;
+ }
+ if (state == "") {
+ LOG(ERROR) << "malformed modules.softdep at token " << token;
+ return false;
+ }
+ if (state == "pre:") {
+ this->module_pre_softdep_.emplace_back(module, token);
+ } else {
+ this->module_post_softdep_.emplace_back(module, token);
+ }
+ }
+
+ return true;
+}
+
+bool Modprobe::ParseLoadCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& module = *it++;
+
+ const std::string& canonical_name = MakeCanonical(module);
+ if (canonical_name.empty()) {
+ return false;
+ }
+ this->module_load_.emplace_back(canonical_name);
+
+ return true;
+}
+
+bool Modprobe::ParseOptionsCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& type = *it++;
+
+ if (type != "options") {
+ LOG(ERROR) << "non-options line encountered in modules.options";
+ return false;
+ }
+
+ if (args.size() < 2) {
+ LOG(ERROR) << "lines in modules.options must have at least 2 entries, not " << args.size();
+ return false;
+ }
+
+ const std::string& module = *it++;
+ std::string options = "";
+
+ const std::string& canonical_name = MakeCanonical(module);
+ if (canonical_name.empty()) {
+ return false;
+ }
+
+ while (it != args.end()) {
+ options += *it++;
+ if (it != args.end()) {
+ options += " ";
+ }
+ }
+
+ auto [unused, inserted] = this->module_options_.emplace(canonical_name, options);
+ if (!inserted) {
+ LOG(ERROR) << "multiple options lines present for module " << module;
+ return false;
+ }
+ return true;
+}
+
+void Modprobe::ParseCfg(const std::string& cfg,
+ std::function<bool(const std::vector<std::string>&)> f) {
+ std::string cfg_contents;
+ if (!android::base::ReadFileToString(cfg, &cfg_contents, false)) {
+ return;
+ }
+
+ std::vector<std::string> lines = android::base::Split(cfg_contents, "\n");
+ for (const std::string line : lines) {
+ if (line.empty() || line[0] == '#') {
+ continue;
+ }
+ const std::vector<std::string> args = android::base::Split(line, " ");
+ if (args.empty()) continue;
+ f(args);
+ }
+ return;
+}
+
+Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
+ using namespace std::placeholders;
+
+ for (const auto& base_path : base_paths) {
+ auto alias_callback = std::bind(&Modprobe::ParseAliasCallback, this, _1);
+ ParseCfg(base_path + "/modules.alias", alias_callback);
+
+ auto dep_callback = std::bind(&Modprobe::ParseDepCallback, this, base_path, _1);
+ ParseCfg(base_path + "/modules.dep", dep_callback);
+
+ auto softdep_callback = std::bind(&Modprobe::ParseSoftdepCallback, this, _1);
+ ParseCfg(base_path + "/modules.softdep", softdep_callback);
+
+ auto load_callback = std::bind(&Modprobe::ParseLoadCallback, this, _1);
+ ParseCfg(base_path + "/modules.load", load_callback);
+
+ auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
+ ParseCfg(base_path + "/modules.options", options_callback);
+ }
+}
+
+std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
+ auto it = module_deps_.find(module);
+ if (it == module_deps_.end()) {
+ return {};
+ }
+ return it->second;
+}
+
+bool Modprobe::InsmodWithDeps(const std::string& module_name) {
+ if (module_name.empty()) {
+ LOG(ERROR) << "Need valid module name, given: " << module_name;
+ return false;
+ }
+
+ auto dependencies = GetDependencies(module_name);
+ if (dependencies.empty()) {
+ LOG(ERROR) << "Module " << module_name << " not in dependency file";
+ return false;
+ }
+
+ // load module dependencies in reverse order
+ for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
+ const std::string& canonical_name = MakeCanonical(*dep);
+ if (canonical_name.empty()) {
+ return false;
+ }
+ if (!LoadWithAliases(canonical_name, true)) {
+ return false;
+ }
+ }
+
+ // try to load soft pre-dependencies
+ for (const auto& [module, softdep] : module_pre_softdep_) {
+ if (module_name == module) {
+ LoadWithAliases(softdep, false);
+ }
+ }
+
+ // load target module itself with args
+ if (!Insmod(dependencies[0])) {
+ return false;
+ }
+
+ // try to load soft post-dependencies
+ for (const auto& [module, softdep] : module_post_softdep_) {
+ if (module_name == module) {
+ LoadWithAliases(softdep, false);
+ }
+ }
+
+ return true;
+}
+
+bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) {
+ std::set<std::string> modules_to_load = {module_name};
+ bool module_loaded = false;
+
+ // use aliases to expand list of modules to load (multiple modules
+ // may alias themselves to the requested name)
+ for (const auto& [alias, aliased_module] : module_aliases_) {
+ if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;
+ modules_to_load.emplace(aliased_module);
+ }
+
+ // attempt to load all modules aliased to this name
+ for (const auto& module : modules_to_load) {
+ if (!ModuleExists(module)) continue;
+ if (InsmodWithDeps(module)) module_loaded = true;
+ }
+
+ if (strict && !module_loaded) {
+ LOG(ERROR) << "LoadWithAliases did not find a module for " << module_name;
+ return false;
+ }
+ return true;
+}
+
+bool Modprobe::LoadListedModules() {
+ for (const auto& module : module_load_) {
+ if (!LoadWithAliases(module, true)) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
new file mode 100644
index 0000000..5f3a04d
--- /dev/null
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <modprobe/modprobe.h>
+
+bool Modprobe::Insmod(const std::string& path_name) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (fd == -1) {
+ LOG(ERROR) << "Could not open module '" << path_name << "'";
+ return false;
+ }
+
+ std::string options = "";
+ auto options_iter = module_options_.find(MakeCanonical(path_name));
+ if (options_iter != module_options_.end()) {
+ options = options_iter->second;
+ }
+
+ LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\"";
+ int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
+ if (ret != 0) {
+ if (errno == EEXIST) {
+ // Module already loaded
+ return true;
+ }
+ LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
+ return false;
+ }
+
+ LOG(INFO) << "Loaded kernel module " << path_name;
+ return true;
+}
+
+bool Modprobe::ModuleExists(const std::string& module_name) {
+ struct stat fileStat;
+ auto deps = GetDependencies(module_name);
+ if (deps.empty()) {
+ // missing deps can happen in the case of an alias
+ return false;
+ }
+ if (stat(deps.front().c_str(), &fileStat)) {
+ return false;
+ }
+ if (!S_ISREG(fileStat.st_mode)) {
+ return false;
+ }
+ return true;
+}
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
new file mode 100644
index 0000000..0f073cb
--- /dev/null
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include <modprobe/modprobe.h>
+
+#include "libmodprobe_test.h"
+
+bool Modprobe::Insmod(const std::string& path_name) {
+ auto deps = GetDependencies(MakeCanonical(path_name));
+ if (deps.empty()) {
+ return false;
+ }
+ if (std::find(test_modules.begin(), test_modules.end(), deps.front()) == test_modules.end()) {
+ return false;
+ }
+ for (auto it = modules_loaded.begin(); it != modules_loaded.end(); ++it) {
+ if (android::base::StartsWith(*it, path_name)) {
+ return true;
+ }
+ }
+ std::string options;
+ auto options_iter = module_options_.find(MakeCanonical(path_name));
+ if (options_iter != module_options_.end()) {
+ options = " " + options_iter->second;
+ }
+ modules_loaded.emplace_back(path_name + options);
+ return true;
+}
+
+bool Modprobe::ModuleExists(const std::string& module_name) {
+ auto deps = GetDependencies(module_name);
+ if (deps.empty()) {
+ // missing deps can happen in the case of an alias
+ return false;
+ }
+ return std::find(test_modules.begin(), test_modules.end(), deps.front()) != test_modules.end();
+}
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
new file mode 100644
index 0000000..481658d
--- /dev/null
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 <functional>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include <modprobe/modprobe.h>
+
+#include "libmodprobe_test.h"
+
+// Used by libmodprobe_ext_test to check if requested modules are present.
+std::vector<std::string> test_modules;
+
+// Used by libmodprobe_ext_test to report which modules would have been loaded.
+std::vector<std::string> modules_loaded;
+
+TEST(libmodprobe, Test) {
+ test_modules = {
+ "/test1.ko", "/test2.ko", "/test3.ko", "/test4.ko", "/test5.ko",
+ "/test6.ko", "/test7.ko", "/test8.ko", "/test9.ko", "/test10.ko",
+ "/test11.ko", "/test12.ko", "/test13.ko", "/test14.ko", "/test15.ko",
+ };
+
+ std::vector<std::string> expected_modules_loaded = {
+ "/test14.ko",
+ "/test15.ko",
+ "/test3.ko",
+ "/test4.ko",
+ "/test1.ko",
+ "/test6.ko",
+ "/test2.ko",
+ "/test5.ko",
+ "/test8.ko",
+ "/test7.ko param1=4",
+ "/test9.ko param_x=1 param_y=2 param_z=3",
+ "/test10.ko",
+ "/test12.ko",
+ "/test11.ko",
+ "/test13.ko",
+ };
+
+ const std::string modules_dep =
+ "test1.ko:\n"
+ "test2.ko:\n"
+ "test3.ko:\n"
+ "test4.ko: test3.ko\n"
+ "test5.ko: test2.ko test6.ko\n"
+ "test6.ko:\n"
+ "test7.ko:\n"
+ "test8.ko:\n"
+ "test9.ko:\n"
+ "test10.ko:\n"
+ "test11.ko:\n"
+ "test12.ko:\n"
+ "test13.ko:\n"
+ "test14.ko:\n"
+ "test15.ko:\n";
+
+ const std::string modules_softdep =
+ "softdep test7 pre: test8\n"
+ "softdep test9 post: test10\n"
+ "softdep test11 pre: test12 post: test13\n"
+ "softdep test3 pre: test141516\n";
+
+ const std::string modules_alias =
+ "# Aliases extracted from modules themselves.\n"
+ "\n"
+ "alias test141516 test14\n"
+ "alias test141516 test15\n"
+ "alias test141516 test16\n";
+
+ const std::string modules_options =
+ "options test7.ko param1=4\n"
+ "options test9.ko param_x=1 param_y=2 param_z=3\n"
+ "options test100.ko param_1=1\n";
+
+ const std::string modules_load =
+ "test4.ko\n"
+ "test1.ko\n"
+ "test3.ko\n"
+ "test5.ko\n"
+ "test7.ko\n"
+ "test9.ko\n"
+ "test11.ko\n";
+
+ TemporaryDir dir;
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_alias, std::string(dir.path) + "/modules.alias", 0600, getuid(), getgid()));
+
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_dep, std::string(dir.path) + "/modules.dep", 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_softdep, std::string(dir.path) + "/modules.softdep", 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_options, std::string(dir.path) + "/modules.options", 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_load, std::string(dir.path) + "/modules.load", 0600, getuid(), getgid()));
+
+ for (auto i = test_modules.begin(); i != test_modules.end(); ++i) {
+ *i = dir.path + *i;
+ }
+
+ Modprobe m({dir.path});
+ EXPECT_TRUE(m.LoadListedModules());
+
+ GTEST_LOG_(INFO) << "Expected modules loaded (in order):";
+ for (auto i = expected_modules_loaded.begin(); i != expected_modules_loaded.end(); ++i) {
+ *i = dir.path + *i;
+ GTEST_LOG_(INFO) << "\"" << *i << "\"";
+ }
+ GTEST_LOG_(INFO) << "Actual modules loaded (in order):";
+ for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
+ GTEST_LOG_(INFO) << "\"" << *i << "\"";
+ }
+
+ EXPECT_TRUE(modules_loaded == expected_modules_loaded);
+}
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/libmodprobe/libmodprobe_test.h
similarity index 70%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to libmodprobe/libmodprobe_test.h
index 410d379..a001b69 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/libmodprobe/libmodprobe_test.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#include <string>
+#include <vector>
+
+extern std::vector<std::string> test_modules;
+extern std::vector<std::string> modules_loaded;
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 66cb49f..debc43f 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -17,7 +17,9 @@
name: "libnativeloader",
defaults: ["libnativeloader-defaults"],
host_supported: true,
- srcs: ["native_loader.cpp"],
+ srcs: [
+ "native_loader.cpp",
+ ],
shared_libs: [
"libnativehelper",
"liblog",
@@ -26,6 +28,11 @@
],
target: {
android: {
+ srcs: [
+ "library_namespaces.cpp",
+ "native_loader_namespace.cpp",
+ "public_libraries.cpp",
+ ],
shared_libs: [
"libdl_android",
],
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
new file mode 100644
index 0000000..46f6fdd
--- /dev/null
+++ b/libnativeloader/README.md
@@ -0,0 +1,84 @@
+libnativeloader
+===============================================================================
+
+Overview
+-------------------------------------------------------------------------------
+libnativeloader is responsible for loading native shared libraries (`*.so`
+files) inside the Android Runtime (ART). The native shared libraries could be
+app-provided JNI libraries or public native libraries like `libc.so` provided
+by the platform.
+
+The most typical use case of this library is calling `System.loadLibrary(name)`.
+When the method is called, the ART runtime delegates the call to this library
+along with the reference to the classloader where the call was made. Then this
+library finds the linker namespace (named `classloader-namespace`) that is
+associated with the given classloader, and tries to load the requested library
+from the namespace. The actual searching, loading, and linking of the library
+is performed by the dynamic linker.
+
+The linker namespace is created when an APK is loaded into the process, and is
+associated with the classloader that loaded the APK. The linker namespace is
+configured so that only the JNI libraries embedded in the APK is accessible
+from the namespace, thus preventing an APK from loading JNI libraries of other
+APKs.
+
+The linker namespace is also configured differently depending on other
+characteristics of the APK such as whether or not the APK is bundled with the
+platform. In case of the unbundled, i.e., downloaded or updated APK, only the
+public native libraries that is listed in `/system/etc/public.libraries.txt`
+are available from the platform, whereas in case of the bundled, all libraries
+under `/system/lib` are available (i.e. shared). In case when the unbundled
+app is from `/vendor` or `/product` partition, the app is additionally provided
+with the [VNDK-SP](https://source.android.com/devices/architecture/vndk#sp-hal)
+libraries. As the platform is getting modularized with
+[APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md),
+some libraries are no longer provided from platform, but from the APEXes which
+have their own linker namespaces. For example, ICU libraries `libicuuc.so` and
+`libicui18n.so` are from the runtime APEX.
+
+The list of public native libraries is not static. The default set of libraries
+are defined in AOSP, but partners can extend it to include their own libraries.
+Currently, following extensions are available:
+
+- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are
+specific to the underlying SoC, e.g. GPU, DSP, etc.
+- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in
+`/{system|system}/lib` that a device manufacturer has newly added. The
+libraries should be named as `lib<name>.<companyname>.so` as in
+`libFoo.acme.so`.
+
+Note that, due to the naming constraint requiring `.<companyname>.so` suffix, it
+is prohibited for a device manufacturer to expose an AOSP-defined private
+library, e.g. libgui.so, libart.so, etc., to APKs.
+
+Lastly, libnativeloader is responsible for abstracting the two types of the
+dynamic linker interface: `libdl.so` and `libnativebridge.so`. The former is
+for non-translated, e.g. ARM-on-ARM, libraries, while the latter is for
+loading libraries in a translated environment such as ARM-on-x86.
+
+Implementation
+-------------------------------------------------------------------------------
+Implementation wise, libnativeloader consists of four parts:
+
+- `native_loader.cpp`
+- `library_namespaces.cpp`
+- `native_loader_namespace.cpp`
+- `public_libraries.cpp`
+
+`native_loader.cpp` implements the public interface of this library. It is just
+a thin wrapper around `library_namespaces.cpp` and `native_loader_namespace.cpp`.
+
+`library_namespaces.cpp` implements the singleton class `LibraryNamespaces` which
+is a manager-like entity that is responsible for creating and configuring
+linker namespaces and finding an already created linker namespace for a given
+classloader.
+
+`native_loader_namesapces.cpp` implements the class `NativeLoaderNamespace` that
+models a linker namespace. It's main job is to abstract the two types of the
+dynamic linker interface so that other parts of this library do not have to know
+the differences of the interfaces.
+
+`public_libraries.cpp` is responsible for reading `*.txt` files for the public
+native libraries from the various partitions. It can be considered as a part of
+`LibraryNamespaces` but is separated from it to hide the details of the parsing
+routines.
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
new file mode 100644
index 0000000..f7f972f
--- /dev/null
+++ b/libnativeloader/library_namespaces.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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 "library_namespaces.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "nativeloader/dlext_namespaces.h"
+#include "public_libraries.h"
+#include "utils.h"
+
+namespace android::nativeloader {
+
+namespace {
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+constexpr const char* kVendorNamespaceName = "sphal";
+constexpr const char* kVndkNamespaceName = "vndk";
+constexpr const char* kRuntimeNamespaceName = "runtime";
+
+// classloader-namespace is a linker namespace that is created for the loaded
+// app. To be specific, it is created for the app classloader. When
+// System.load() is called from a Java class that is loaded from the
+// classloader, the classloader-namespace namespace associated with that
+// classloader is selected for dlopen. The namespace is configured so that its
+// search path is set to the app-local JNI directory and it is linked to the
+// platform namespace with the names of libs listed in the public.libraries.txt.
+// This way an app can only load its own JNI libraries along with the public libs.
+constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+// Same thing for vendor APKs.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
+
+// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
+// System.load() with an absolute path which is outside of the classloader library search path.
+// This list includes all directories app is allowed to access this way.
+constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
+
+constexpr const char* kVendorLibPath = "/vendor/" LIB;
+constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
+
+const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+ APK_ORIGIN_DEFAULT = 0,
+ APK_ORIGIN_VENDOR = 1,
+ APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
+jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
+ jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+ jmethodID get_parent =
+ env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
+
+ return env->CallObjectMethod(class_loader, get_parent);
+}
+
+ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+ ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+ if (dex_path != nullptr) {
+ ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+ apk_origin = APK_ORIGIN_VENDOR;
+ }
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+ LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+ "Dex path contains both vendor and product partition : %s",
+ dex_path_utf_chars.c_str());
+
+ apk_origin = APK_ORIGIN_PRODUCT;
+ }
+ }
+ return apk_origin;
+}
+
+} // namespace
+
+void LibraryNamespaces::Initialize() {
+ // Once public namespace is initialized there is no
+ // point in running this code - it will have no effect
+ // on the current list of public libraries.
+ if (initialized_) {
+ return;
+ }
+
+ // android_init_namespaces() expects all the public libraries
+ // to be loaded so that they can be found by soname alone.
+ //
+ // TODO(dimitry): this is a bit misleading since we do not know
+ // if the vendor public library is going to be opened from /vendor/lib
+ // we might as well end up loading them from /system/lib or /product/lib
+ // For now we rely on CTS test to catch things like this but
+ // it should probably be addressed in the future.
+ for (const auto& soname : android::base::Split(default_public_libraries(), ":")) {
+ LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+ "Error preloading public library %s: %s", soname.c_str(), dlerror());
+ }
+}
+
+NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
+ jobject class_loader, bool is_shared,
+ jstring dex_path, jstring java_library_path,
+ jstring java_permitted_path,
+ std::string* error_msg) {
+ std::string library_path; // empty string by default.
+
+ if (java_library_path != nullptr) {
+ ScopedUtfChars library_path_utf_chars(env, java_library_path);
+ library_path = library_path_utf_chars.c_str();
+ }
+
+ ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
+ // (http://b/27588281) This is a workaround for apps using custom
+ // classloaders and calling System.load() with an absolute path which
+ // is outside of the classloader library search path.
+ //
+ // This part effectively allows such a classloader to access anything
+ // under /data and /mnt/expand
+ std::string permitted_path = kWhitelistedDirectories;
+
+ if (java_permitted_path != nullptr) {
+ ScopedUtfChars path(env, java_permitted_path);
+ if (path.c_str() != nullptr && path.size() > 0) {
+ permitted_path = permitted_path + ":" + path.c_str();
+ }
+ }
+
+ // Initialize the anonymous namespace with the first non-empty library path.
+ if (!library_path.empty() && !initialized_ &&
+ !InitPublicNamespace(library_path.c_str(), error_msg)) {
+ return nullptr;
+ }
+
+ bool found = FindNamespaceByClassLoader(env, class_loader);
+
+ LOG_ALWAYS_FATAL_IF(found, "There is already a namespace associated with this classloader");
+
+ std::string system_exposed_libraries = default_public_libraries();
+ const char* namespace_name = kClassloaderNamespaceName;
+ bool unbundled_vendor_or_product_app = false;
+ if ((apk_origin == APK_ORIGIN_VENDOR ||
+ (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+ !is_shared) {
+ unbundled_vendor_or_product_app = true;
+ // For vendor / product apks, give access to the vendor / product lib even though
+ // they are treated as unbundled; the libs and apks are still bundled
+ // together in the vendor / product partition.
+ const char* origin_partition;
+ const char* origin_lib_path;
+
+ switch (apk_origin) {
+ case APK_ORIGIN_VENDOR:
+ origin_partition = "vendor";
+ origin_lib_path = kVendorLibPath;
+ break;
+ case APK_ORIGIN_PRODUCT:
+ origin_partition = "product";
+ origin_lib_path = kProductLibPath;
+ break;
+ default:
+ origin_partition = "unknown";
+ origin_lib_path = "";
+ }
+ library_path = library_path + ":" + origin_lib_path;
+ permitted_path = permitted_path + ":" + origin_lib_path;
+
+ // Also give access to LLNDK libraries since they are available to vendors
+ system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str();
+
+ // Different name is useful for debugging
+ namespace_name = kVendorClassloaderNamespaceName;
+ ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+ origin_partition, library_path.c_str());
+ } else {
+ // extended public libraries are NOT available to vendor apks, otherwise it
+ // would be system->vendor violation.
+ if (!extended_public_libraries().empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries();
+ }
+ }
+
+ // Create the app namespace
+ NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
+ auto app_ns =
+ NativeLoaderNamespace::Create(namespace_name, library_path, permitted_path, parent_ns,
+ is_shared, target_sdk_version < 24 /* is_greylist_enabled */);
+ if (app_ns.IsNil()) {
+ *error_msg = app_ns.GetError();
+ return nullptr;
+ }
+
+ // ... and link to other namespaces to allow access to some public libraries
+ bool is_bridged = app_ns.IsBridged();
+
+ auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
+ if (!app_ns.Link(platform_ns, system_exposed_libraries)) {
+ *error_msg = app_ns.GetError();
+ return nullptr;
+ }
+
+ auto runtime_ns = NativeLoaderNamespace::GetExportedNamespace(kRuntimeNamespaceName, is_bridged);
+ // Runtime apex does not exist in host, and under certain build conditions.
+ if (!runtime_ns.IsNil()) {
+ if (!app_ns.Link(runtime_ns, runtime_public_libraries())) {
+ *error_msg = app_ns.GetError();
+ return nullptr;
+ }
+ }
+
+ // Give access to VNDK-SP libraries from the 'vndk' namespace.
+ if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) {
+ auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
+ if (!vndk_ns.IsNil() && !app_ns.Link(vndk_ns, vndksp_libraries())) {
+ *error_msg = app_ns.GetError();
+ return nullptr;
+ }
+ }
+
+ // Note that when vendor_ns is not configured, vendor_ns.IsNil() will be true
+ // and it will result in linking to the default namespace which is expected
+ // behavior in this case.
+ if (!vendor_public_libraries().empty()) {
+ auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
+ if (!app_ns.Link(vendor_ns, vendor_public_libraries())) {
+ *error_msg = dlerror();
+ return nullptr;
+ }
+ }
+
+ namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), app_ns));
+
+ return &(namespaces_.back().second);
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+ [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
+ return env->IsSameObject(value.first, class_loader);
+ });
+ if (it != namespaces_.end()) {
+ return &it->second;
+ }
+
+ return nullptr;
+}
+
+bool LibraryNamespaces::InitPublicNamespace(const char* library_path, std::string* error_msg) {
+ // Ask native bride if this apps library path should be handled by it
+ bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
+
+ // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+ // code is one example) unknown to linker in which case linker uses anonymous
+ // namespace. The second argument specifies the search path for the anonymous
+ // namespace which is the library_path of the classloader.
+ initialized_ = android_init_anonymous_namespace(default_public_libraries().c_str(),
+ is_native_bridge ? nullptr : library_path);
+ if (!initialized_) {
+ *error_msg = dlerror();
+ return false;
+ }
+
+ // and now initialize native bridge namespaces if necessary.
+ if (NativeBridgeInitialized()) {
+ initialized_ = NativeBridgeInitAnonymousNamespace(default_public_libraries().c_str(),
+ is_native_bridge ? library_path : nullptr);
+ if (!initialized_) {
+ *error_msg = NativeBridgeGetError();
+ }
+ }
+
+ return initialized_;
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ jobject parent_class_loader = GetParentClassLoader(env, class_loader);
+
+ while (parent_class_loader != nullptr) {
+ NativeLoaderNamespace* ns;
+ if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+ return ns;
+ }
+
+ parent_class_loader = GetParentClassLoader(env, parent_class_loader);
+ }
+
+ return nullptr;
+}
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
new file mode 100644
index 0000000..fd46cdc
--- /dev/null
+++ b/libnativeloader/library_namespaces.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#if !defined(__ANDROID__)
+#error "Not available for host"
+#endif
+
+#define LOG_TAG "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <list>
+#include <string>
+
+#include <jni.h>
+
+namespace android::nativeloader {
+
+// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
+// objects for an app process. Its main job is to create (and configure) a new
+// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
+// object for a given ClassLoader.
+class LibraryNamespaces {
+ public:
+ LibraryNamespaces() : initialized_(false) {}
+
+ LibraryNamespaces(LibraryNamespaces&&) = default;
+ LibraryNamespaces(const LibraryNamespaces&) = delete;
+ LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
+
+ void Initialize();
+ void Reset() { namespaces_.clear(); }
+ NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring java_library_path,
+ jstring java_permitted_path, std::string* error_msg);
+ NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ private:
+ bool InitPublicNamespace(const char* library_path, std::string* error_msg);
+ NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ bool initialized_;
+ std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+};
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index e460b1a..0c29324 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -14,728 +14,64 @@
* limitations under the License.
*/
+#define LOG_TAG "nativeloader"
+
#include "nativeloader/native_loader.h"
-#include <nativehelper/ScopedUtfChars.h>
#include <dlfcn.h>
-#ifdef __ANDROID__
-#define LOG_TAG "libnativeloader"
-#include "nativeloader/dlext_namespaces.h"
-#include "log/log.h"
-#endif
-#include <dirent.h>
#include <sys/types.h>
-#include "nativebridge/native_bridge.h"
-#include <algorithm>
-#include <list>
#include <memory>
#include <mutex>
-#include <regex>
#include <string>
#include <vector>
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/strings.h>
+#include <nativebridge/native_bridge.h>
+#include <nativehelper/ScopedUtfChars.h>
-#ifdef __BIONIC__
-#include <android-base/properties.h>
+#ifdef __ANDROID__
+#include <log/log.h>
+#include "library_namespaces.h"
+#include "nativeloader/dlext_namespaces.h"
#endif
-#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
- "%s:%d: %s CHECK '" #predicate "' failed.",\
- __FILE__, __LINE__, __FUNCTION__)
-
-using namespace std::string_literals;
-
namespace android {
+namespace {
#if defined(__ANDROID__)
-struct NativeLoaderNamespace {
- public:
- NativeLoaderNamespace()
- : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+using android::nativeloader::LibraryNamespaces;
- explicit NativeLoaderNamespace(android_namespace_t* ns)
- : android_ns_(ns), native_bridge_ns_(nullptr) { }
+constexpr const char* kApexPath = "/apex/";
- explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
- : android_ns_(nullptr), native_bridge_ns_(ns) { }
+std::mutex g_namespaces_mutex;
+LibraryNamespaces* g_namespaces = new LibraryNamespaces;
- NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
- NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
-
- NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
-
- android_namespace_t* get_android_ns() const {
- CHECK(native_bridge_ns_ == nullptr);
- return android_ns_;
+android_namespace_t* FindExportedNamespace(const char* caller_location) {
+ std::string location = caller_location;
+ // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+ // /apex/com.android...modulename/...
+ //
+ // And we extract from it 'modulename', which is the name of the linker namespace.
+ if (android::base::StartsWith(location, kApexPath)) {
+ size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+ LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+ "Error finding namespace of apex: no slash in path %s", caller_location);
+ size_t dot_index = location.find_last_of('.', slash_index);
+ LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+ "Error finding namespace of apex: no dot in apex name %s", caller_location);
+ std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+ android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+ LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+ "Error finding namespace of apex: no namespace called %s", name.c_str());
+ return boot_namespace;
}
-
- native_bridge_namespace_t* get_native_bridge_ns() const {
- CHECK(android_ns_ == nullptr);
- return native_bridge_ns_;
- }
-
- bool is_android_namespace() const {
- return native_bridge_ns_ == nullptr;
- }
-
- private:
- // Only one of them can be not null
- android_namespace_t* android_ns_;
- native_bridge_namespace_t* native_bridge_ns_;
-};
-
-static constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/public.libraries.txt";
-static constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
- sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
-static constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
- sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
-static constexpr const char kPublicNativeLibrariesVendorConfig[] =
- "/vendor/etc/public.libraries.txt";
-static constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/llndk.libraries.txt";
-static constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/vndksp.libraries.txt";
-
-static const std::vector<const std::string> kRuntimePublicLibraries = {
- "libicuuc.so",
- "libicui18n.so",
-};
-
-// The device may be configured to have the vendor libraries loaded to a separate namespace.
-// For historical reasons this namespace was named sphal but effectively it is intended
-// to use to load vendor libraries to separate namespace with controlled interface between
-// vendor and system namespaces.
-static constexpr const char* kVendorNamespaceName = "sphal";
-
-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
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// default namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-static constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for vendor APKs.
-static constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-
-// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
-// System.load() with an absolute path which is outside of the classloader library search path.
-// This list includes all directories app is allowed to access this way.
-static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
-
-static constexpr const char* kApexPath = "/apex/";
-
-#if defined(__LP64__)
-static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
-static constexpr const char* kVendorLibPath = "/vendor/lib64";
-static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
-#else
-static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
-static constexpr const char* kVendorLibPath = "/vendor/lib";
-static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
-#endif
-
-static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
-static const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
-
-// Define origin of APK if it is from vendor partition or product partition
-typedef enum {
- APK_ORIGIN_DEFAULT = 0,
- APK_ORIGIN_VENDOR = 1,
- APK_ORIGIN_PRODUCT = 2,
-} ApkOrigin;
-
-static bool is_debuggable() {
- bool debuggable = false;
-#ifdef __BIONIC__
- debuggable = android::base::GetBoolProperty("ro.debuggable", false);
-#endif
- return debuggable;
+ return nullptr;
}
-
-static std::string vndk_version_str() {
-#ifdef __BIONIC__
- std::string version = android::base::GetProperty("ro.vndk.version", "");
- if (version != "" && version != "current") {
- return "." + version;
- }
-#endif
- return "";
-}
-
-static void insert_vndk_version_str(std::string* file_name) {
- CHECK(file_name != nullptr);
- size_t insert_pos = file_name->find_last_of(".");
- if (insert_pos == std::string::npos) {
- insert_pos = file_name->length();
- }
- file_name->insert(insert_pos, vndk_version_str());
-}
-
-static const std::function<bool(const std::string&, std::string*)> always_true =
- [](const std::string&, std::string*) { return true; };
-
-class LibraryNamespaces {
- public:
- LibraryNamespaces() : initialized_(false) { }
-
- NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
- bool is_shared, jstring dex_path, jstring java_library_path,
- jstring java_permitted_path, std::string* error_msg) {
- std::string library_path; // empty string by default.
-
- if (java_library_path != nullptr) {
- ScopedUtfChars library_path_utf_chars(env, java_library_path);
- library_path = library_path_utf_chars.c_str();
- }
-
- ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
-
- // (http://b/27588281) This is a workaround for apps using custom
- // classloaders and calling System.load() with an absolute path which
- // is outside of the classloader library search path.
- //
- // This part effectively allows such a classloader to access anything
- // under /data and /mnt/expand
- std::string permitted_path = kWhitelistedDirectories;
-
- if (java_permitted_path != nullptr) {
- ScopedUtfChars path(env, java_permitted_path);
- if (path.c_str() != nullptr && path.size() > 0) {
- permitted_path = permitted_path + ":" + path.c_str();
- }
- }
-
- // Initialize the anonymous namespace with the first non-empty library path.
- if (!library_path.empty() && !initialized_ &&
- !InitPublicNamespace(library_path.c_str(), error_msg)) {
- return nullptr;
- }
-
- bool found = FindNamespaceByClassLoader(env, class_loader);
-
- LOG_ALWAYS_FATAL_IF(found,
- "There is already a namespace associated with this classloader");
-
- uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
- if (is_shared) {
- namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
- }
-
- if (target_sdk_version < 24) {
- namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
- }
-
- NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
-
- bool is_native_bridge = false;
-
- if (parent_ns != nullptr) {
- is_native_bridge = !parent_ns->is_android_namespace();
- } else if (!library_path.empty()) {
- is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
- }
-
- std::string system_exposed_libraries = system_public_libraries_;
- const char* namespace_name = kClassloaderNamespaceName;
- android_namespace_t* vndk_ns = nullptr;
- if ((apk_origin == APK_ORIGIN_VENDOR ||
- (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
- !is_shared) {
- LOG_FATAL_IF(is_native_bridge,
- "Unbundled vendor / product apk must not use translated architecture");
-
- // For vendor / product apks, give access to the vendor / product lib even though
- // they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor / product partition.
- const char* origin_partition;
- const char* origin_lib_path;
-
- switch (apk_origin) {
- case APK_ORIGIN_VENDOR:
- origin_partition = "vendor";
- origin_lib_path = kVendorLibPath;
- break;
- case APK_ORIGIN_PRODUCT:
- origin_partition = "product";
- origin_lib_path = kProductLibPath;
- break;
- default:
- origin_partition = "unknown";
- origin_lib_path = "";
- }
-
- LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
- origin_partition);
-
- library_path = library_path + ":" + origin_lib_path;
- permitted_path = permitted_path + ":" + origin_lib_path;
-
- // Also give access to LLNDK libraries since they are available to vendors
- system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
-
- // Give access to VNDK-SP libraries from the 'vndk' namespace.
- vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
- if (vndk_ns == nullptr) {
- ALOGW("Cannot find \"%s\" namespace for %s apks", kVndkNamespaceName, origin_partition);
- }
-
- // Different name is useful for debugging
- namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
- origin_partition, library_path.c_str());
- } else {
- // oem and product public libraries are NOT available to vendor apks, otherwise it
- // would be system->vendor violation.
- if (!oem_public_libraries_.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
- }
- if (!product_public_libraries_.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
- }
- }
-
- std::string runtime_exposed_libraries = base::Join(kRuntimePublicLibraries, ":");
-
- NativeLoaderNamespace native_loader_ns;
- if (!is_native_bridge) {
- 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(),
- namespace_type,
- permitted_path.c_str(),
- android_parent_ns);
- if (ns == nullptr) {
- *error_msg = dlerror();
- return nullptr;
- }
-
- // Note that when vendor_ns is not configured this function will return nullptr
- // and it will result in linking vendor_public_libraries_ to the default namespace
- // which is expected behavior in this case.
- android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
-
- android_namespace_t* runtime_ns = android_get_exported_namespace(kRuntimeNamespaceName);
-
- if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
-
- // Runtime apex does not exist in host, and under certain build conditions.
- if (runtime_ns != nullptr) {
- if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
- // vendor apks are allowed to use VNDK-SP libraries.
- if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- if (!vendor_public_libraries_.empty()) {
- if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- native_loader_ns = NativeLoaderNamespace(ns);
- } else {
- 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(),
- namespace_type,
- permitted_path.c_str(),
- native_bridge_parent_namespace);
- if (ns == nullptr) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
-
- native_bridge_namespace_t* vendor_ns = NativeBridgeGetExportedNamespace(kVendorNamespaceName);
- native_bridge_namespace_t* runtime_ns =
- NativeBridgeGetExportedNamespace(kRuntimeNamespaceName);
-
- if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
-
- // Runtime apex does not exist in host, and under certain build conditions.
- if (runtime_ns != nullptr) {
- if (!NativeBridgeLinkNamespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
- }
- if (!vendor_public_libraries_.empty()) {
- if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
- }
-
- native_loader_ns = NativeLoaderNamespace(ns);
- }
-
- namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
-
- return &(namespaces_.back().second);
- }
-
- NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
- [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
- return env->IsSameObject(value.first, class_loader);
- });
- if (it != namespaces_.end()) {
- return &it->second;
- }
-
- return nullptr;
- }
-
- void Initialize() {
- // Once public namespace is initialized there is no
- // point in running this code - it will have no effect
- // on the current list of public libraries.
- if (initialized_) {
- return;
- }
-
- std::vector<std::string> sonames;
- const char* android_root_env = getenv("ANDROID_ROOT");
- std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
- std::string public_native_libraries_system_config =
- root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
- std::string llndk_native_libraries_system_config =
- root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
- std::string vndksp_native_libraries_system_config =
- root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
-
- std::string product_public_native_libraries_dir = "/product/etc";
-
- std::string error_msg;
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
- "Error reading public native library list from \"%s\": %s",
- public_native_libraries_system_config.c_str(), error_msg.c_str());
-
- // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
- // variable to add libraries to the list. This is intended for platform tests only.
- if (is_debuggable()) {
- const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
- if (additional_libs != nullptr && additional_libs[0] != '\0') {
- std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
- std::copy(additional_libs_vector.begin(), additional_libs_vector.end(),
- std::back_inserter(sonames));
- }
- }
-
- // 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.
- //
- // TODO(dimitry): this is a bit misleading since we do not know
- // if the vendor public library is going to be opened from /vendor/lib
- // we might as well end up loading them from /system/lib or /product/lib
- // For now we rely on CTS test to catch things like this but
- // it should probably be addressed in the future.
- for (const auto& soname : sonames) {
- LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
- "Error preloading public library %s: %s", soname.c_str(), dlerror());
- }
-
- system_public_libraries_ = base::Join(sonames, ':');
-
- // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
- // system libs that are exposed to apps. The libs in the txt files must be
- // named as lib<name>.<companyname>.so.
- sonames.clear();
- ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
- oem_public_libraries_ = base::Join(sonames, ':');
-
- // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
- // product libs that are exposed to apps.
- sonames.clear();
- ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
- product_public_libraries_ = base::Join(sonames, ':');
-
- // Insert VNDK version to llndk and vndksp config file names.
- insert_vndk_version_str(&llndk_native_libraries_system_config);
- insert_vndk_version_str(&vndksp_native_libraries_system_config);
-
- sonames.clear();
- ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
- system_llndk_libraries_ = base::Join(sonames, ':');
-
- sonames.clear();
- ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
- system_vndksp_libraries_ = base::Join(sonames, ':');
-
- sonames.clear();
- // This file is optional, quietly ignore if the file does not exist.
- ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
-
- vendor_public_libraries_ = base::Join(sonames, ':');
- }
-
- void Reset() { namespaces_.clear(); }
-
- private:
- void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
- if (dir != nullptr) {
- // Failing to opening the dir is not an error, which can happen in
- // webview_zygote.
- while (struct dirent* ent = readdir(dir.get())) {
- if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
- continue;
- }
- const std::string filename(ent->d_name);
- if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
- android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
- const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
- const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
- const std::string company_name = filename.substr(start, end - start);
- const std::string config_file_path = dirname + "/"s + filename;
- LOG_ALWAYS_FATAL_IF(
- company_name.empty(),
- "Error extracting company name from public native library list file path \"%s\"",
- config_file_path.c_str());
-
- std::string error_msg;
-
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(
- config_file_path, sonames,
- [&company_name](const std::string& soname, std::string* error_msg) {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return true;
- } else {
- *error_msg = "Library name \"" + soname +
- "\" does not end with the company name: " + company_name + ".";
- return false;
- }
- },
- &error_msg),
- "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
- error_msg.c_str());
- }
- }
- }
- }
-
- /**
- * 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 */,
- std::string* /* error_msg */)>& check_soname,
- std::string* error_msg = nullptr) {
- // Read list of public native libraries from the config file.
- std::string file_content;
- if(!base::ReadFileToString(configFile, &file_content)) {
- if (error_msg) *error_msg = strerror(errno);
- return false;
- }
-
- std::vector<std::string> lines = base::Split(file_content, "\n");
-
- for (auto& line : lines) {
- auto trimmed_line = base::Trim(line);
- if (trimmed_line[0] == '#' || trimmed_line.empty()) {
- continue;
- }
- size_t space_pos = trimmed_line.rfind(' ');
- if (space_pos != std::string::npos) {
- std::string type = trimmed_line.substr(space_pos + 1);
- if (type != "32" && type != "64") {
- if (error_msg) *error_msg = "Malformed line: " + line;
- return false;
- }
-#if defined(__LP64__)
- // Skip 32 bit public library.
- if (type == "32") {
- continue;
- }
-#else
- // Skip 64 bit public library.
- if (type == "64") {
- continue;
- }
-#endif
- trimmed_line.resize(space_pos);
- }
-
- if (check_soname(trimmed_line, error_msg)) {
- sonames->push_back(trimmed_line);
- } else {
- return false;
- }
- }
-
- return true;
- }
-
- bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
- // Ask native bride if this apps library path should be handled by it
- bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
-
- // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
- // code is one example) unknown to linker in which case linker uses anonymous
- // namespace. The second argument specifies the search path for the anonymous
- // namespace which is the library_path of the classloader.
- initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
- is_native_bridge ? nullptr : library_path);
- if (!initialized_) {
- *error_msg = dlerror();
- return false;
- }
-
- // and now initialize native bridge namespaces if necessary.
- if (NativeBridgeInitialized()) {
- initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
- is_native_bridge ? library_path : nullptr);
- if (!initialized_) {
- *error_msg = NativeBridgeGetError();
- }
- }
-
- return initialized_;
- }
-
- jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
- jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
- jmethodID get_parent = env->GetMethodID(class_loader_class,
- "getParent",
- "()Ljava/lang/ClassLoader;");
-
- return env->CallObjectMethod(class_loader, get_parent);
- }
-
- NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- jobject parent_class_loader = GetParentClassLoader(env, class_loader);
-
- while (parent_class_loader != nullptr) {
- NativeLoaderNamespace* ns;
- if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
- return ns;
- }
-
- parent_class_loader = GetParentClassLoader(env, parent_class_loader);
- }
-
- return nullptr;
- }
-
- ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
- ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
-
- if (dex_path != nullptr) {
- ScopedUtfChars dex_path_utf_chars(env, dex_path);
-
- if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
- apk_origin = APK_ORIGIN_VENDOR;
- }
-
- if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
- LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
- "Dex path contains both vendor and product partition : %s",
- dex_path_utf_chars.c_str());
-
- apk_origin = APK_ORIGIN_PRODUCT;
- }
- }
-
- return apk_origin;
- }
-
- bool initialized_;
- std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
- std::string system_public_libraries_;
- std::string vendor_public_libraries_;
- std::string oem_public_libraries_;
- std::string product_public_libraries_;
- std::string system_llndk_libraries_;
- std::string system_vndksp_libraries_;
-
- DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
-};
-
-static std::mutex g_namespaces_mutex;
-static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
-#endif
+#endif // #if defined(__ANDROID__)
+} // namespace
void InitializeNativeLoader() {
#if defined(__ANDROID__)
@@ -769,30 +105,6 @@
return nullptr;
}
-#if defined(__ANDROID__)
-static android_namespace_t* FindExportedNamespace(const char* caller_location) {
- std::string location = caller_location;
- // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
- // /apex/com.android...modulename/...
- //
- // And we extract from it 'modulename', which is the name of the linker namespace.
- if (android::base::StartsWith(location, kApexPath)) {
- size_t slash_index = location.find_first_of('/', strlen(kApexPath));
- LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
- "Error finding namespace of apex: no slash in path %s", caller_location);
- size_t dot_index = location.find_last_of('.', slash_index);
- LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
- "Error finding namespace of apex: no dot in apex name %s", caller_location);
- std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
- android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
- LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
- "Error finding namespace of apex: no namespace called %s", name.c_str());
- return boot_namespace;
- }
- return nullptr;
-}
-#endif
-
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) {
@@ -910,25 +222,12 @@
#if defined(__ANDROID__)
void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
bool* needs_native_bridge, char** error_msg) {
- if (ns->is_android_namespace()) {
- android_dlextinfo extinfo;
- extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
- extinfo.library_namespace = ns->get_android_ns();
-
- void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
- if (handle == nullptr) {
- *error_msg = strdup(dlerror());
- }
- *needs_native_bridge = false;
- return handle;
- } else {
- void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
- if (handle == nullptr) {
- *error_msg = strdup(NativeBridgeGetError());
- }
- *needs_native_bridge = true;
- return handle;
+ void* handle = ns->Load(path);
+ if (handle == nullptr) {
+ *error_msg = ns->GetError();
}
+ *needs_native_bridge = ns->IsBridged();
+ return handle;
}
// native_bridge_namespaces are not supported for callers of this function.
@@ -937,16 +236,16 @@
android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
- if (ns != nullptr) {
- return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
+ if (ns != nullptr && !ns->IsBridged()) {
+ return ns->ToRawAndroidNamespace();
}
-
return nullptr;
}
+
NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
}
#endif
-}; // android namespace
+}; // namespace android
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
new file mode 100644
index 0000000..58ac686
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <dlfcn.h>
+
+#include <functional>
+
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+#include "nativeloader/dlext_namespaces.h"
+
+namespace android {
+
+namespace {
+
+constexpr const char* kDefaultNamespaceName = "default";
+constexpr const char* kPlatformNamespaceName = "platform";
+
+} // namespace
+
+NativeLoaderNamespace NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
+ bool is_bridged) {
+ if (!is_bridged) {
+ return NativeLoaderNamespace(name, android_get_exported_namespace(name.c_str()));
+ } else {
+ return NativeLoaderNamespace(name, NativeBridgeGetExportedNamespace(name.c_str()));
+ }
+}
+
+char* NativeLoaderNamespace::GetError() const {
+ if (!IsBridged()) {
+ return strdup(dlerror());
+ } else {
+ return strdup(NativeBridgeGetError());
+ }
+}
+
+// The platform namespace is called "default" for binaries in /system and
+// "platform" for those in the Runtime APEX. Try "platform" first since
+// "default" always exists.
+NativeLoaderNamespace NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
+ NativeLoaderNamespace ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
+ if (ns.IsNil()) {
+ ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
+ }
+ return ns;
+}
+
+NativeLoaderNamespace NativeLoaderNamespace::Create(const std::string& name,
+ const std::string& search_paths,
+ const std::string& permitted_paths,
+ const NativeLoaderNamespace* parent,
+ bool is_shared, bool is_greylist_enabled) {
+ bool is_bridged = false;
+ if (parent != nullptr) {
+ is_bridged = parent->IsBridged();
+ } else if (!search_paths.empty()) {
+ is_bridged = NativeBridgeIsPathSupported(search_paths.c_str());
+ }
+
+ // Fall back to the platform namespace if no parent is set.
+ const NativeLoaderNamespace& effective_parent =
+ parent != nullptr ? *parent : GetPlatformNamespace(is_bridged);
+
+ uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ if (is_shared) {
+ type |= ANDROID_NAMESPACE_TYPE_SHARED;
+ }
+ if (is_greylist_enabled) {
+ type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+ }
+
+ if (!is_bridged) {
+ android_namespace_t* raw =
+ android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
+ permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
+ return NativeLoaderNamespace(name, raw);
+ } else {
+ native_bridge_namespace_t* raw = NativeBridgeCreateNamespace(
+ name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(),
+ effective_parent.ToRawNativeBridgeNamespace());
+ return NativeLoaderNamespace(name, raw);
+ }
+}
+
+bool NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
+ const std::string& shared_libs) const {
+ LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s",
+ this->name().c_str(), target.name().c_str());
+ if (!IsBridged()) {
+ return android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
+ shared_libs.c_str());
+ } else {
+ return NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
+ target.ToRawNativeBridgeNamespace(), shared_libs.c_str());
+ }
+}
+
+void* NativeLoaderNamespace::Load(const char* lib_name) const {
+ if (!IsBridged()) {
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = this->ToRawAndroidNamespace();
+ return android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
+ } else {
+ return NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace());
+ }
+}
+
+} // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
new file mode 100644
index 0000000..71e4247
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.h
@@ -0,0 +1,72 @@
+/*
+ * 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
+#if defined(__ANDROID__)
+
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android/dlext.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+namespace android {
+
+// NativeLoaderNamespace abstracts a linker namespace for the native
+// architecture (ex: arm on arm) or the translated architecture (ex: arm on
+// x86). Instances of this class are managed by LibraryNamespaces object.
+struct NativeLoaderNamespace {
+ public:
+ // TODO(return with errors)
+ static NativeLoaderNamespace Create(const std::string& name, const std::string& search_paths,
+ const std::string& permitted_paths,
+ const NativeLoaderNamespace* parent, bool is_shared,
+ bool is_greylist_enabled);
+
+ NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
+ NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
+ NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
+
+ android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); }
+ native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); }
+
+ std::string name() const { return name_; }
+ bool IsBridged() const { return raw_.index() == 1; }
+ bool IsNil() const {
+ return IsBridged() ? std::get<1>(raw_) == nullptr : std::get<0>(raw_) == nullptr;
+ }
+
+ bool Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
+ void* Load(const char* lib_name) const;
+ char* GetError() const;
+
+ static NativeLoaderNamespace GetExportedNamespace(const std::string& name, bool is_bridged);
+ static NativeLoaderNamespace GetPlatformNamespace(bool is_bridged);
+
+ private:
+ explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns)
+ : name_(name), raw_(ns) {}
+ explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns)
+ : name_(name), raw_(ns) {}
+
+ std::string name_;
+ std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_;
+};
+
+} // namespace android
+#endif // #if defined(__ANDROID__)
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
new file mode 100644
index 0000000..c205eb1
--- /dev/null
+++ b/libnativeloader/public_libraries.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "nativeloader"
+
+#include "public_libraries.h"
+
+#include <dirent.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+
+#include "utils.h"
+
+namespace android::nativeloader {
+
+using namespace std::string_literals;
+
+namespace {
+
+constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
+constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
+constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
+constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
+constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
+constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
+
+const std::vector<const std::string> kRuntimePublicLibraries = {
+ "libicuuc.so",
+ "libicui18n.so",
+};
+
+constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/" LIB;
+
+// TODO(b/130388701): do we need this?
+std::string root_dir() {
+ static const char* android_root_env = getenv("ANDROID_ROOT");
+ return android_root_env != nullptr ? android_root_env : "/system";
+}
+
+bool debuggable() {
+ static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ return debuggable;
+}
+
+std::string vndk_version_str() {
+ static std::string version = android::base::GetProperty("ro.vndk.version", "");
+ if (version != "" && version != "current") {
+ return "." + version;
+ }
+ return "";
+}
+
+// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
+// variable to add libraries to the list. This is intended for platform tests only.
+std::string additional_public_libraries() {
+ if (debuggable()) {
+ const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
+ return val ? val : "";
+ }
+ return "";
+}
+
+void InsertVndkVersionStr(std::string* file_name) {
+ CHECK(file_name != nullptr);
+ size_t insert_pos = file_name->find_last_of(".");
+ if (insert_pos == std::string::npos) {
+ insert_pos = file_name->length();
+ }
+ file_name->insert(insert_pos, vndk_version_str());
+}
+
+const std::function<bool(const std::string&, std::string*)> always_true =
+ [](const std::string&, std::string*) { return true; };
+
+bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
+ const std::function<bool(const std::string& /* soname */,
+ std::string* /* error_msg */)>& check_soname,
+ std::string* error_msg = nullptr) {
+ // Read list of public native libraries from the config file.
+ std::string file_content;
+ if (!base::ReadFileToString(configFile, &file_content)) {
+ if (error_msg) *error_msg = strerror(errno);
+ return false;
+ }
+
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+ size_t space_pos = trimmed_line.rfind(' ');
+ if (space_pos != std::string::npos) {
+ std::string type = trimmed_line.substr(space_pos + 1);
+ if (type != "32" && type != "64") {
+ if (error_msg) *error_msg = "Malformed line: " + line;
+ return false;
+ }
+#if defined(__LP64__)
+ // Skip 32 bit public library.
+ if (type == "32") {
+ continue;
+ }
+#else
+ // Skip 64 bit public library.
+ if (type == "64") {
+ continue;
+ }
+#endif
+ trimmed_line.resize(space_pos);
+ }
+
+ if (check_soname(trimmed_line, error_msg)) {
+ sonames->push_back(trimmed_line);
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
+ if (dir != nullptr) {
+ // Failing to opening the dir is not an error, which can happen in
+ // webview_zygote.
+ while (struct dirent* ent = readdir(dir.get())) {
+ if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+ continue;
+ }
+ const std::string filename(ent->d_name);
+ std::string_view fn = filename;
+ if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
+ android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
+ const std::string company_name(fn);
+ const std::string config_file_path = dirname + "/"s + filename;
+ LOG_ALWAYS_FATAL_IF(
+ company_name.empty(),
+ "Error extracting company name from public native library list file path \"%s\"",
+ config_file_path.c_str());
+
+ std::string error_msg;
+
+ LOG_ALWAYS_FATAL_IF(
+ !ReadConfig(config_file_path, sonames,
+ [&company_name](const std::string& soname, std::string* error_msg) {
+ if (android::base::StartsWith(soname, "lib") &&
+ android::base::EndsWith(soname, "." + company_name + ".so")) {
+ return true;
+ } else {
+ *error_msg = "Library name \"" + soname +
+ "\" does not end with the company name: " + company_name +
+ ".";
+ return false;
+ }
+ },
+ &error_msg),
+ "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
+ error_msg.c_str());
+ }
+ }
+ }
+}
+
+static std::string InitDefaultPublicLibraries() {
+ std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
+ std::vector<std::string> sonames;
+ std::string error_msg;
+ LOG_ALWAYS_FATAL_IF(!ReadConfig(config_file, &sonames, always_true, &error_msg),
+ "Error reading public native library list from \"%s\": %s",
+ config_file.c_str(), error_msg.c_str());
+
+ std::string additional_libs = additional_public_libraries();
+ if (!additional_libs.empty()) {
+ auto vec = base::Split(additional_libs, ":");
+ std::copy(vec.begin(), vec.end(), std::back_inserter(sonames));
+ }
+
+ // 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
+ 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);
+ }
+ }
+ return android::base::Join(sonames, ':');
+}
+
+static std::string InitRuntimePublicLibraries() {
+ CHECK(sizeof(kRuntimePublicLibraries) > 0);
+ std::string list = android::base::Join(kRuntimePublicLibraries, ":");
+
+ std::string additional_libs = additional_public_libraries();
+ if (!additional_libs.empty()) {
+ list = list + ':' + additional_libs;
+ }
+ return list;
+}
+
+static std::string InitVendorPublicLibraries() {
+ // This file is optional, quietly ignore if the file does not exist.
+ std::vector<std::string> sonames;
+ ReadConfig(kVendorPublicLibrariesFile, &sonames, always_true, nullptr);
+ return android::base::Join(sonames, ':');
+}
+
+// read /system/etc/public.libraries-<companyname>.txt and
+// /product/etc/public.libraries-<companyname>.txt which contain partner defined
+// system libs that are exposed to apps. The libs in the txt files must be
+// named as lib<name>.<companyname>.so.
+static std::string InitExtendedPublicLibraries() {
+ std::vector<std::string> sonames;
+ ReadExtensionLibraries("/system/etc", &sonames);
+ ReadExtensionLibraries("/product/etc", &sonames);
+ return android::base::Join(sonames, ':');
+}
+
+static std::string InitLlndkLibraries() {
+ std::string config_file = kLlndkLibrariesFile;
+ InsertVndkVersionStr(&config_file);
+ std::vector<std::string> sonames;
+ ReadConfig(config_file, &sonames, always_true, nullptr);
+ return android::base::Join(sonames, ':');
+}
+
+static std::string InitVndkspLibraries() {
+ std::string config_file = kVndkLibrariesFile;
+ InsertVndkVersionStr(&config_file);
+ std::vector<std::string> sonames;
+ ReadConfig(config_file, &sonames, always_true, nullptr);
+ return android::base::Join(sonames, ':');
+}
+
+} // namespace
+
+const std::string& default_public_libraries() {
+ static std::string list = InitDefaultPublicLibraries();
+ return list;
+}
+
+const std::string& runtime_public_libraries() {
+ static std::string list = InitRuntimePublicLibraries();
+ return list;
+}
+
+const std::string& vendor_public_libraries() {
+ static std::string list = InitVendorPublicLibraries();
+ return list;
+}
+
+const std::string& extended_public_libraries() {
+ static std::string list = InitExtendedPublicLibraries();
+ return list;
+}
+
+const std::string& llndk_libraries() {
+ static std::string list = InitLlndkLibraries();
+ return list;
+}
+
+const std::string& vndksp_libraries() {
+ static std::string list = InitVndkspLibraries();
+ return list;
+}
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
new file mode 100644
index 0000000..9b6dea8
--- /dev/null
+++ b/libnativeloader/public_libraries.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+
+namespace android::nativeloader {
+
+// These provide the list of libraries that are available to the namespace for apps.
+// Not all of the libraries are available to apps. Depending on the context,
+// e.g., if it is a vendor app or not, different set of libraries are made available.
+const std::string& default_public_libraries();
+const std::string& runtime_public_libraries();
+const std::string& vendor_public_libraries();
+const std::string& extended_public_libraries();
+const std::string& llndk_libraries();
+const std::string& vndksp_libraries();
+
+}; // namespace android::nativeloader
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/libnativeloader/utils.h
similarity index 70%
rename from mkbootimg/mkbootimg_dummy.cpp
rename to libnativeloader/utils.h
index 410d379..a1c2be5 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/libnativeloader/utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include <abi_check/mkbootimg_abi_check.h>
+namespace android::nativeloader {
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#if defined(__LP64__)
+#define LIB "lib64"
+#else
+#define LIB "lib"
+#endif
+
+} // namespace android::nativeloader
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 0207a75..618a5c5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -3,6 +3,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
linux_bionic: {
@@ -23,6 +24,7 @@
],
name: "libprocessgroup",
host_supported: true,
+ native_bridge_supported: true,
recovery_available: true,
vendor_available: true,
vndk: {
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
index 9d5afeb..0af75bb 100644
--- a/libprocessgroup/cgrouprc/Android.bp
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -21,6 +21,7 @@
// modules should use libprocessgroup which links to the LL-NDK library
// defined below. The static library is built for tests.
vendor_available: false,
+ native_bridge_supported: true,
srcs: [
"cgroup_controller.cpp",
"cgroup_file.cpp",
@@ -55,6 +56,7 @@
llndk_library {
name: "libcgrouprc",
symbol_file: "libcgrouprc.llndk.txt",
+ native_bridge_supported: true,
export_include_dirs: [
"include",
],
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
index dfbeed7..559a869 100644
--- a/libprocessgroup/cgrouprc_format/Android.bp
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -16,6 +16,7 @@
name: "libcgrouprc_format",
host_supported: true,
recovery_available: true,
+ native_bridge_supported: true,
srcs: [
"cgroup_controller.cpp",
],
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 7e6bf45..f73ec2d 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -39,6 +39,11 @@
bool UsePerAppMemcg();
+// Drop the fd cache of cgroup path. It is used for when resource caching is enabled and a process
+// loses the access to the path, the access checking (See SetCgroupAction::EnableResourceCaching)
+// should be active again. E.g. Zygote specialization for child process.
+void DropTaskProfilesResourceCaching();
+
// Return 0 and removes the cgroup if there are no longer any processes in it.
// Returns -1 in the case of an error occurring or if there are processes still running
// even after retrying for up to 200ms.
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index d3ac26b..7c191be 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -111,6 +111,10 @@
return memcg_supported;
}
+void DropTaskProfilesResourceCaching() {
+ TaskProfiles::GetInstance().DropResourceCaching();
+}
+
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
bool use_fd_cache) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index edc316a..aee5f0c 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -173,6 +173,15 @@
fd_ = std::move(fd);
}
+void SetCgroupAction::DropResourceCaching() {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (fd_ == FDS_NOT_CACHED) {
+ return;
+ }
+
+ fd_.reset(FDS_NOT_CACHED);
+}
+
bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
if (tid <= 0) {
return true;
@@ -292,6 +301,24 @@
res_cached_ = true;
}
+void TaskProfile::DropResourceCaching() {
+ if (!res_cached_) {
+ return;
+ }
+
+ for (auto& element : elements_) {
+ element->DropResourceCaching();
+ }
+
+ res_cached_ = false;
+}
+
+void TaskProfiles::DropResourceCaching() const {
+ for (auto& iter : profiles_) {
+ iter.second->DropResourceCaching();
+ }
+}
+
TaskProfiles& TaskProfiles::GetInstance() {
// Deliberately leak this object to avoid a race between destruction on
// process exit and concurrent access from another thread.
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 77bac2d..891d5b5 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -51,6 +51,7 @@
virtual bool ExecuteForTask(int) const { return false; };
virtual void EnableResourceCaching() {}
+ virtual void DropResourceCaching() {}
};
// Profile actions
@@ -114,6 +115,7 @@
virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
virtual bool ExecuteForTask(int tid) const;
virtual void EnableResourceCaching();
+ virtual void DropResourceCaching();
const CgroupController* controller() const { return &controller_; }
std::string path() const { return path_; }
@@ -145,6 +147,7 @@
bool ExecuteForProcess(uid_t uid, pid_t pid) const;
bool ExecuteForTask(int tid) const;
void EnableResourceCaching();
+ void DropResourceCaching();
private:
bool res_cached_;
@@ -158,6 +161,7 @@
TaskProfile* GetProfile(const std::string& name) const;
const ProfileAttribute* GetAttribute(const std::string& name) const;
+ void DropResourceCaching() const;
private:
std::map<std::string, std::unique_ptr<TaskProfile>> profiles_;
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index cb288c5..24c6379 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -188,7 +188,7 @@
int (*write)(void* priv, const void* data, size_t len,
unsigned int block, unsigned int nr_blocks),
void* priv) {
- int ret;
+ int ret = 0;
int chunks;
struct chunk_data chk;
struct output_file* out;
diff --git a/libsync/Android.bp b/libsync/Android.bp
index e56f8ba..c996e1b 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -23,6 +23,7 @@
cc_library {
name: "libsync",
recovery_available: true,
+ native_bridge_supported: true,
defaults: ["libsync_defaults"],
}
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 2e22b43..b265b61 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -3,6 +3,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index da5d86c..ccda5d1 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -26,6 +26,19 @@
],
export_include_dirs: ["include"],
+
+ tidy: true,
+ tidy_checks: [
+ "-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
+ tidy_checks_as_errors: [
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
}
cc_test {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 9dc2699..8fe7854 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -39,9 +39,12 @@
const int LOCAL_QLOG_NL_EVENT = 112;
const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
+#include <android-base/parseint.h>
#include <log/log.h>
#include <sysutils/NetlinkEvent.h>
+using android::base::ParseInt;
+
NetlinkEvent::NetlinkEvent() {
mAction = Action::kUnknown;
memset(mParams, 0, sizeof(mParams));
@@ -301,8 +304,9 @@
raw = (char*)nlAttrData(payload);
}
- char* hex = (char*) calloc(1, 5 + (len * 2));
- strcpy(hex, "HEX=");
+ size_t hexSize = 5 + (len * 2);
+ char* hex = (char*)calloc(1, hexSize);
+ strlcpy(hex, "HEX=", hexSize);
for (int i = 0; i < len; i++) {
hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
@@ -474,23 +478,20 @@
struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
- // Construct "SERVERS=<comma-separated string of DNS addresses>".
- static const char kServerTag[] = "SERVERS=";
- static const size_t kTagLength = strlen(kServerTag);
+ // Construct a comma-separated string of DNS addresses.
// Reserve sufficient space for an IPv6 link-local address: all but the
// last address are followed by ','; the last is followed by '\0'.
static const size_t kMaxSingleAddressLength =
INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(",");
- const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength;
+ const size_t bufsize = numaddrs * kMaxSingleAddressLength;
char *buf = (char *) malloc(bufsize);
if (!buf) {
SLOGE("RDNSS option: out of memory\n");
return false;
}
- strcpy(buf, kServerTag);
- size_t pos = kTagLength;
struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
+ size_t pos = 0;
for (int i = 0; i < numaddrs; i++) {
if (i > 0) {
buf[pos++] = ',';
@@ -508,7 +509,8 @@
mSubsystem = strdup("net");
asprintf(&mParams[0], "INTERFACE=%s", ifname);
asprintf(&mParams[1], "LIFETIME=%u", lifetime);
- mParams[2] = buf;
+ asprintf(&mParams[2], "SERVERS=%s", buf);
+ free(buf);
} else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
// TODO: support DNSSL.
} else {
@@ -634,7 +636,9 @@
else if (!strcmp(a, "change"))
mAction = Action::kChange;
} else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != nullptr) {
- mSeq = atoi(a);
+ if (!ParseInt(a, &mSeq)) {
+ SLOGE("NetlinkEvent::parseAsciiNetlinkMessage: failed to parse SEQNUM=%s", a);
+ }
} else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != nullptr) {
mSubsystem = strdup(a);
} else if (param_idx < NL_PARAMS_MAX) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index b7650a1..5423de5 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -241,6 +241,7 @@
"tests/files/offline/debug_frame_first_x86/*",
"tests/files/offline/debug_frame_load_bias_arm/*",
"tests/files/offline/eh_frame_hdr_begin_x86_64/*",
+ "tests/files/offline/invalid_elf_offset_arm/*",
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/jit_map_arm/*",
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index dee8eb3..f0e4138 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -29,12 +29,12 @@
#include <unwindstack/DwarfSection.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include "DwarfDebugFrame.h"
#include "DwarfEhFrame.h"
#include "DwarfEhFrameWithHdr.h"
+#include "MemoryBuffer.h"
#include "Symbols.h"
namespace unwindstack {
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 20bc4b9..8a85607 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -23,7 +23,8 @@
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryRange.h"
// This implements the JIT Compilation Interface.
// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 03658b4..5b30a4d 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -27,7 +27,9 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryFileAtOffset.h"
+#include "MemoryRange.h"
namespace unwindstack {
@@ -231,11 +233,13 @@
}
}
- // If there is a read-only map then a read-execute map that represents the
- // same elf object, make sure the previous map is using the same elf
- // object if it hasn't already been set.
- if (prev_map != nullptr && elf_start_offset != offset && prev_map->offset == elf_start_offset &&
- prev_map->name == name) {
+ if (!elf->valid()) {
+ elf_start_offset = offset;
+ } else if (prev_map != nullptr && elf_start_offset != offset &&
+ prev_map->offset == elf_start_offset && prev_map->name == name) {
+ // If there is a read-only map then a read-execute map that represents the
+ // same elf object, make sure the previous map is using the same elf
+ // object if it hasn't already been set.
std::lock_guard<std::mutex> guard(prev_map->mutex_);
if (prev_map->elf.get() == nullptr) {
prev_map->elf = elf;
@@ -296,7 +300,7 @@
std::string MapInfo::GetBuildID() {
uintptr_t id = build_id.load();
- if (build_id != 0) {
+ if (id != 0) {
return *reinterpret_cast<std::string*>(id);
}
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 9904fef..a66cd5b 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -32,6 +32,14 @@
#include <unwindstack/Memory.h>
#include "Check.h"
+#include "MemoryBuffer.h"
+#include "MemoryCache.h"
+#include "MemoryFileAtOffset.h"
+#include "MemoryLocal.h"
+#include "MemoryOffline.h"
+#include "MemoryOfflineBuffer.h"
+#include "MemoryRange.h"
+#include "MemoryRemote.h"
namespace unwindstack {
@@ -168,6 +176,16 @@
return false;
}
+std::unique_ptr<Memory> Memory::CreateFileMemory(const std::string& path, uint64_t offset) {
+ auto memory = std::make_unique<MemoryFileAtOffset>();
+
+ if (memory->Init(path, offset)) {
+ return memory;
+ }
+
+ return nullptr;
+}
+
std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
if (pid == getpid()) {
return std::shared_ptr<Memory>(new MemoryLocal());
@@ -182,6 +200,11 @@
return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
}
+std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_t start,
+ uint64_t end) {
+ return std::shared_ptr<Memory>(new MemoryOfflineBuffer(data, start, end));
+}
+
size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
if (addr >= raw_.size()) {
return 0;
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
new file mode 100644
index 0000000..3fe4bbb
--- /dev/null
+++ b/libunwindstack/MemoryBuffer.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryBuffer : public Memory {
+ public:
+ MemoryBuffer() = default;
+ virtual ~MemoryBuffer() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ uint8_t* GetPtr(size_t offset);
+
+ void Resize(size_t size) { raw_.resize(size); }
+
+ uint64_t Size() { return raw_.size(); }
+
+ private:
+ std::vector<uint8_t> raw_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_BUFFER_H
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
new file mode 100644
index 0000000..769d907
--- /dev/null
+++ b/libunwindstack/MemoryCache.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_CACHE_H
+#define _LIBUNWINDSTACK_MEMORY_CACHE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryCache : public Memory {
+ public:
+ MemoryCache(Memory* memory) : impl_(memory) {}
+ virtual ~MemoryCache() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ void Clear() override { cache_.clear(); }
+
+ private:
+ constexpr static size_t kCacheBits = 12;
+ constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
+ constexpr static size_t kCacheSize = 1 << kCacheBits;
+ std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
+
+ std::unique_ptr<Memory> impl_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_CACHE_H
diff --git a/libunwindstack/MemoryFileAtOffset.h b/libunwindstack/MemoryFileAtOffset.h
new file mode 100644
index 0000000..d136eb4
--- /dev/null
+++ b/libunwindstack/MemoryFileAtOffset.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryFileAtOffset : public Memory {
+ public:
+ MemoryFileAtOffset() = default;
+ virtual ~MemoryFileAtOffset();
+
+ bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ size_t Size() { return size_; }
+
+ void Clear() override;
+
+ protected:
+ size_t size_ = 0;
+ size_t offset_ = 0;
+ uint8_t* data_ = nullptr;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
new file mode 100644
index 0000000..29aaf12
--- /dev/null
+++ b/libunwindstack/MemoryLocal.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_LOCAL_H
+#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryLocal : public Memory {
+ public:
+ MemoryLocal() = default;
+ virtual ~MemoryLocal() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_LOCAL_H
diff --git a/libunwindstack/MemoryOffline.h b/libunwindstack/MemoryOffline.h
new file mode 100644
index 0000000..789f1a2
--- /dev/null
+++ b/libunwindstack/MemoryOffline.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.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+class MemoryOffline : public Memory {
+ public:
+ MemoryOffline() = default;
+ virtual ~MemoryOffline() = default;
+
+ bool Init(const std::string& file, uint64_t offset);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::unique_ptr<MemoryRange> memory_;
+};
+
+class MemoryOfflineParts : public Memory {
+ public:
+ MemoryOfflineParts() = default;
+ virtual ~MemoryOfflineParts();
+
+ void Add(MemoryOffline* memory) { memories_.push_back(memory); }
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::vector<MemoryOffline*> memories_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_H
diff --git a/libunwindstack/MemoryOfflineBuffer.h b/libunwindstack/MemoryOfflineBuffer.h
new file mode 100644
index 0000000..64c49a1
--- /dev/null
+++ b/libunwindstack/MemoryOfflineBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryOfflineBuffer : public Memory {
+ public:
+ MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
+ virtual ~MemoryOfflineBuffer() = default;
+
+ void Reset(const uint8_t* data, uint64_t start, uint64_t end);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ const uint8_t* data_;
+ uint64_t start_;
+ uint64_t end_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
diff --git a/libunwindstack/MemoryRange.h b/libunwindstack/MemoryRange.h
new file mode 100644
index 0000000..3b4ab5c
--- /dev/null
+++ b/libunwindstack/MemoryRange.h
@@ -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.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_RANGE_H
+#define _LIBUNWINDSTACK_MEMORY_RANGE_H
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// MemoryRange maps one address range onto another.
+// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
+// such that range.read(offset) is equivalent to underlying.read(src_begin).
+class MemoryRange : public Memory {
+ public:
+ MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+ uint64_t offset);
+ virtual ~MemoryRange() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ uint64_t offset() { return offset_; }
+ uint64_t length() { return length_; }
+
+ private:
+ std::shared_ptr<Memory> memory_;
+ uint64_t begin_;
+ uint64_t length_;
+ uint64_t offset_;
+};
+
+class MemoryRanges : public Memory {
+ public:
+ MemoryRanges() = default;
+ virtual ~MemoryRanges() = default;
+
+ void Insert(MemoryRange* memory);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_RANGE_H
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
new file mode 100644
index 0000000..db367d6
--- /dev/null
+++ b/libunwindstack/MemoryRemote.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_REMOTE_H
+#define _LIBUNWINDSTACK_MEMORY_REMOTE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <atomic>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryRemote : public Memory {
+ public:
+ MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
+ virtual ~MemoryRemote() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ pid_t pid() { return pid_; }
+
+ private:
+ pid_t pid_;
+ std::atomic_uintptr_t read_redirect_func_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_REMOTE_H
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 37323dc..c95f852 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -63,7 +63,10 @@
if (info != nullptr) {
frame->map_start = info->start;
frame->map_end = info->end;
- frame->map_elf_start_offset = info->elf_start_offset;
+ // Since this is a dex file frame, the elf_start_offset is not set
+ // by any of the normal code paths. Use the offset of the map since
+ // that matches the actual offset.
+ frame->map_elf_start_offset = info->offset;
frame->map_exact_offset = info->offset;
frame->map_load_bias = info->load_bias;
frame->map_flags = info->flags;
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 025fd98..13ce10f 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -25,10 +25,11 @@
#include <string>
#include <unwindstack/Elf.h>
-#include <unwindstack/Memory.h>
namespace unwindstack {
+class MemoryFileAtOffset;
+
struct MapInfo {
MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const char* name)
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index b3beb6e..3106564 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -21,12 +21,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <atomic>
-#include <map>
#include <memory>
#include <string>
-#include <unordered_map>
-#include <vector>
namespace unwindstack {
@@ -37,6 +33,9 @@
static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
+ static std::shared_ptr<Memory> CreateOfflineMemory(const uint8_t* data, uint64_t start,
+ uint64_t end);
+ static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
@@ -55,157 +54,6 @@
}
};
-class MemoryCache : public Memory {
- public:
- MemoryCache(Memory* memory) : impl_(memory) {}
- virtual ~MemoryCache() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- void Clear() override { cache_.clear(); }
-
- private:
- constexpr static size_t kCacheBits = 12;
- constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
- constexpr static size_t kCacheSize = 1 << kCacheBits;
- std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
-
- std::unique_ptr<Memory> impl_;
-};
-
-class MemoryBuffer : public Memory {
- public:
- MemoryBuffer() = default;
- virtual ~MemoryBuffer() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- uint8_t* GetPtr(size_t offset);
-
- void Resize(size_t size) { raw_.resize(size); }
-
- uint64_t Size() { return raw_.size(); }
-
- private:
- std::vector<uint8_t> raw_;
-};
-
-class MemoryFileAtOffset : public Memory {
- public:
- MemoryFileAtOffset() = default;
- virtual ~MemoryFileAtOffset();
-
- bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- size_t Size() { return size_; }
-
- void Clear() override;
-
- protected:
- size_t size_ = 0;
- size_t offset_ = 0;
- uint8_t* data_ = nullptr;
-};
-
-class MemoryRemote : public Memory {
- public:
- MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
- virtual ~MemoryRemote() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- pid_t pid() { return pid_; }
-
- private:
- pid_t pid_;
- std::atomic_uintptr_t read_redirect_func_;
-};
-
-class MemoryLocal : public Memory {
- public:
- MemoryLocal() = default;
- virtual ~MemoryLocal() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-};
-
-// MemoryRange maps one address range onto another.
-// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
-// such that range.read(offset) is equivalent to underlying.read(src_begin).
-class MemoryRange : public Memory {
- public:
- MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
- uint64_t offset);
- virtual ~MemoryRange() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- uint64_t offset() { return offset_; }
- uint64_t length() { return length_; }
-
- private:
- std::shared_ptr<Memory> memory_;
- uint64_t begin_;
- uint64_t length_;
- uint64_t offset_;
-};
-
-class MemoryRanges : public Memory {
- public:
- MemoryRanges() = default;
- virtual ~MemoryRanges() = default;
-
- void Insert(MemoryRange* memory);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
-};
-
-class MemoryOffline : public Memory {
- public:
- MemoryOffline() = default;
- virtual ~MemoryOffline() = default;
-
- bool Init(const std::string& file, uint64_t offset);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- std::unique_ptr<MemoryRange> memory_;
-};
-
-class MemoryOfflineBuffer : public Memory {
- public:
- MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
- virtual ~MemoryOfflineBuffer() = default;
-
- void Reset(const uint8_t* data, uint64_t start, uint64_t end);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- const uint8_t* data_;
- uint64_t start_;
- uint64_t end_;
-};
-
-class MemoryOfflineParts : public Memory {
- public:
- MemoryOfflineParts() = default;
- virtual ~MemoryOfflineParts();
-
- void Add(MemoryOffline* memory) { memories_.push_back(memory); }
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- std::vector<MemoryOffline*> memories_;
-};
-
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 5f3d1ea..7b1dd92 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -1662,7 +1662,7 @@
ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
}
-INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
- ::testing::Values("logging", "register_logging", "no_logging"));
+INSTANTIATE_TEST_SUITE_P(, ArmExidxDecodeTest,
+ ::testing::Values("logging", "register_logging", "no_logging"));
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index bb2e8f0..9dd0cdd 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -66,7 +66,7 @@
DwarfCie cie_;
DwarfFde fde_;
};
-TYPED_TEST_CASE_P(DwarfCfaLogTest);
+TYPED_TEST_SUITE_P(DwarfCfaLogTest);
// NOTE: All class variable references have to be prefaced with this->.
@@ -763,17 +763,17 @@
ASSERT_EQ("", GetFakeLogBuf());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
- cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
- cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
- cfa_undefined, cfa_same, cfa_register, cfa_state,
- cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
- cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
- cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
- cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
- cfa_gnu_negative_offset_extended, cfa_register_override);
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+ cfa_undefined, cfa_same, cfa_register, cfa_state,
+ cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+ cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+ cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+ cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 7395b04..dd71490 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -64,7 +64,7 @@
DwarfCie cie_;
DwarfFde fde_;
};
-TYPED_TEST_CASE_P(DwarfCfaTest);
+TYPED_TEST_SUITE_P(DwarfCfaTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -952,16 +952,17 @@
ASSERT_EQ("", GetFakeLogBuf());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
- cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
- cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
- cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
- cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
- cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
- cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
- cfa_gnu_negative_offset_extended, cfa_register_override);
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+ cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+ cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+ cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+ cfa_val_offset, cfa_val_offset_sf, cfa_val_expression,
+ cfa_gnu_args_size, cfa_gnu_negative_offset_extended,
+ cfa_register_override);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaTest, DwarfCfaTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 120bd73..2b36f17 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -44,7 +44,7 @@
MemoryFake memory_;
DwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfDebugFrameTest);
+TYPED_TEST_SUITE_P(DwarfDebugFrameTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -812,7 +812,7 @@
EXPECT_EQ(0xb50U, fde->pc_end);
}
-REGISTER_TYPED_TEST_CASE_P(
+REGISTER_TYPED_TEST_SUITE_P(
DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
@@ -825,6 +825,6 @@
GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 9cac6e8..4792fb5 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -42,7 +42,7 @@
MemoryFake memory_;
DwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfEhFrameTest);
+TYPED_TEST_SUITE_P(DwarfEhFrameTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -125,9 +125,9 @@
EXPECT_EQ(1U, cie->return_address_register);
}
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index be9e721..78608e3 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -73,7 +73,7 @@
MemoryFake memory_;
TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
+TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -446,14 +446,14 @@
ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
}
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
- GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
- GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
- GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
- GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_search,
- GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+ GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+ GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+ GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
+ GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_search,
+ GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index 3f09dd8..f4ade5d 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -48,7 +48,7 @@
std::unique_ptr<DwarfMemory> mem_;
std::unique_ptr<DwarfOp<TypeParam>> op_;
};
-TYPED_TEST_CASE_P(DwarfOpLogTest);
+TYPED_TEST_SUITE_P(DwarfOpLogTest);
TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
// Multi operation opcodes.
@@ -65,9 +65,9 @@
ASSERT_EQ(expected, lines);
}
-REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+REGISTER_TYPED_TEST_SUITE_P(DwarfOpLogTest, multiple_ops);
typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index d424d5f..0898ec0 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -48,7 +48,7 @@
std::unique_ptr<DwarfMemory> mem_;
std::unique_ptr<DwarfOp<TypeParam>> op_;
};
-TYPED_TEST_CASE_P(DwarfOpTest);
+TYPED_TEST_SUITE_P(DwarfOpTest);
TYPED_TEST_P(DwarfOpTest, decode) {
// Memory error.
@@ -1571,15 +1571,16 @@
EXPECT_FALSE(this->op_->dex_pc_set());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
- op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
- const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
- op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or, op_plus,
- op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
- compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
- op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop, is_dex_pc);
+REGISTER_TYPED_TEST_SUITE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
+ op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
+ const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
+ op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or,
+ op_plus, op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
+ compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
+ op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop,
+ is_dex_pc);
typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpTest, DwarfOpTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 46f555a..b386ef4 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -68,7 +68,7 @@
MemoryFake memory_;
TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfSectionImplTest);
+TYPED_TEST_SUITE_P(DwarfSectionImplTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -571,18 +571,18 @@
ASSERT_EQ("", GetFakeLogBuf());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
- GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
- Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
- Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
- Eval_cfa_register_prev, Eval_cfa_register_from_value,
- Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
- Eval_invalid_register, Eval_different_reg_locations,
- Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
- Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
- GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
+REGISTER_TYPED_TEST_SUITE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
+ GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
+ Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
+ Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
+ Eval_cfa_register_prev, Eval_cfa_register_from_value,
+ Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
+ Eval_invalid_register, Eval_different_reg_locations,
+ Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
+ Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 07fd6f6..5735858 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -31,7 +31,7 @@
class ElfCacheTest : public ::testing::Test {
protected:
- static void SetUpTestCase() { memory_.reset(new MemoryFake); }
+ static void SetUpTestSuite() { memory_.reset(new MemoryFake); }
void SetUp() override { Elf::SetCachingEnabled(true); }
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 6be8bdc..5b4ca7c 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -58,7 +58,7 @@
ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
}
- static void SetUpTestCase() {
+ static void SetUpTestSuite() {
std::vector<uint8_t> buffer(12288, 0);
memcpy(buffer.data(), ELFMAG, SELFMAG);
buffer[EI_CLASS] = ELFCLASS32;
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
index 28e0e76..a6c12aa 100644
--- a/libunwindstack/tests/MemoryBufferTest.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,9 +18,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "LogFake.h"
+#include "MemoryBuffer.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
index a3def20..3bd3e4d 100644
--- a/libunwindstack/tests/MemoryCacheTest.cpp
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -20,8 +20,7 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
+#include "MemoryCache.h"
#include "MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index d7d1ace..4124a49 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
+#include "MemoryFileAtOffset.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 5a389d0..c9e5dc0 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -22,7 +22,7 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
+#include "MemoryLocal.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
index f022884..9531708 100644
--- a/libunwindstack/tests/MemoryOfflineBufferTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
@@ -18,9 +18,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "LogFake.h"
+#include "MemoryOfflineBuffer.h"
namespace unwindstack {
@@ -31,7 +30,7 @@
memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd));
}
- static void SetUpTestCase() {
+ static void SetUpTestSuite() {
buffer_.resize(kLength);
for (size_t i = 0; i < kLength; i++) {
buffer_[i] = i % 189;
diff --git a/libunwindstack/tests/MemoryOfflineTest.cpp b/libunwindstack/tests/MemoryOfflineTest.cpp
index ab9aa9d..d0c441b 100644
--- a/libunwindstack/tests/MemoryOfflineTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -19,7 +19,8 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryOffline.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 2bac95b..2d4f141 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -21,9 +21,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "MemoryFake.h"
+#include "MemoryRange.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
index d24fcd2..e4e9fc4 100644
--- a/libunwindstack/tests/MemoryRangesTest.cpp
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -20,9 +20,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "MemoryFake.h"
+#include "MemoryRange.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index fb56e8a..c90dedc 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -30,7 +30,7 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
+#include "MemoryRemote.h"
#include "MemoryFake.h"
#include "TestUtils.h"
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 9a27dbd..7e36953 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -236,7 +236,7 @@
}
using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64, RegsMips, RegsMips64>;
-TYPED_TEST_CASE(RegsIterateTest, RegTypes);
+TYPED_TEST_SUITE(RegsIterateTest, RegTypes);
TYPED_TEST(RegsIterateTest, iterate) {
std::vector<Register> expected = ExpectedRegisters<TypeParam>();
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index b40a253..ae3c349 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -55,7 +55,7 @@
MemoryFake memory_;
};
-TYPED_TEST_CASE_P(SymbolsTest);
+TYPED_TEST_SUITE_P(SymbolsTest);
TYPED_TEST_P(SymbolsTest, function_bounds_check) {
Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
@@ -362,11 +362,11 @@
EXPECT_EQ(4U, offset);
}
-REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
- multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
- symtab_read_cached, get_global);
+REGISTER_TYPED_TEST_SUITE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+ multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
+ symtab_read_cached, get_global);
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, SymbolsTest, SymbolsTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 6c64c40..baada82 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -35,7 +35,6 @@
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
@@ -43,6 +42,7 @@
#include <unwindstack/Unwinder.h>
#include "ElfTestUtils.h"
+#include "MemoryOffline.h"
#include "TestUtils.h"
namespace unwindstack {
@@ -62,7 +62,7 @@
free(cwd_);
}
- void Init(const char* file_dir, ArchEnum arch) {
+ void Init(const char* file_dir, ArchEnum arch, bool add_stack = true) {
dir_ = TestGetFileDirectory() + "offline/" + file_dir;
std::string data;
@@ -71,23 +71,25 @@
maps_.reset(new BufferMaps(data.c_str()));
ASSERT_TRUE(maps_->Parse());
- std::string stack_name(dir_ + "stack.data");
- struct stat st;
- if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
- std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
- ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
- process_memory_.reset(stack_memory.release());
- } else {
- std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
- for (size_t i = 0;; i++) {
- stack_name = dir_ + "stack" + std::to_string(i) + ".data";
- if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
- ASSERT_TRUE(i != 0) << "No stack data files found.";
- break;
+ if (add_stack) {
+ std::string stack_name(dir_ + "stack.data");
+ struct stat st;
+ if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+ std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+ ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+ process_memory_.reset(stack_memory.release());
+ } else {
+ std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
+ for (size_t i = 0;; i++) {
+ stack_name = dir_ + "stack" + std::to_string(i) + ".data";
+ if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
+ ASSERT_TRUE(i != 0) << "No stack data files found.";
+ break;
+ }
+ AddMemory(stack_name, stack_memory.get());
}
- AddMemory(stack_name, stack_memory.get());
+ process_memory_.reset(stack_memory.release());
}
- process_memory_.reset(stack_memory.release());
}
switch (arch) {
@@ -1442,4 +1444,17 @@
EXPECT_EQ(0x7be4f07d20ULL, unwinder.frames()[12].sp);
}
+TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("invalid_elf_offset_arm/", ARCH_ARM, false));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(1U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(" #00 pc 00aa7508 invalid.apk (offset 0x12e4000)\n", frame_info);
+ EXPECT_EQ(0xc898f508, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 4e38015..f76a101 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -35,11 +35,11 @@
#include <android-base/threads.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
#include <unwindstack/Unwinder.h>
+#include "MemoryRemote.h"
#include "TestUtils.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 30e57a1..ef1950c 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -54,7 +54,7 @@
}
}
- static void SetUpTestCase() {
+ static void SetUpTestSuite() {
maps_.reset(new Maps);
ElfFake* elf = new ElfFake(new MemoryFake);
@@ -132,6 +132,10 @@
const auto& info6 = *--maps_->end();
info6->memory_backed_elf = true;
+ AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
+ const auto& info7 = *--maps_->end();
+ info7->load_bias = 0;
+
process_memory_.reset(new MemoryFake);
}
@@ -1015,6 +1019,50 @@
EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
}
+TEST_F(UnwinderTest, dex_pc_in_map_non_zero_offset) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xd0400);
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xd0400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk", frame->map_name);
+ EXPECT_EQ(0x1000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1000U, frame->map_exact_offset);
+ EXPECT_EQ(0xd0000U, frame->map_start);
+ EXPECT_EQ(0xd1000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
TEST_F(UnwinderTest, dex_pc_not_in_map) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
regs_.set_pc(0x1000);
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
new file mode 100644
index 0000000..022404c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
@@ -0,0 +1 @@
+c7ee8000-c8c52fff r-xp 12e4000 00:00 0 invalid.apk
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
new file mode 100644
index 0000000..b7f10ef
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: c0434c00
+r1: 2a4c9fbc
+r2: 00000000
+r3: c83ef1f9
+r4: 00000004
+r5: c2044904
+r6: 00000000
+r7: c20443b8
+r8: 000b33ff
+r9: c20444b0
+r10: cac90740
+r11: 00000000
+ip: ed891ca4
+sp: c2044218
+lr: ed807265
+pc: c898f508
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 92e5c0a..7a6d8ba 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -31,6 +31,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
#include "ArmExidx.h"
#include "ElfInterfaceArm.h"
@@ -105,14 +106,7 @@
// Send all log messages to stdout.
log_to_stdout(true);
- MemoryFileAtOffset* memory = new MemoryFileAtOffset;
- if (!memory->Init(file, offset)) {
- // Initializatation failed.
- printf("Failed to init\n");
- return 1;
- }
-
- Elf elf(memory);
+ Elf elf(Memory::CreateFileMemory(file, offset).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
return 1;
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index b77a86b..d0562d9 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -33,6 +33,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
#include "ArmExidx.h"
#include "DwarfOp.h"
@@ -165,14 +166,7 @@
}
int GetInfo(const char* file, uint64_t pc) {
- MemoryFileAtOffset* memory = new MemoryFileAtOffset;
- if (!memory->Init(file, 0)) {
- // Initializatation failed.
- printf("Failed to init\n");
- return 1;
- }
-
- Elf elf(memory);
+ Elf elf(Memory::CreateFileMemory(file, pc).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
return 1;
@@ -205,7 +199,7 @@
DwarfSection* section = interface->eh_frame();
if (section != nullptr) {
printf("\neh_frame:\n");
- PrintRegInformation(section, memory, pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type());
} else {
printf("\nno eh_frame information\n");
}
@@ -213,7 +207,7 @@
section = interface->debug_frame();
if (section != nullptr) {
printf("\ndebug_frame:\n");
- PrintRegInformation(section, memory, pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type());
printf("\n");
} else {
printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index b0a4dd0..8df2284 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -59,13 +59,7 @@
// Send all log messages to stdout.
unwindstack::log_to_stdout(true);
- unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
- if (!memory->Init(argv[1], 0)) {
- printf("Failed to init\n");
- return 1;
- }
-
- unwindstack::Elf elf(memory);
+ unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", argv[1]);
return 1;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 4f194c7..8be4dd0 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -17,6 +17,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
header_libs: [
"liblog_headers",
@@ -121,6 +122,7 @@
cc_library {
name: "libutils",
defaults: ["libutils_defaults"],
+ native_bridge_supported: true,
srcs: [
"FileMap.cpp",
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 2d696eb..14e3e35 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -14,7 +14,9 @@
#define DEBUG_CALLBACKS 0
#include <utils/Looper.h>
+
#include <sys/eventfd.h>
+#include <cinttypes>
namespace android {
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index 546c15c..e5b536c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -2,6 +2,7 @@
cc_library {
name: "libvndksupport",
+ native_bridge_supported: true,
srcs: ["linker.c"],
cflags: [
"-Wall",
@@ -22,6 +23,7 @@
llndk_library {
name: "libvndksupport",
+ native_bridge_supported: true,
symbol_file: "libvndksupport.map.txt",
export_include_dirs: ["include"],
}
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index bc1543b..3843252 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -26,6 +26,8 @@
// Incorrectly warns when C++11 empty brace {} initializer is used.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
"-Wno-missing-field-initializers",
+ "-Wconversion",
+ "-Wno-sign-conversion",
],
// Enable -Wold-style-cast only for non-Windows targets. _islower_l,
@@ -82,6 +84,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
vndk: {
enabled: true,
},
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index 1714586..10311b5 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -20,9 +20,15 @@
#include <stddef.h>
#include <stdint.h>
+#include <limits>
+
// Check if |length| bytes at |entry_name| constitute a valid entry name.
-// Entry names must be valid UTF-8 and must not contain '0'.
+// Entry names must be valid UTF-8 and must not contain '0'. They also must
+// fit into the central directory record.
inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
+ if (length > std::numeric_limits<uint16_t>::max()) {
+ return false;
+ }
for (size_t i = 0; i < length; ++i) {
const uint8_t byte = entry_name[i];
if (byte == 0) {
@@ -35,7 +41,8 @@
return false;
} else {
// 2-5 byte sequences.
- for (uint8_t first = (byte & 0x7f) << 1; first & 0x80; first = (first & 0x7f) << 1) {
+ for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
+ first = static_cast<uint8_t>((first & 0x7f) << 1)) {
++i;
// Missing continuation byte..
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index ab38dfd..e3ac114 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,6 +25,9 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <string>
+#include <string_view>
+
#include "android-base/off64_t.h"
/* Zip compression methods we support */
@@ -33,32 +36,6 @@
kCompressDeflated = 8, // standard deflate
};
-struct ZipString {
- const uint8_t* name;
- uint16_t name_length;
-
- ZipString() {}
-
- /*
- * entry_name has to be an c-style string with only ASCII characters.
- */
- explicit ZipString(const char* entry_name);
-
- bool operator==(const ZipString& rhs) const {
- return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
- }
-
- bool StartsWith(const ZipString& prefix) const {
- return name && (name_length >= prefix.name_length) &&
- (memcmp(name, prefix.name, prefix.name_length) == 0);
- }
-
- bool EndsWith(const ZipString& suffix) const {
- return name && (name_length >= suffix.name_length) &&
- (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0);
- }
-};
-
/*
* Represents information about a zip entry in a zip file.
*/
@@ -149,8 +126,7 @@
void CloseArchive(ZipArchiveHandle archive);
/*
- * Find an entry in the Zip archive, by name. |entryName| must be a null
- * terminated string, and |data| must point to a writeable memory location.
+ * Find an entry in the Zip archive, by name. |data| must be non-null.
*
* Returns 0 if an entry is found, and populates |data| with information
* about this entry. Returns negative values otherwise.
@@ -164,7 +140,7 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
-int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
/*
* Start iterating over all entries of a zip file. The order of iteration
@@ -180,7 +156,8 @@
* Returns 0 on success and negative values on failure.
*/
int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const ZipString* optional_prefix, const ZipString* optional_suffix);
+ const std::string_view optional_prefix = "",
+ const std::string_view optional_suffix = "");
/*
* Advance to the next element in the zipfile in iteration order.
@@ -188,7 +165,8 @@
* Returns 0 on success, -1 if there are no more elements in this
* archive and lower negative values on failure.
*/
-int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
/*
* End iteration over all entries of a zip file and frees the memory allocated
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index f6c8427..a2a0dbf 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -21,6 +21,7 @@
#include <memory>
#include <string>
+#include <string_view>
#include <vector>
#include "android-base/macros.h"
@@ -76,7 +77,7 @@
uint32_t uncompressed_size;
uint16_t last_mod_time;
uint16_t last_mod_date;
- uint32_t padding_length;
+ uint16_t padding_length;
off64_t local_file_header_offset;
};
@@ -101,7 +102,7 @@
* Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
* Returns 0 on success, and an error value < 0 on failure.
*/
- int32_t StartEntry(const char* path, size_t flags);
+ int32_t StartEntry(std::string_view path, size_t flags);
/**
* Starts a new zip entry with the given path and flags, where the
@@ -111,17 +112,17 @@
* Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
* Returns 0 on success, and an error value < 0 on failure.
*/
- int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment);
+ int32_t StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment);
/**
* Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
*/
- int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+ int32_t StartEntryWithTime(std::string_view path, size_t flags, time_t time);
/**
* Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
*/
- int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment);
+ int32_t StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time, uint32_t alignment);
/**
* Writes bytes to the zip file for the previously started zip entry.
@@ -161,8 +162,8 @@
int32_t HandleError(int32_t error_code);
int32_t PrepareDeflate();
- int32_t StoreBytes(FileEntry* file, const void* data, size_t len);
- int32_t CompressBytes(FileEntry* file, const void* data, size_t len);
+ int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
+ int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
int32_t FlushCompressedBytes(FileEntry* file);
enum class State {
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 6756007..426325e 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -17,6 +17,7 @@
#include <errno.h>
#include <error.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <getopt.h>
#include <inttypes.h>
#include <stdio.h>
@@ -52,9 +53,21 @@
static uint64_t total_compressed_length = 0;
static size_t file_count = 0;
-static bool Filter(const std::string& name) {
- if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
- if (!includes.empty() && includes.find(name) == includes.end()) return true;
+static bool ShouldInclude(const std::string& name) {
+ // Explicitly excluded?
+ if (!excludes.empty()) {
+ for (const auto& exclude : excludes) {
+ if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
+ }
+ }
+
+ // Implicitly included?
+ if (includes.empty()) return true;
+
+ // Explicitly included?
+ for (const auto& include : includes) {
+ if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
+ }
return false;
}
@@ -72,7 +85,7 @@
static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
if (uncompressed == 0) return 0;
- return (100LL * (uncompressed - compressed)) / uncompressed;
+ return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
}
static void MaybeShowHeader() {
@@ -236,16 +249,15 @@
// libziparchive iteration order doesn't match the central directory.
// We could sort, but that would cost extra and wouldn't match either.
void* cookie;
- int err = StartIteration(zah, &cookie, nullptr, nullptr);
+ int err = StartIteration(zah, &cookie);
if (err != 0) {
error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
}
ZipEntry entry;
- ZipString string;
- while ((err = Next(cookie, &entry, &string)) >= 0) {
- std::string name(string.name, string.name + string.name_length);
- if (!Filter(name)) ProcessOne(zah, entry, name);
+ std::string name;
+ while ((err = Next(cookie, &entry, &name)) >= 0) {
+ if (ShouldInclude(name)) ProcessOne(zah, entry, name);
}
if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
@@ -260,7 +272,8 @@
printf(
"\n"
- "Extract FILEs from ZIP archive. Default is all files.\n"
+ "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+ "exclude (-x) lists use shell glob patterns.\n"
"\n"
"-d DIR Extract into DIR\n"
"-l List contents (-lq excludes archive name, -lv is verbose)\n"
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 0710d0a..c95b035 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -47,6 +47,7 @@
#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
#include <android-base/mapped_file.h>
#include <android-base/memory.h>
+#include <android-base/strings.h>
#include <android-base/utf8.h>
#include <log/log.h>
#include "zlib.h"
@@ -101,38 +102,8 @@
return val;
}
-static uint32_t ComputeHash(const ZipString& name) {
-#if !defined(_WIN32)
- return std::hash<std::string_view>{}(
- std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
-#else
- // Remove this code path once the windows compiler knows how to compile the above statement.
- uint32_t hash = 0;
- uint16_t len = name.name_length;
- const uint8_t* str = name.name;
-
- while (len--) {
- hash = hash * 31 + *str++;
- }
-
- return hash;
-#endif
-}
-
-static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
- const ZipStringOffset& zip_string_offset) {
- const ZipString from_offset = zip_string_offset.GetZipString(start);
- return from_offset == zip_string;
-}
-
-/**
- * Returns offset of ZipString#name from the start of the central directory in the memory map.
- * For valid ZipStrings contained in the zip archive mmap, 0 < offset < 0xffffff.
- */
-static inline uint32_t GetOffset(const uint8_t* name, const uint8_t* start) {
- CHECK_GT(name, start);
- CHECK_LT(name, start + 0xffffff);
- return static_cast<uint32_t>(name - start);
+static uint32_t ComputeHash(std::string_view name) {
+ return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
}
/*
@@ -140,27 +111,27 @@
* valid range.
*/
static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
- const ZipString& name, const uint8_t* start) {
+ std::string_view name, const uint8_t* start) {
const uint32_t hash = ComputeHash(name);
// NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
uint32_t ent = hash & (hash_table_size - 1);
while (hash_table[ent].name_offset != 0) {
- if (isZipStringEqual(start, name, hash_table[ent])) {
+ if (hash_table[ent].ToStringView(start) == name) {
return ent;
}
ent = (ent + 1) & (hash_table_size - 1);
}
- ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
+ ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
return kEntryNotFound;
}
/*
* Add a new entry to the hash table.
*/
-static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size,
- const ZipString& name, const uint8_t* start) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint32_t hash_table_size,
+ std::string_view name, const uint8_t* start) {
const uint64_t hash = ComputeHash(name);
uint32_t ent = hash & (hash_table_size - 1);
@@ -169,15 +140,18 @@
* Further, we guarantee that the hashtable size is not 0.
*/
while (hash_table[ent].name_offset != 0) {
- if (isZipStringEqual(start, name, hash_table[ent])) {
- // We've found a duplicate entry. We don't accept it
- ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
+ if (hash_table[ent].ToStringView(start) == name) {
+ // We've found a duplicate entry. We don't accept duplicates.
+ ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
return kDuplicateEntry;
}
ent = (ent + 1) & (hash_table_size - 1);
}
- hash_table[ent].name_offset = GetOffset(name.name, start);
- hash_table[ent].name_length = name.name_length;
+
+ // `name` has already been validated before entry.
+ const char* start_char = reinterpret_cast<const char*>(start);
+ hash_table[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
+ hash_table[ent].name_length = static_cast<uint16_t>(name.size());
return 0;
}
@@ -227,7 +201,7 @@
}
static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
- off64_t file_length, off64_t read_amount,
+ off64_t file_length, uint32_t read_amount,
uint8_t* scan_buffer) {
const off64_t search_start = file_length - read_amount;
@@ -243,7 +217,8 @@
* doing an initial minimal read; if we don't find it, retry with a
* second read as above.)
*/
- int i = read_amount - sizeof(EocdRecord);
+ CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
+ int32_t i = read_amount - sizeof(EocdRecord);
for (; i >= 0; i--) {
if (scan_buffer[i] == 0x50) {
uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
@@ -346,9 +321,9 @@
*
* We start by pulling in the last part of the file.
*/
- off64_t read_amount = kMaxEOCDSearch;
+ uint32_t read_amount = kMaxEOCDSearch;
if (file_length < read_amount) {
- read_amount = file_length;
+ read_amount = static_cast<uint32_t>(file_length);
}
std::vector<uint8_t> scan_buffer(read_amount);
@@ -378,7 +353,7 @@
reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
if (archive->hash_table == nullptr) {
ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
- archive->hash_table_size, sizeof(ZipString));
+ archive->hash_table_size, sizeof(ZipStringOffset));
return -1;
}
@@ -416,21 +391,19 @@
const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
if (file_name + file_name_length > cd_end) {
- ALOGW(
- "Zip: file name boundary exceeds the central directory range, file_name_length: "
- "%" PRIx16 ", cd_length: %zu",
- file_name_length, cd_length);
+ ALOGW("Zip: file name for entry %" PRIu16
+ " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
+ i, file_name_length, cd_length);
return -1;
}
- /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
+ // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
if (!IsValidEntryName(file_name, file_name_length)) {
+ ALOGW("Zip: invalid file name at entry %" PRIu16, i);
return -1;
}
- /* add the CDE filename to the hash table */
- ZipString entry_name;
- entry_name.name = file_name;
- entry_name.name_length = file_name_length;
+ // Add the CDE filename to the hash table.
+ std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
archive->central_directory.GetBasePtr());
if (add_result != 0) {
@@ -545,21 +518,19 @@
return 0;
}
-static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) {
+static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry* data) {
const uint16_t nameLen = archive->hash_table[ent].name_length;
// Recover the start of the central directory entry from the filename
// pointer. The filename is the first entry past the fixed-size data,
// so we can just subtract back from that.
- const ZipString from_offset =
- archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
- const uint8_t* ptr = from_offset.name;
+ const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
+ const uint8_t* ptr = base_ptr + archive->hash_table[ent].name_offset;
ptr -= sizeof(CentralDirectoryRecord);
// This is the base of our mmapped region, we have to sanity check that
// the name that's in the hash table is a pointer to a location within
// this mapped region.
- const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
ALOGW("Zip: Invalid entry pointer");
return kInvalidOffset;
@@ -651,26 +622,24 @@
// Check that the local file header name matches the declared
// name in the central directory.
- if (lfh->file_name_length == nameLen) {
- const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
- if (name_offset + lfh->file_name_length > cd_offset) {
- ALOGW("Zip: Invalid declared length");
- return kInvalidOffset;
- }
-
- std::vector<uint8_t> name_buf(nameLen);
- if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
- ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
- return kIoError;
- }
- const ZipString from_offset =
- archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
- if (memcmp(from_offset.name, name_buf.data(), nameLen)) {
- return kInconsistentInformation;
- }
-
- } else {
- ALOGW("Zip: lfh name did not match central directory.");
+ if (lfh->file_name_length != nameLen) {
+ ALOGW("Zip: lfh name length did not match central directory");
+ return kInconsistentInformation;
+ }
+ const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
+ if (name_offset + lfh->file_name_length > cd_offset) {
+ ALOGW("Zip: lfh name has invalid declared length");
+ return kInvalidOffset;
+ }
+ std::vector<uint8_t> name_buf(nameLen);
+ if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
+ return kIoError;
+ }
+ const std::string_view entry_name =
+ archive->hash_table[ent].ToStringView(archive->central_directory.GetBasePtr());
+ if (memcmp(entry_name.data(), name_buf.data(), nameLen) != 0) {
+ ALOGW("Zip: lfh name did not match central directory");
return kInconsistentInformation;
}
@@ -701,52 +670,32 @@
}
struct IterationHandle {
- uint32_t position;
- // We're not using vector here because this code is used in the Windows SDK
- // where the STL is not available.
- ZipString prefix;
- ZipString suffix;
ZipArchive* archive;
- IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) {
- if (in_prefix) {
- uint8_t* name_copy = new uint8_t[in_prefix->name_length];
- memcpy(name_copy, in_prefix->name, in_prefix->name_length);
- prefix.name = name_copy;
- prefix.name_length = in_prefix->name_length;
- } else {
- prefix.name = NULL;
- prefix.name_length = 0;
- }
- if (in_suffix) {
- uint8_t* name_copy = new uint8_t[in_suffix->name_length];
- memcpy(name_copy, in_suffix->name, in_suffix->name_length);
- suffix.name = name_copy;
- suffix.name_length = in_suffix->name_length;
- } else {
- suffix.name = NULL;
- suffix.name_length = 0;
- }
- }
+ std::string prefix;
+ std::string suffix;
- ~IterationHandle() {
- delete[] prefix.name;
- delete[] suffix.name;
- }
+ uint32_t position = 0;
+
+ IterationHandle(ZipArchive* archive, std::string_view in_prefix, std::string_view in_suffix)
+ : archive(archive), prefix(in_prefix), suffix(in_suffix) {}
};
int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const ZipString* optional_prefix, const ZipString* optional_suffix) {
+ const std::string_view optional_prefix,
+ const std::string_view optional_suffix) {
if (archive == NULL || archive->hash_table == NULL) {
ALOGW("Zip: Invalid ZipArchiveHandle");
return kInvalidHandle;
}
- IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
- cookie->position = 0;
- cookie->archive = archive;
+ if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
+ optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: prefix/suffix too long");
+ return kInvalidEntryName;
+ }
- *cookie_ptr = cookie;
+ *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix);
return 0;
}
@@ -754,22 +703,33 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
-int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
- if (entryName.name_length == 0) {
- ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+ ZipEntry* data) {
+ if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: Invalid filename of length %zu", entryName.size());
return kInvalidEntryName;
}
const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
archive->central_directory.GetBasePtr());
if (ent < 0) {
- ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
- return ent;
+ ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
+ return static_cast<int32_t>(ent); // kEntryNotFound is safe to truncate.
}
- return FindEntry(archive, ent, data);
+ // We know there are at most hash_table_size entries, safe to truncate.
+ return FindEntry(archive, static_cast<uint32_t>(ent), data);
}
-int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
+int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
+ std::string_view sv;
+ int32_t result = Next(cookie, data, &sv);
+ if (result == 0 && name) {
+ *name = std::string(sv);
+ }
+ return result;
+}
+
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
if (handle == NULL) {
ALOGW("Zip: Null ZipArchiveHandle");
@@ -786,16 +746,14 @@
const uint32_t hash_table_length = archive->hash_table_size;
const ZipStringOffset* hash_table = archive->hash_table;
for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
- const ZipString from_offset =
- hash_table[i].GetZipString(archive->central_directory.GetBasePtr());
- if (hash_table[i].name_offset != 0 &&
- (handle->prefix.name_length == 0 || from_offset.StartsWith(handle->prefix)) &&
- (handle->suffix.name_length == 0 || from_offset.EndsWith(handle->suffix))) {
+ const std::string_view entry_name =
+ hash_table[i].ToStringView(archive->central_directory.GetBasePtr());
+ if (hash_table[i].name_offset != 0 && (android::base::StartsWith(entry_name, handle->prefix) &&
+ android::base::EndsWith(entry_name, handle->suffix))) {
handle->position = (i + 1);
const int error = FindEntry(archive, i, data);
- if (!error) {
- name->name = from_offset.name;
- name->name_length = hash_table[i].name_length;
+ if (!error && name) {
+ *name = entry_name;
}
return error;
}
@@ -849,7 +807,6 @@
return FileWriter{};
}
- int result = 0;
#if defined(__linux__)
if (declared_length > 0) {
// Make sure we have enough space on the volume to extract the compressed
@@ -861,7 +818,7 @@
// EOPNOTSUPP error when issued in other filesystems.
// Hence, check for the return error code before concluding that the
// disk does not have enough space.
- result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+ long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
if (result == -1 && errno == ENOSPC) {
ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s",
static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
@@ -879,7 +836,7 @@
// Block device doesn't support ftruncate(2).
if (!S_ISBLK(sb.st_mode)) {
- result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+ long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
if (result == -1) {
ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
static_cast<int64_t>(declared_length + current_offset), strerror(errno));
@@ -998,16 +955,16 @@
std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
const bool compute_crc = (crc_out != nullptr);
- uint64_t crc = 0;
+ uLong crc = 0;
uint32_t remaining_bytes = compressed_length;
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
- const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+ const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
const uint32_t offset = (compressed_length - remaining_bytes);
// Make sure to read at offset to ensure concurrent access to the fd.
if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
- ALOGW("Zip: inflate read failed, getSize = %zu: %s", read_size, strerror(errno));
+ ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
return kIoError;
}
@@ -1031,7 +988,8 @@
if (!writer->Append(&write_buf[0], write_size)) {
return kIoError;
} else if (compute_crc) {
- crc = crc32(crc, &write_buf[0], write_size);
+ DCHECK_LE(write_size, kBufSize);
+ crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
}
zstream.next_out = &write_buf[0];
@@ -1076,17 +1034,17 @@
const uint32_t length = entry->uncompressed_length;
uint32_t count = 0;
- uint64_t crc = 0;
+ uLong crc = 0;
while (count < length) {
uint32_t remaining = length - count;
off64_t offset = entry->offset + count;
// Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
- const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
// Make sure to read at offset to ensure concurrent access to the fd.
if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
- ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s",
+ ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
block_size, static_cast<int64_t>(offset), strerror(errno));
return kIoError;
}
@@ -1163,12 +1121,6 @@
return archive->mapped_zip.GetFileDescriptor();
}
-ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
- size_t len = strlen(entry_name);
- CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
- name_length = static_cast<uint16_t>(len);
-}
-
#if !defined(_WIN32)
class ProcessWriter : public zip_archive::Writer {
public:
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 46aa5a6..09d3b8a 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -55,10 +55,10 @@
// In order to walk through all file names in the archive, look for a name
// that does not exist in the archive.
- ZipString name("thisFileNameDoesNotExist");
+ std::string_view name("thisFileNameDoesNotExist");
// Start the benchmark.
- while (state.KeepRunning()) {
+ for (auto _ : state) {
OpenArchive(temp_file->path, &handle);
FindEntry(handle, name, &data);
CloseArchive(handle);
@@ -71,11 +71,11 @@
ZipArchiveHandle handle;
void* iteration_cookie;
ZipEntry data;
- ZipString name;
+ std::string name;
- while (state.KeepRunning()) {
+ for (auto _ : state) {
OpenArchive(temp_file->path, &handle);
- StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+ StartIteration(handle, &iteration_cookie);
while (Next(iteration_cookie, &data, &name) == 0) {
}
EndIteration(iteration_cookie);
@@ -84,4 +84,27 @@
}
BENCHMARK(Iterate_all_files);
+static void StartAlignedEntry(benchmark::State& state) {
+ TemporaryFile file;
+ FILE* fp = fdopen(file.fd, "w");
+
+ ZipWriter writer(fp);
+
+ auto alignment = uint32_t(state.range(0));
+ std::string name = "name";
+ int counter = 0;
+ for (auto _ : state) {
+ writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
+ state.PauseTiming();
+ writer.WriteBytes("hola", 4);
+ writer.FinishEntry();
+ state.ResumeTiming();
+ }
+
+ writer.Finish();
+ fclose(fp);
+}
+BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
+
+
BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 330a02a..30a1d72 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -137,22 +137,22 @@
};
/**
- * More space efficient string representation of strings in an mmaped zipped file than
- * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
- * space. ZipString stores a pointer to a string (on 64 bit, 8 bytes) and the length to read from
- * that pointer, 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting 6 bytes.
- * ZipStringOffset stores a 4 byte offset from a fixed location in the memory mapped file instead
- * of the entire address, consuming 8 bytes with alignment.
+ * More space efficient string representation of strings in an mmaped zipped
+ * file than std::string_view. Using std::string_view as an entry in the
+ * ZipArchive hash table wastes space. std::string_view stores a pointer to a
+ * string (on 64 bit, 8 bytes) and the length to read from that pointer,
+ * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
+ * 6 bytes.
+ *
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
+ * mapped file instead of the entire address, consuming 8 bytes with alignment.
*/
struct ZipStringOffset {
uint32_t name_offset;
uint16_t name_length;
- const ZipString GetZipString(const uint8_t* start) const {
- ZipString zip_string;
- zip_string.name = start + name_offset;
- zip_string.name_length = name_length;
- return zip_string;
+ const std::string_view ToStringView(const uint8_t* start) const {
+ return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
}
};
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 9ec89b1..1ec95b6 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -27,6 +27,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <log/log.h>
#include <ziparchive/zip_archive.h>
@@ -77,6 +78,12 @@
}
const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
+ // Simple sanity check. The vector should *only* be handled by this code. A caller
+ // should not const-cast and modify the capacity. This may invalidate next_out.
+ //
+ // Note: it would be better to store the results of data() across Read calls.
+ CHECK_EQ(data_.capacity(), kBufSize);
+
if (length_ == 0) {
return nullptr;
}
@@ -97,7 +104,8 @@
if (bytes < data_.size()) {
data_.resize(bytes);
}
- computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
length_ -= bytes;
offset_ += bytes;
return &data_;
@@ -192,9 +200,15 @@
}
const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
+ // Simple sanity check. The vector should *only* be handled by this code. A caller
+ // should not const-cast and modify the capacity. This may invalidate next_out.
+ //
+ // Note: it would be better to store the results of data() across Read calls.
+ CHECK_EQ(out_.capacity(), kBufSize);
+
if (z_stream_.avail_out == 0) {
z_stream_.next_out = out_.data();
- z_stream_.avail_out = out_.size();
+ z_stream_.avail_out = static_cast<uint32_t>(out_.size());
;
}
@@ -203,7 +217,9 @@
if (compressed_length_ == 0) {
return nullptr;
}
- size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
+ DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k.
+ uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
+ : compressed_length_;
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
errno = 0;
if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
@@ -230,14 +246,16 @@
if (z_stream_.avail_out == 0) {
uncompressed_length_ -= out_.size();
- computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
return &out_;
}
if (zerr == Z_STREAM_END) {
if (z_stream_.avail_out != 0) {
// Resize the vector down to the actual size of the data.
out_.resize(out_.size() - z_stream_.avail_out);
- computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
uncompressed_length_ -= out_.size();
return &out_;
}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index cea42d4..8781ab7 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -27,6 +27,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/mapped_file.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
@@ -63,11 +64,6 @@
return OpenArchive(abs_path.c_str(), handle);
}
-static void SetZipString(ZipString* zip_str, const std::string& str) {
- zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
- zip_str->name_length = str.size();
-}
-
TEST(ziparchive, Open) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -111,7 +107,27 @@
close(fd);
}
-static void AssertIterationOrder(const ZipString* prefix, const ZipString* suffix,
+TEST(ziparchive, Iteration_std_string_view) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
+
+ ZipEntry data;
+ std::vector<std::string_view> names;
+ std::string_view name;
+ while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
+
+ // Assert that the names are as expected.
+ std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
+ std::sort(names.begin(), names.end());
+ ASSERT_EQ(expected_names, names);
+
+ CloseArchive(handle);
+}
+
+static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
const std::vector<std::string>& expected_names_sorted) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -122,10 +138,10 @@
ZipEntry data;
std::vector<std::string> names;
- ZipString name;
+ std::string name;
for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
- names.push_back(std::string(reinterpret_cast<const char*>(name.name), name.name_length));
+ names.push_back(name);
}
// End of iteration.
@@ -141,30 +157,26 @@
static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
"b/d.txt"};
- AssertIterationOrder(nullptr, nullptr, kExpectedMatchesSorted);
+ AssertIterationOrder("", "", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithPrefix) {
- ZipString prefix("b/");
static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
- AssertIterationOrder(&prefix, nullptr, kExpectedMatchesSorted);
+ AssertIterationOrder("b/", "", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithSuffix) {
- ZipString suffix(".txt");
static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
"b/d.txt"};
- AssertIterationOrder(nullptr, &suffix, kExpectedMatchesSorted);
+ AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithPrefixAndSuffix) {
- ZipString prefix("b");
- ZipString suffix(".txt");
static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
- AssertIterationOrder(&prefix, &suffix, kExpectedMatchesSorted);
+ AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
@@ -172,12 +184,10 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
void* iteration_cookie;
- ZipString prefix("x");
- ZipString suffix("y");
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
ZipEntry data;
- ZipString name;
+ std::string name;
// End of iteration.
ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -190,9 +200,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry data;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
// Known facts about a.txt, from zipinfo -v.
ASSERT_EQ(63, data.offset);
@@ -203,9 +211,28 @@
ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
// An entry that doesn't exist. Should be a negative return code.
- ZipString absent_name;
- SetZipString(&absent_name, kNonexistentTxtName);
- ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
+ ASSERT_LT(FindEntry(handle, kNonexistentTxtName, &data), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_empty) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_too_long) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ std::string very_long_name(65536, 'x');
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
CloseArchive(handle);
}
@@ -215,9 +242,9 @@
ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
- ZipString name;
+ std::string name;
ZipEntry data;
ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -232,9 +259,7 @@
// An entry that's deflated.
ZipEntry data;
- ZipString a_name;
- SetZipString(&a_name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, a_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
const uint32_t a_size = data.uncompressed_length;
ASSERT_EQ(a_size, kATxtContents.size());
uint8_t* buffer = new uint8_t[a_size];
@@ -243,9 +268,7 @@
delete[] buffer;
// An entry that's stored.
- ZipString b_name;
- SetZipString(&b_name, kBTxtName);
- ASSERT_EQ(0, FindEntry(handle, b_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
const uint32_t b_size = data.uncompressed_length;
ASSERT_EQ(b_size, kBTxtContents.size());
buffer = new uint8_t[b_size];
@@ -300,9 +323,7 @@
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
ZipEntry entry;
- ZipString empty_name;
- SetZipString(&empty_name, kEmptyTxtName);
- ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -325,14 +346,12 @@
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
ZipEntry entry;
- ZipString ab_name;
- SetZipString(&ab_name, kAbTxtName);
- ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
// Extract the entry to memory.
std::vector<uint8_t> buffer(kAbUncompressedSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
// Extract the entry to a file.
TemporaryFile tmp_output_file;
@@ -384,9 +403,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry entry;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
// Assert that the first 8 bytes of the file haven't been clobbered.
@@ -415,16 +432,15 @@
ASSERT_EQ(0, fstat(fd, &sb));
// Memory map the file first and open the archive from the memory region.
- auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)};
+ auto file_map{
+ android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
ZipArchiveHandle handle;
ASSERT_EQ(0,
OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
// Assert one entry can be found and extracted correctly.
- std::string BINARY_PATH("META-INF/com/google/android/update-binary");
- ZipString binary_path(BINARY_PATH.c_str());
ZipEntry binary_entry;
- ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+ ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
TemporaryFile tmp_binary;
ASSERT_NE(-1, tmp_binary.fd);
ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
@@ -433,9 +449,7 @@
static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
- ZipString name;
- SetZipString(&name, entry_name);
- ASSERT_EQ(0, FindEntry(handle, name, entry));
+ ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
std::unique_ptr<ZipArchiveStreamEntry> stream;
if (raw) {
stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
@@ -488,7 +502,8 @@
std::vector<uint8_t> cmp_data(entry.uncompressed_length);
ASSERT_EQ(entry.uncompressed_length, read_data.size());
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
+ ASSERT_EQ(
+ 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
CloseArchive(handle);
@@ -585,11 +600,7 @@
// an entry whose name is "name" and whose size is 12 (contents =
// "abdcdefghijk").
ZipEntry entry;
- ZipString name;
- std::string name_str = "name";
- SetZipString(&name, name_str);
-
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, "name", &entry));
ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
entry_out->resize(12);
@@ -737,8 +748,8 @@
};
TEST(ziparchive, Inflate) {
- const uint32_t compressed_length = kATxtContentsCompressed.size();
- const uint32_t uncompressed_length = kATxtContents.size();
+ const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
+ const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
const VectorReader reader(kATxtContentsCompressed);
{
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 0df0fa5..198154b 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -130,7 +130,7 @@
return error_code;
}
-int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
+int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
uint32_t alignment = 0;
if (flags & kAlign32) {
flags &= ~kAlign32;
@@ -139,11 +139,11 @@
return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
}
-int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
}
-int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
uint32_t alignment = 0;
if (flags & kAlign32) {
flags &= ~kAlign32;
@@ -169,8 +169,8 @@
year = 80;
}
- *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
- *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+ *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
+ *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
}
static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
@@ -193,16 +193,22 @@
dst->compression_method = src.compression_method;
dst->last_mod_time = src.last_mod_time;
dst->last_mod_date = src.last_mod_date;
- dst->file_name_length = src.path.size();
+ DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
+ dst->file_name_length = static_cast<uint16_t>(src.path.size());
dst->extra_field_length = src.padding_length;
}
-int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
uint32_t alignment) {
if (state_ != State::kWritingZip) {
return kInvalidState;
}
+ // Can only have 16535 entries because of zip records.
+ if (files_.size() == std::numeric_limits<uint16_t>::max()) {
+ return HandleError(kIoError);
+ }
+
if (flags & kAlign32) {
return kInvalidAlign32Flag;
}
@@ -210,10 +216,17 @@
if (powerof2(alignment) == 0) {
return kInvalidAlignment;
}
+ if (alignment > std::numeric_limits<uint16_t>::max()) {
+ return kInvalidAlignment;
+ }
FileEntry file_entry = {};
file_entry.local_file_header_offset = current_offset_;
file_entry.path = path;
+ // No support for larger than 4GB files.
+ if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
file_entry.path.size())) {
@@ -234,13 +247,24 @@
ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
- std::vector<char> zero_padding;
+ // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
+ static constexpr auto kPageSize = 4096;
+ static constexpr char kSmallZeroPadding[kPageSize] = {};
+ // use this buffer if our preallocated one is too small
+ std::vector<char> zero_padding_big;
+ const char* zero_padding = nullptr;
+
if (alignment != 0 && (offset & (alignment - 1))) {
// Pad the extra field so the data will be aligned.
- uint16_t padding = alignment - (offset % alignment);
+ uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
file_entry.padding_length = padding;
offset += padding;
- zero_padding.resize(padding, 0);
+ if (padding <= std::size(kSmallZeroPadding)) {
+ zero_padding = kSmallZeroPadding;
+ } else {
+ zero_padding_big.resize(padding, 0);
+ zero_padding = zero_padding_big.data();
+ }
}
LocalFileHeader header = {};
@@ -252,11 +276,11 @@
return HandleError(kIoError);
}
- if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
+ if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
return HandleError(kIoError);
}
- if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+ if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
file_) != file_entry.padding_length) {
return HandleError(kIoError);
}
@@ -314,7 +338,8 @@
}
z_stream_->next_out = buffer_.data();
- z_stream_->avail_out = buffer_.size();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
return kNoError;
}
@@ -322,25 +347,31 @@
if (state_ != State::kWritingEntry) {
return HandleError(kInvalidState);
}
+ // Need to be able to mark down data correctly.
+ if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) >
+ std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
+ uint32_t len32 = static_cast<uint32_t>(len);
int32_t result = kNoError;
if (current_file_entry_.compression_method & kCompressDeflated) {
- result = CompressBytes(¤t_file_entry_, data, len);
+ result = CompressBytes(¤t_file_entry_, data, len32);
} else {
- result = StoreBytes(¤t_file_entry_, data, len);
+ result = StoreBytes(¤t_file_entry_, data, len32);
}
if (result != kNoError) {
return result;
}
- current_file_entry_.crc32 =
- crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
- current_file_entry_.uncompressed_size += len;
+ current_file_entry_.crc32 = static_cast<uint32_t>(
+ crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32));
+ current_file_entry_.uncompressed_size += len32;
return kNoError;
}
-int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) {
+int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
CHECK(state_ == State::kWritingEntry);
if (fwrite(data, 1, len, file_) != len) {
@@ -351,7 +382,7 @@
return kNoError;
}
-int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) {
+int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
CHECK(state_ == State::kWritingEntry);
CHECK(z_stream_);
CHECK(z_stream_->next_out != nullptr);
@@ -379,7 +410,8 @@
// Reset the output buffer for the next input.
z_stream_->next_out = buffer_.data();
- z_stream_->avail_out = buffer_.size();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
}
}
return kNoError;
@@ -404,7 +436,8 @@
current_offset_ += write_bytes;
z_stream_->next_out = buffer_.data();
- z_stream_->avail_out = buffer_.size();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
}
if (zerr != Z_STREAM_END) {
return HandleError(kZlibError);
@@ -491,7 +524,11 @@
cdr.crc32 = file.crc32;
cdr.compressed_size = file.compressed_size;
cdr.uncompressed_size = file.uncompressed_size;
- cdr.file_name_length = file.path.size();
+ // Checked in IsValidEntryName.
+ DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max());
+ cdr.file_name_length = static_cast<uint16_t>(file.path.size());
+ // Checked in StartAlignedEntryWithTime.
+ DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max());
cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
return HandleError(kIoError);
@@ -508,10 +545,15 @@
er.eocd_signature = EocdRecord::kSignature;
er.disk_num = 0;
er.cd_start_disk = 0;
- er.num_records_on_disk = files_.size();
- er.num_records = files_.size();
- er.cd_size = current_offset_ - startOfCdr;
- er.cd_start_offset = startOfCdr;
+ // Checked when adding entries.
+ DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max());
+ er.num_records_on_disk = static_cast<uint16_t>(files_.size());
+ er.num_records = static_cast<uint16_t>(files_.size());
+ if (current_offset_ > std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
+ er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr);
+ er.cd_start_offset = static_cast<uint32_t>(startOfCdr);
if (fwrite(&er, sizeof(er), 1, file_) != 1) {
return HandleError(kIoError);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index c284273..c3da23c 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -62,7 +62,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.has_data_descriptor);
EXPECT_EQ(strlen(expected), data.compressed_length);
@@ -95,19 +95,19 @@
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(2u, data.compressed_length);
ASSERT_EQ(2u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(3u, data.compressed_length);
ASSERT_EQ(3u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.compressed_length);
EXPECT_EQ(0u, data.uncompressed_length);
@@ -129,7 +129,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
CloseArchive(handle);
@@ -163,7 +163,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
struct tm mod = data.GetModificationTime();
@@ -191,7 +191,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
CloseArchive(handle);
@@ -213,7 +213,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
struct tm mod = data.GetModificationTime();
@@ -241,7 +241,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
ASSERT_EQ(4u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
@@ -257,7 +257,7 @@
std::vector<uint8_t> buffer(kBufSize);
size_t prev = 1;
for (size_t i = 0; i < kBufSize; i++) {
- buffer[i] = i + prev;
+ buffer[i] = static_cast<uint8_t>(i + prev);
prev = i;
}
@@ -273,13 +273,14 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
EXPECT_EQ(kBufSize, data.uncompressed_length);
std::vector<uint8_t> decompress(kBufSize);
memset(decompress.data(), 0, kBufSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
+ static_cast<uint32_t>(decompress.size())));
EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
<< "Input buffer and output buffer are different.";
@@ -339,12 +340,12 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
- ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+ ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
CloseArchive(handle);
@@ -391,7 +392,7 @@
actual.resize(expected.size());
uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
- if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) {
+ if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
return ::testing::AssertionFailure() << "failed to extract entry";
}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index c2ee061..521f92e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -189,8 +189,8 @@
/* vmpressure event handler data */
static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
-/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
-#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket, 1 lmk events */
+#define MAX_EPOLL_EVENTS (2 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
static int epollfd;
static int maxevents;
@@ -1863,6 +1863,74 @@
return false;
}
+#ifdef LMKD_LOG_STATS
+static int kernel_poll_fd = -1;
+
+static void poll_kernel() {
+ if (kernel_poll_fd == -1) {
+ // not waiting
+ return;
+ }
+
+ while (1) {
+ char rd_buf[256];
+ int bytes_read =
+ TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
+ if (bytes_read <= 0) break;
+ rd_buf[bytes_read] = '\0';
+
+ int64_t pid;
+ int64_t uid;
+ int64_t group_leader_pid;
+ int64_t min_flt;
+ int64_t maj_flt;
+ int64_t rss_in_pages;
+ int16_t oom_score_adj;
+ int16_t min_score_adj;
+ int64_t starttime;
+ char* taskname = 0;
+ int fields_read = sscanf(rd_buf,
+ "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+ " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
+ &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
+ &oom_score_adj, &min_score_adj, &starttime, &taskname);
+
+ /* only the death of the group leader process is logged */
+ if (fields_read == 10 && group_leader_pid == pid) {
+ int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+ stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, oom_score_adj,
+ min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
+ process_start_time_ns, min_score_adj);
+ }
+
+ free(taskname);
+ }
+}
+
+static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
+
+static void init_poll_kernel() {
+ struct epoll_event epev;
+ kernel_poll_fd =
+ TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+
+ if (kernel_poll_fd < 0) {
+ ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
+ return;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void*)&kernel_poll_hinfo;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
+ ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
+ close(kernel_poll_fd);
+ kernel_poll_fd = -1;
+ } else {
+ maxevents++;
+ }
+}
+#endif
+
static int init(void) {
struct epoll_event epev;
int i;
@@ -1910,6 +1978,11 @@
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
+#ifdef LMKD_LOG_STATS
+ if (enable_stats_log) {
+ init_poll_kernel();
+ }
+#endif
} else {
/* Try to use psi monitor first if kernel has it */
use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 07040b0..25104eb 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -4,10 +4,15 @@
# Make sure any property changes are only performed with /data mounted, after
# post-fs-data state because otherwise behavior is undefined. The exceptions
# are device adjustments for logcatd service properties (persist.* overrides
-# notwithstanding) for logd.logpersistd.size and logd.logpersistd.buffer.
+# notwithstanding) for logd.logpersistd.size logd.logpersistd.rotate_kbytes and
+# logd.logpersistd.buffer.
# persist to non-persistent trampolines to permit device properties can be
# overridden when /data mounts, or during runtime.
+on property:persist.logd.logpersistd.count=*
+ # expect /init to report failure if property empty (default)
+ setprop persist.logd.logpersistd.size ${persist.logd.logpersistd.count}
+
on property:persist.logd.logpersistd.size=256
setprop persist.logd.logpersistd.size ""
setprop logd.logpersistd.size ""
@@ -16,6 +21,14 @@
# expect /init to report failure if property empty (default)
setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
+on property:persist.logd.logpersistd.rotate_kbytes=1024
+ setprop persist.logd.logpersistd.rotate_kbytes ""
+ setprop logd.logpersistd.rotate_kbytes ""
+
+on property:persist.logd.logpersistd.rotate_kbytes=*
+ # expect /init to report failure if property empty (default)
+ setprop logd.logpersistd.rotate_kbytes ${persist.logd.logpersistd.rotate_kbytes}
+
on property:persist.logd.logpersistd.buffer=all
setprop persist.logd.logpersistd.buffer ""
setprop logd.logpersistd.buffer ""
@@ -54,7 +67,7 @@
stop logcatd
# logcatd service
-service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-1024} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
class late_start
disabled
# logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index a21555c..5a375ec 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -229,70 +229,17 @@
static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
static const char newline[] = "\n";
- // Dedupe messages, checking for identical messages starting with avc:
- static unsigned count;
- static char* last_str;
- static bool last_info;
+ auditParse(str, uid, &denial_metadata);
+ iov[0].iov_base = info ? const_cast<char*>(log_info) : const_cast<char*>(log_warning);
+ iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
+ iov[1].iov_base = str;
+ iov[1].iov_len = strlen(str);
+ iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+ iov[2].iov_len = denial_metadata.length();
+ iov[3].iov_base = const_cast<char*>(newline);
+ iov[3].iov_len = strlen(newline);
- if (last_str != nullptr) {
- static const char avc[] = "): avc: ";
- char* avcl = strstr(last_str, avc);
- bool skip = false;
-
- if (avcl) {
- char* avcr = strstr(str, avc);
-
- skip = avcr &&
- !fastcmp<strcmp>(avcl + strlen(avc), avcr + strlen(avc));
- if (skip) {
- ++count;
- free(last_str);
- last_str = strdup(str);
- last_info = info;
- }
- }
- if (!skip) {
- static const char resume[] = " duplicate messages suppressed\n";
- iov[0].iov_base = last_info ? const_cast<char*>(log_info)
- : const_cast<char*>(log_warning);
- iov[0].iov_len =
- last_info ? sizeof(log_info) : sizeof(log_warning);
- iov[1].iov_base = last_str;
- iov[1].iov_len = strlen(last_str);
- iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
- iov[2].iov_len = denial_metadata.length();
- if (count > 1) {
- iov[3].iov_base = const_cast<char*>(resume);
- iov[3].iov_len = strlen(resume);
- } else {
- iov[3].iov_base = const_cast<char*>(newline);
- iov[3].iov_len = strlen(newline);
- }
-
- writev(fdDmesg, iov, arraysize(iov));
- free(last_str);
- last_str = nullptr;
- }
- }
- if (last_str == nullptr) {
- count = 0;
- last_str = strdup(str);
- last_info = info;
- }
- if (count == 0) {
- auditParse(str, uid, &denial_metadata);
- iov[0].iov_base = info ? const_cast<char*>(log_info)
- : const_cast<char*>(log_warning);
- iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
- iov[1].iov_base = str;
- iov[1].iov_len = strlen(str);
- iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
- iov[2].iov_len = denial_metadata.length();
- iov[3].iov_base = const_cast<char*>(newline);
- iov[3].iov_len = strlen(newline);
-
- writev(fdDmesg, iov, arraysize(iov));
- }
+ writev(fdDmesg, iov, arraysize(iov));
}
if (!main && !events) {
diff --git a/logd/README.property b/logd/README.property
index da5f96f..d2a2cbb 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -17,10 +17,13 @@
Responds to logcatd, clear and stop.
logd.logpersistd.buffer persist logpersistd buffers to collect
logd.logpersistd.size persist logpersistd size in MB
+logd.logpersistd.rotate_kbytes persist logpersistd outout file size in KB.
persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context.
persist.logd.logpersistd.buffer all logpersistd buffers to collect
persist.logd.logpersistd.size 256 logpersistd size in MB
+persist.logd.logpersistd.count 256 sets max number of rotated logs to <count>.
+persist.logd.logpersistd.rotate_kbytes 1024 logpersistd output file size in KB
persist.logd.size number ro Global default size of the buffer for
all log ids at initial startup, at
runtime use: logcat -b all -G <value>
diff --git a/logd/logd.rc b/logd/logd.rc
index 438419a..530f342 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,7 +6,8 @@
file /dev/kmsg w
user logd
group logd system package_info readproc
- capabilities SYSLOG AUDIT_CONTROL SETGID
+ capabilities SYSLOG AUDIT_CONTROL
+ priority 10
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index fd3cdf8..23bbf86 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -17,6 +17,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/capability.h>
#include <poll.h>
#include <sched.h>
#include <semaphore.h>
@@ -57,35 +58,10 @@
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
-//
-// The service is designed to be run by init, it does not respond well
-// to starting up manually. When starting up manually the sockets will
-// fail to open typically for one of the following reasons:
-// EADDRINUSE if logger is running.
-// EACCESS if started without precautions (below)
-//
-// Here is a cookbook procedure for starting up logd manually assuming
-// init is out of the way, pedantically all permissions and SELinux
-// security is put back in place:
-//
-// setenforce 0
-// rm /dev/socket/logd*
-// chmod 777 /dev/socket
-// # here is where you would attach the debugger or valgrind for example
-// runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 &
-// sleep 1
-// chmod 755 /dev/socket
-// chown logd.logd /dev/socket/logd*
-// restorecon /dev/socket/logd*
-// setenforce 1
-//
-// If minimalism prevails, typical for debugging and security is not a concern:
-//
-// setenforce 0
-// chmod 777 /dev/socket
-// logd
-//
-
+// The service is designed to be run by init, it does not respond well to starting up manually. Init
+// has a 'sigstop' feature that sends SIGSTOP to a service immediately before calling exec(). This
+// allows debuggers, etc to be attached to logd at the very beginning, while still having init
+// handle the user, groups, capabilities, files, etc setup.
static int drop_privs(bool klogd, bool auditd) {
sched_param param = {};
@@ -99,11 +75,6 @@
return -1;
}
- if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
- android::prdebug("failed to set background cgroup");
- return -1;
- }
-
if (!__android_logger_property_get_bool("ro.debuggable",
BOOL_DEFAULT_FALSE) &&
prctl(PR_SET_DUMPABLE, 0) == -1) {
@@ -111,52 +82,26 @@
return -1;
}
- std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
- cap_free);
- if (cap_clear(caps.get()) < 0) return -1;
- cap_value_t cap_value[] = { CAP_SETGID, // must be first for below
- klogd ? CAP_SYSLOG : CAP_SETGID,
- auditd ? CAP_AUDIT_CONTROL : CAP_SETGID };
- if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(cap_value), cap_value,
- CAP_SET) < 0) {
+ std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
+ if (cap_clear(caps.get()) < 0) {
return -1;
}
- if (cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(cap_value), cap_value,
- CAP_SET) < 0) {
+ std::vector<cap_value_t> cap_value;
+ if (klogd) {
+ cap_value.emplace_back(CAP_SYSLOG);
+ }
+ if (auditd) {
+ cap_value.emplace_back(CAP_AUDIT_CONTROL);
+ }
+
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
+ return -1;
+ }
+ if (cap_set_flag(caps.get(), CAP_EFFECTIVE, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
return -1;
}
if (cap_set_proc(caps.get()) < 0) {
- android::prdebug(
- "failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
- errno);
- return -1;
- }
-
- gid_t groups[] = { AID_READPROC };
-
- if (setgroups(arraysize(groups), groups) == -1) {
- android::prdebug("failed to set AID_READPROC groups");
- return -1;
- }
-
- if (setgid(AID_LOGD) != 0) {
- android::prdebug("failed to set AID_LOGD gid");
- return -1;
- }
-
- if (setuid(AID_LOGD) != 0) {
- android::prdebug("failed to set AID_LOGD uid");
- return -1;
- }
-
- if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
- return -1;
- }
- if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR) < 0) {
- return -1;
- }
- if (cap_set_proc(caps.get()) < 0) {
- android::prdebug("failed to clear CAP_SETGID (%d)", errno);
+ android::prdebug("failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
return -1;
}
@@ -205,67 +150,14 @@
}
}
-static sem_t uidName;
-static uid_t uid;
-static char* name;
-
static sem_t reinit;
static bool reinit_running = false;
static LogBuffer* logBuf = nullptr;
-static bool package_list_parser_cb(pkg_info* info, void* /* userdata */) {
- bool rc = true;
- if (info->uid == uid) {
- name = strdup(info->name);
- // false to stop processing
- rc = false;
- }
-
- packagelist_free(info);
- return rc;
-}
-
static void* reinit_thread_start(void* /*obj*/) {
prctl(PR_SET_NAME, "logd.daemon");
- set_sched_policy(0, SP_BACKGROUND);
- setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
-
- // We should drop to AID_LOGD, if we are anything else, we have
- // even lesser privileges and accept our fate.
- gid_t groups[] = {
- AID_SYSTEM, // search access to /data/system path
- AID_PACKAGE_INFO, // readonly access to /data/system/packages.list
- };
- if (setgroups(arraysize(groups), groups) == -1) {
- android::prdebug(
- "logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
- }
- if (setgid(AID_LOGD) != 0) {
- android::prdebug("logd.daemon: failed to set AID_LOGD gid");
- }
- if (setuid(AID_LOGD) != 0) {
- android::prdebug("logd.daemon: failed to set AID_LOGD uid");
- }
-
- cap_t caps = cap_init();
- (void)cap_clear(caps);
- (void)cap_set_proc(caps);
- (void)cap_free(caps);
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
- // uidToName Privileged Worker
- if (uid) {
- name = nullptr;
-
- // if we got the perms wrong above, this would spam if we reported
- // problems with acquisition of an uid name from the packages.
- (void)packagelist_parse(package_list_parser_cb, nullptr);
-
- uid = 0;
- sem_post(&uidName);
- continue;
- }
-
if (fdDmesg >= 0) {
static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
'l',
@@ -302,26 +194,30 @@
return nullptr;
}
-static sem_t sem_name;
-
char* android::uidToName(uid_t u) {
- if (!u || !reinit_running) {
- return nullptr;
- }
+ struct Userdata {
+ uid_t uid;
+ char* name;
+ } userdata = {
+ .uid = u,
+ .name = nullptr,
+ };
- sem_wait(&sem_name);
+ packagelist_parse(
+ [](pkg_info* info, void* callback_parameter) {
+ auto userdata = reinterpret_cast<Userdata*>(callback_parameter);
+ bool result = true;
+ if (info->uid == userdata->uid) {
+ userdata->name = strdup(info->name);
+ // false to stop processing
+ result = false;
+ }
+ packagelist_free(info);
+ return result;
+ },
+ &userdata);
- // Not multi-thread safe, we use sem_name to protect
- uid = u;
-
- name = nullptr;
- sem_post(&reinit);
- sem_wait(&uidName);
- char* ret = name;
-
- sem_post(&sem_name);
-
- return ret;
+ return userdata.name;
}
// Serves as a global method to trigger reinitialization
@@ -373,11 +269,6 @@
}
static int issueReinit() {
- cap_t caps = cap_init();
- (void)cap_clear(caps);
- (void)cap_set_proc(caps);
- (void)cap_free(caps);
-
int sock = TEMP_FAILURE_RETRY(socket_local_client(
"logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
if (sock < 0) return -errno;
@@ -440,10 +331,13 @@
if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
}
+ bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+ if (drop_privs(klogd, auditd) != 0) {
+ return EXIT_FAILURE;
+ }
+
// Reinit Thread
sem_init(&reinit, 0, 0);
- sem_init(&uidName, 0, 0);
- sem_init(&sem_name, 0, 1);
pthread_attr_t attr;
if (!pthread_attr_init(&attr)) {
struct sched_param param;
@@ -461,12 +355,6 @@
pthread_attr_destroy(&attr);
}
- bool auditd =
- __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
- if (drop_privs(klogd, auditd) != 0) {
- return EXIT_FAILURE;
- }
-
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
// entries.
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
index 83a194f..d39da8a 100644
--- a/logd/tests/Android.bp
+++ b/logd/tests/Android.bp
@@ -35,12 +35,12 @@
srcs: ["logd_test.cpp"],
- shared_libs: [
+ static_libs: [
"libbase",
"libcutils",
"libselinux",
+ "liblog",
],
- static_libs: ["liblog"],
}
// Build tests for the logger. Run with:
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 447b067..b6c33d7 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -952,7 +952,7 @@
void __android_log_btwrite_multiple__helper(int count) {
#ifdef __ANDROID__
log_time ts(CLOCK_MONOTONIC);
-
+ usleep(100);
log_time ts1(CLOCK_MONOTONIC);
// We fork to create a unique pid for the submitted log messages
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
deleted file mode 100644
index c3cf746..0000000
--- a/mkbootimg/Android.bp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2012 The Android Open Source Project
-
-cc_library_headers {
- name: "libmkbootimg_abi_headers",
- vendor_available: true,
- export_include_dirs: ["include"],
-}
-
-cc_library_headers {
- name: "bootimg_headers",
- vendor_available: true,
- recovery_available: true,
- export_include_dirs: ["include/bootimg"],
- host_supported: true,
- target: {
- windows: {
- enabled: true,
- },
- },
-}
-
-cc_library {
- name: "libmkbootimg_abi_check",
- vendor_available: true,
- vndk: {
- enabled: true,
- },
- srcs: [
- "mkbootimg_dummy.cpp",
- ],
- header_libs: ["libmkbootimg_abi_headers"],
- export_header_lib_headers: ["libmkbootimg_abi_headers"],
-}
-
-python_defaults {
- name: "mkbootimg_defaults",
-
- version: {
- py2: {
- enabled: true,
- embedded_launcher: true,
- },
- py3: {
- enabled: false,
- embedded_launcher: false,
- },
- },
-}
-
-python_binary_host {
- name: "mkbootimg",
- defaults: ["mkbootimg_defaults"],
- srcs: [
- "mkbootimg.py",
- ],
-}
-
-python_binary_host {
- name: "unpack_bootimg",
- defaults: ["mkbootimg_defaults"],
- srcs: [
- "unpack_bootimg.py",
- ],
-}
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
deleted file mode 100644
index 3a00860..0000000
--- a/mkbootimg/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-hridya@google.com
-tbao@google.com
diff --git a/mkbootimg/include/abi_check/mkbootimg_abi_check.h b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
deleted file mode 100644
index d478aba..0000000
--- a/mkbootimg/include/abi_check/mkbootimg_abi_check.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <bootimg/bootimg.h>
-
-// This header has been created for the following reaons:
-// 1) In order for a change in a user defined type to be classified as API /
-// ABI breaking, it needs to be referenced by an 'exported interface'
-// (in this case the function mkbootimg_dummy).
-// 2) Since 'mkbootimg_dummy' needs to be exported, we need to have it
-// exposed through a public header.
-// 3) It is desirable not to pollute bootimg.h with interfaces which are not
-// 'used' in reality by on device binaries. Furthermore, bootimg.h might
-// be exported by a library in the future, so we must avoid polluting it.
-void mkbootimg_dummy(boot_img_hdr*);
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
deleted file mode 100644
index 9ee7869..0000000
--- a/mkbootimg/include/bootimg/bootimg.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2007 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>
-
-#define BOOT_MAGIC "ANDROID!"
-#define BOOT_MAGIC_SIZE 8
-#define BOOT_NAME_SIZE 16
-#define BOOT_ARGS_SIZE 512
-#define BOOT_EXTRA_ARGS_SIZE 1024
-
-// The bootloader expects the structure of boot_img_hdr with header
-// version 0 to be as follows:
-struct boot_img_hdr_v0 {
- // Must be BOOT_MAGIC.
- uint8_t magic[BOOT_MAGIC_SIZE];
-
- uint32_t kernel_size; /* size in bytes */
- uint32_t kernel_addr; /* physical load addr */
-
- uint32_t ramdisk_size; /* size in bytes */
- uint32_t ramdisk_addr; /* physical load addr */
-
- uint32_t second_size; /* size in bytes */
- uint32_t second_addr; /* physical load addr */
-
- uint32_t tags_addr; /* physical addr for kernel tags */
- uint32_t page_size; /* flash page size we assume */
-
- // Version of the boot image header.
- uint32_t header_version;
-
- // Operating system version and security patch level.
- // For version "A.B.C" and patch level "Y-M-D":
- // (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
- // os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
- uint32_t os_version;
-
-#if __cplusplus
- void SetOsVersion(unsigned major, unsigned minor, unsigned patch) {
- os_version &= ((1 << 11) - 1);
- os_version |= (((major & 0x7f) << 25) | ((minor & 0x7f) << 18) | ((patch & 0x7f) << 11));
- }
-
- void SetOsPatchLevel(unsigned year, unsigned month) {
- os_version &= ~((1 << 11) - 1);
- os_version |= (((year - 2000) & 0x7f) << 4) | ((month & 0xf) << 0);
- }
-#endif
-
- uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
-
- uint8_t cmdline[BOOT_ARGS_SIZE];
-
- uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
-
- // Supplemental command line data; kept here to maintain
- // binary compatibility with older versions of mkbootimg.
- uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
-} __attribute__((packed));
-
-/*
- * It is expected that callers would explicitly specify which version of the
- * boot image header they need to use.
- */
-typedef struct boot_img_hdr_v0 boot_img_hdr;
-
-/* When a boot header is of version 0, the structure of boot image is as
- * follows:
- *
- * +-----------------+
- * | boot header | 1 page
- * +-----------------+
- * | kernel | n pages
- * +-----------------+
- * | ramdisk | m pages
- * +-----------------+
- * | second stage | o pages
- * +-----------------+
- *
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. second is optional (second_size == 0 -> no second)
- * 3. load each element (kernel, ramdisk, second) at
- * the specified physical address (kernel_addr, etc)
- * 4. prepare tags at tag_addr. kernel_args[] is
- * appended to the kernel commandline in the tags.
- * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 6. if second_size != 0: jump to second_addr
- * else: jump to kernel_addr
- */
-
-struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
- uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO/ACPIO image */
- uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
- uint32_t header_size;
-} __attribute__((packed));
-
-/* When the boot image header has a version of 2, the structure of the boot
- * image is as follows:
- *
- * +---------------------+
- * | boot header | 1 page
- * +---------------------+
- * | kernel | n pages
- * +---------------------+
- * | ramdisk | m pages
- * +---------------------+
- * | second stage | o pages
- * +---------------------+
- * | recovery dtbo/acpio | p pages
- * +---------------------+
- * | dtb | q pages
- * +---------------------+
-
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- * p = (recovery_dtbo_size + page_size - 1) / page_size
- * q = (dtb_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel, ramdisk and DTB are required (size != 0)
- * 2. recovery_dtbo/recovery_acpio is required for recovery.img in non-A/B
- * devices(recovery_dtbo_size != 0)
- * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second, dtb) at
- * the specified physical address (kernel_addr, etc)
- * 5. If booting to recovery mode in a non-A/B device, extract recovery
- * dtbo/acpio and apply the correct set of overlays on the base device tree
- * depending on the hardware/product revision.
- * 6. prepare tags at tag_addr. kernel_args[] is
- * appended to the kernel commandline in the tags.
- * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 8. if second_size != 0: jump to second_addr
- * else: jump to kernel_addr
- */
-struct boot_img_hdr_v2 : public boot_img_hdr_v1 {
- uint32_t dtb_size; /* size in bytes for DTB image */
- uint64_t dtb_addr; /* physical load address for DTB image */
-} __attribute__((packed));
diff --git a/mkbootimg/mkbootimg.py b/mkbootimg/mkbootimg.py
deleted file mode 100644
index 92b11a5..0000000
--- a/mkbootimg/mkbootimg.py
+++ /dev/null
@@ -1,232 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015, 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.
-
-from __future__ import print_function
-from sys import argv, exit, stderr
-from argparse import ArgumentParser, FileType, Action
-from os import fstat
-from struct import pack
-from hashlib import sha1
-import sys
-import re
-
-def filesize(f):
- if f is None:
- return 0
- try:
- return fstat(f.fileno()).st_size
- except OSError:
- return 0
-
-
-def update_sha(sha, f):
- if f:
- sha.update(f.read())
- f.seek(0)
- sha.update(pack('I', filesize(f)))
- else:
- sha.update(pack('I', 0))
-
-
-def pad_file(f, padding):
- pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
- f.write(pack(str(pad) + 'x'))
-
-
-def get_number_of_pages(image_size, page_size):
- """calculates the number of pages required for the image"""
- return (image_size + page_size - 1) / page_size
-
-
-def get_recovery_dtbo_offset(args):
- """calculates the offset of recovery_dtbo image in the boot image"""
- num_header_pages = 1 # header occupies a page
- num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
- num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
- num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
- dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
- num_ramdisk_pages + num_second_pages)
- return dtbo_offset
-
-
-def write_header(args):
- BOOT_IMAGE_HEADER_V1_SIZE = 1648
- BOOT_IMAGE_HEADER_V2_SIZE = 1660
- BOOT_MAGIC = 'ANDROID!'.encode()
-
- if (args.header_version > 2):
- raise ValueError('Boot header version %d not supported' % args.header_version)
-
- args.output.write(pack('8s', BOOT_MAGIC))
- args.output.write(pack('10I',
- filesize(args.kernel), # size in bytes
- args.base + args.kernel_offset, # physical load addr
- filesize(args.ramdisk), # size in bytes
- args.base + args.ramdisk_offset, # physical load addr
- filesize(args.second), # size in bytes
- args.base + args.second_offset, # physical load addr
- args.base + args.tags_offset, # physical addr for kernel tags
- args.pagesize, # flash page size we assume
- args.header_version, # version of bootimage header
- (args.os_version << 11) | args.os_patch_level)) # os version and patch level
- args.output.write(pack('16s', args.board.encode())) # asciiz product name
- args.output.write(pack('512s', args.cmdline[:512].encode()))
-
- sha = sha1()
- update_sha(sha, args.kernel)
- update_sha(sha, args.ramdisk)
- update_sha(sha, args.second)
-
- if args.header_version > 0:
- update_sha(sha, args.recovery_dtbo)
- if args.header_version > 1:
- update_sha(sha, args.dtb)
-
- img_id = pack('32s', sha.digest())
-
- args.output.write(img_id)
- args.output.write(pack('1024s', args.cmdline[512:].encode()))
-
- if args.header_version > 0:
- args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
- if args.recovery_dtbo:
- args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
- else:
- args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
-
- # Populate boot image header size for header versions 1 and 2.
- if args.header_version == 1:
- args.output.write(pack('I', BOOT_IMAGE_HEADER_V1_SIZE))
- elif args.header_version == 2:
- args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE))
-
- if args.header_version > 1:
- args.output.write(pack('I', filesize(args.dtb))) # size in bytes
- args.output.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address
- pad_file(args.output, args.pagesize)
- return img_id
-
-
-class ValidateStrLenAction(Action):
- def __init__(self, option_strings, dest, nargs=None, **kwargs):
- if 'maxlen' not in kwargs:
- raise ValueError('maxlen must be set')
- self.maxlen = int(kwargs['maxlen'])
- del kwargs['maxlen']
- super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
-
- def __call__(self, parser, namespace, values, option_string=None):
- if len(values) > self.maxlen:
- raise ValueError('String argument too long: max {0:d}, got {1:d}'.
- format(self.maxlen, len(values)))
- setattr(namespace, self.dest, values)
-
-
-def write_padded_file(f_out, f_in, padding):
- if f_in is None:
- return
- f_out.write(f_in.read())
- pad_file(f_out, padding)
-
-
-def parse_int(x):
- return int(x, 0)
-
-def parse_os_version(x):
- match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
- if match:
- a = int(match.group(1))
- b = c = 0
- if match.lastindex >= 2:
- b = int(match.group(2))
- if match.lastindex == 3:
- c = int(match.group(3))
- # 7 bits allocated for each field
- assert a < 128
- assert b < 128
- assert c < 128
- return (a << 14) | (b << 7) | c
- return 0
-
-def parse_os_patch_level(x):
- match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
- if match:
- y = int(match.group(1)) - 2000
- m = int(match.group(2))
- # 7 bits allocated for the year, 4 bits for the month
- assert y >= 0 and y < 128
- assert m > 0 and m <= 12
- return (y << 4) | m
- return 0
-
-def parse_cmdline():
- parser = ArgumentParser()
- parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
- required=True)
- parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
- parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
- parser.add_argument('--dtb', help='path to dtb', type=FileType('rb'))
- recovery_dtbo_group = parser.add_mutually_exclusive_group()
- recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
- recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
- type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
- parser.add_argument('--cmdline', help='extra arguments to be passed on the '
- 'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
- parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
- parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)
- parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
- parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
- default=0x00f00000)
- parser.add_argument('--dtb_offset', help='dtb offset', type=parse_int, default=0x01f00000)
-
- parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
- default=0)
- parser.add_argument('--os_patch_level', help='operating system patch level',
- type=parse_os_patch_level, default=0)
- parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
- parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
- maxlen=16)
- parser.add_argument('--pagesize', help='page size', type=parse_int,
- choices=[2**i for i in range(11,15)], default=2048)
- parser.add_argument('--id', help='print the image ID on standard output',
- action='store_true')
- parser.add_argument('--header_version', help='boot image header version', type=parse_int, default=0)
- parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
- required=True)
- return parser.parse_args()
-
-
-def write_data(args):
- write_padded_file(args.output, args.kernel, args.pagesize)
- write_padded_file(args.output, args.ramdisk, args.pagesize)
- write_padded_file(args.output, args.second, args.pagesize)
-
- if args.header_version > 0:
- write_padded_file(args.output, args.recovery_dtbo, args.pagesize)
- if args.header_version > 1:
- write_padded_file(args.output, args.dtb, args.pagesize)
-
-def main():
- args = parse_cmdline()
- img_id = write_header(args)
- write_data(args)
- if args.id:
- if isinstance(img_id, str):
- # Python 2's struct.pack returns a string, but py3 returns bytes.
- img_id = [ord(x) for x in img_id]
- print('0x' + ''.join('{:02x}'.format(c) for c in img_id))
-
-if __name__ == '__main__':
- main()
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
deleted file mode 100755
index 789bf5e..0000000
--- a/mkbootimg/unpack_bootimg.py
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2018, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""unpacks the bootimage.
-
-Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
-"""
-
-from __future__ import print_function
-from argparse import ArgumentParser, FileType
-from struct import unpack
-import os
-
-
-def create_out_dir(dir_path):
- """creates a directory 'dir_path' if it does not exist"""
- if not os.path.exists(dir_path):
- os.makedirs(dir_path)
-
-
-def extract_image(offset, size, bootimage, extracted_image_name):
- """extracts an image from the bootimage"""
- bootimage.seek(offset)
- with open(extracted_image_name, 'wb') as file_out:
- file_out.write(bootimage.read(size))
-
-
-def get_number_of_pages(image_size, page_size):
- """calculates the number of pages required for the image"""
- return (image_size + page_size - 1) / page_size
-
-
-def unpack_bootimage(args):
- """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
- boot_magic = unpack('8s', args.boot_img.read(8))
- print('boot_magic: %s' % boot_magic)
- kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
- print('kernel_size: %s' % kernel_ramdisk_second_info[0])
- print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
- print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
- print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
- print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
- print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
- print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
- print('page size: %s' % kernel_ramdisk_second_info[7])
- print('boot image header version: %s' % kernel_ramdisk_second_info[8])
- print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
-
- product_name = unpack('16s', args.boot_img.read(16))
- print('product name: %s' % product_name)
- cmdline = unpack('512s', args.boot_img.read(512))
- print('command line args: %s' % cmdline)
-
- args.boot_img.read(32) # ignore SHA
-
- extra_cmdline = unpack('1024s', args.boot_img.read(1024))
- print('additional command line args: %s' % extra_cmdline)
-
- kernel_size = kernel_ramdisk_second_info[0]
- ramdisk_size = kernel_ramdisk_second_info[2]
- second_size = kernel_ramdisk_second_info[4]
- page_size = kernel_ramdisk_second_info[7]
- version = kernel_ramdisk_second_info[8]
- if version > 0:
- recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
- print('recovery dtbo size: %s' % recovery_dtbo_size)
- recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
- print('recovery dtbo offset: %#x' % recovery_dtbo_offset)
- boot_header_size = unpack('I', args.boot_img.read(4))[0]
- print('boot header size: %s' % boot_header_size)
- else:
- recovery_dtbo_size = 0
- if version > 1:
- dtb_size = unpack('I', args.boot_img.read(4))[0]
- print('dtb size: %s' % dtb_size)
- dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
- print('dtb address: %#x' % dtb_load_address)
- else:
- dtb_size = 0
-
-
- # The first page contains the boot header
- num_header_pages = 1
-
- num_kernel_pages = get_number_of_pages(kernel_size, page_size)
- kernel_offset = page_size * num_header_pages # header occupies a page
- image_info_list = [(kernel_offset, kernel_size, 'kernel')]
-
- num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
- ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
- ) # header + kernel
- image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
-
- 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,
- 'recovery_dtbo'))
- if dtb_size > 0:
- num_second_pages = get_number_of_pages(second_size, page_size)
- num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size)
- dtb_offset = page_size * (
- num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages +
- num_recovery_dtbo_pages
- )
-
- image_info_list.append((dtb_offset, dtb_size, 'dtb'))
-
- for image_info in image_info_list:
- extract_image(image_info[0], image_info[1], args.boot_img,
- os.path.join(args.out, image_info[2]))
-
-
-def parse_cmdline():
- """parse command line arguments"""
- parser = ArgumentParser(
- description='Unpacks boot.img/recovery.img, extracts the kernel,'
- 'ramdisk, second bootloader, recovery dtbo and dtb')
- parser.add_argument(
- '--boot_img',
- help='path to boot image',
- type=FileType('rb'),
- required=True)
- parser.add_argument('--out', help='path to out binaries', default='out')
- return parser.parse_args()
-
-
-def main():
- """parse arguments and unpack boot image"""
- args = parse_cmdline()
- create_out_dir(args.out)
- unpack_bootimage(args)
-
-
-if __name__ == '__main__':
- main()
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 5c57d69..ac802b5 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -3,6 +3,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index f484550..33da1f1 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -585,6 +585,7 @@
{"ro.boottime.imsdatadaemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.imsqmidaemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.init.first_stage", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.cold_boot_wait", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.mount_all.default", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.selinux", "u:object_r:boottime_prop:s0"},
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7ff1588..f084cd2 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -89,7 +89,7 @@
EXPORT_GLOBAL_GCOV_OPTIONS :=
ifeq ($(NATIVE_COVERAGE),true)
- EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+ EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
endif
# Put it here instead of in init.rc module definition,
@@ -97,7 +97,7 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 5aac61b..ad14493 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -20,6 +20,9 @@
[legacy]
namespace.default.isolated = false
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /product/${LIB}
@@ -41,7 +44,7 @@
additional.namespaces = runtime,conscrypt,media,resolv
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
# to load the shared library from the runtime namespace. And then, if the
@@ -50,17 +53,15 @@
# Finally, if all attempts fail, the dynamic linker returns an error.
namespace.default.links = runtime,resolv
namespace.default.asan.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.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
-# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
-namespace.default.link.runtime.shared_libs += libicui18n.so
-namespace.default.link.runtime.shared_libs += libicuuc.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
namespace.default.link.runtime.shared_libs += libpac.so
@@ -74,16 +75,20 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
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.
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -120,11 +125,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
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
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 84b308d..f732b3c 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -43,6 +43,9 @@
# can't be loaded in this namespace.
###############################################################################
namespace.default.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
@@ -121,7 +124,7 @@
namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
# to load the shared library from the runtime namespace. And then, if the
@@ -129,20 +132,19 @@
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
namespace.default.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
-# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
namespace.default.link.runtime.shared_libs += libicui18n.so
namespace.default.link.runtime.shared_libs += libicuuc.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.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
# 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
@@ -153,16 +155,20 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
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.
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -190,11 +196,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
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
@@ -237,6 +243,8 @@
# Note that there is no link from the default namespace to this namespace.
###############################################################################
namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.sphal.visible = true
namespace.sphal.search.paths = /odm/${LIB}
@@ -326,6 +334,8 @@
# This namespace is exclusively for vndk-sp libs.
###############################################################################
namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.vndk.visible = true
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
@@ -363,7 +373,7 @@
# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
# "sphal" namespace for vendor libs. The ordering matters. The "default"
# namespace has higher priority than the "sphal" namespace.
-namespace.vndk.links = default,sphal
+namespace.vndk.links = default,sphal,runtime
# When these NDK libs are required inside this namespace, then it is redirected
# to the default namespace. This is possible since their ABI is stable across
@@ -371,6 +381,8 @@
namespace.vndk.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.vndk.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+
# Allow VNDK-SP extensions to use vendor libraries
namespace.vndk.link.sphal.allow_all_shared_libs = true
@@ -423,8 +435,10 @@
namespace.default.asan.permitted.paths += /data/asan/vendor
namespace.default.asan.permitted.paths += /vendor
-namespace.default.links = system,vndk%VNDK_IN_SYSTEM_NS%
-namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.links = system,vndk%VNDK_IN_SYSTEM_NS%,runtime
+namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.default.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
namespace.default.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
@@ -433,15 +447,15 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = 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 = system
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
-# when it exists.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.system.allow_all_shared_libs = true
###############################################################################
@@ -477,13 +491,15 @@
# Android releases. The links here should be identical to that of the
# 'vndk_in_system' namespace, except for the link between 'vndk' and
# 'vndk_in_system'.
-namespace.vndk.links = system,default%VNDK_IN_SYSTEM_NS%
+namespace.vndk.links = system,default%VNDK_IN_SYSTEM_NS%,runtime
namespace.vndk.link.system.shared_libs = %LLNDK_LIBRARIES%
namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.vndk.link.default.allow_all_shared_libs = true
+namespace.vndk.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+
namespace.vndk.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
###############################################################################
@@ -507,7 +523,8 @@
namespace.system.links = runtime
namespace.system.link.runtime.shared_libs = libdexfile_external.so
-# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+namespace.system.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
namespace.system.link.runtime.shared_libs += libicui18n.so
namespace.system.link.runtime.shared_libs += libicuuc.so
namespace.system.link.runtime.shared_libs += libnativebridge.so
@@ -515,6 +532,7 @@
namespace.system.link.runtime.shared_libs += libnativeloader.so
# Workaround for b/124772622
namespace.system.link.runtime.shared_libs += libandroidicu.so
+namespace.system.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# "vndk_in_system" namespace
@@ -553,7 +571,8 @@
# 1. 'vndk_in_system' needs to be freely linked back to 'vndk'.
# 2. 'vndk_in_system' does not need to link to 'default', as any library that
# requires anything vendor would not be a vndk_in_system library.
-namespace.vndk_in_system.links = vndk,system
+namespace.vndk_in_system.links = vndk,system,runtime
+namespace.vndk_in_system.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
namespace.vndk_in_system.link.system.shared_libs = %LLNDK_LIBRARIES%
namespace.vndk_in_system.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -570,6 +589,10 @@
[unrestricted]
additional.namespaces = runtime,media,conscrypt,resolv
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
@@ -581,21 +604,21 @@
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.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
namespace.default.links = runtime,resolv
-namespace.default.visible = true
-
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
-# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
namespace.default.link.runtime.shared_libs += libicui18n.so
namespace.default.link.runtime.shared_libs += libicuuc.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.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.default.link.resolv.shared_libs = libnetd_resolv.so
@@ -603,16 +626,18 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
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.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -639,11 +664,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
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
@@ -684,3 +709,5 @@
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 5db7698..9212408 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -43,6 +43,9 @@
# partitions are also allowed temporarily.
###############################################################################
namespace.default.isolated = false
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
@@ -61,8 +64,7 @@
namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
-# Keep in sync with the platform namespace in the com.android.runtime APEX
-# ld.config.txt.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
# to load the shared library from the runtime namespace. And then, if the
@@ -70,17 +72,15 @@
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
namespace.default.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
-# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
namespace.default.link.runtime.shared_libs += libicui18n.so
namespace.default.link.runtime.shared_libs += libicuuc.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
@@ -94,17 +94,20 @@
# "runtime" APEX namespace
#
# This namespace pulls in externally accessible libs from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-# Keep in sync with the default namespace in the com.android.runtime APEX
-# ld.config.txt.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
-# when it exists.
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -130,11 +133,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
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
@@ -177,6 +180,8 @@
# Note that there is no link from the default namespace to this namespace.
###############################################################################
namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.sphal.visible = true
namespace.sphal.search.paths = /odm/${LIB}
@@ -266,6 +271,8 @@
# This namespace is exclusively for vndk-sp libs.
###############################################################################
namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.vndk.visible = true
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
@@ -358,7 +365,8 @@
namespace.default.links = runtime
namespace.default.link.runtime.shared_libs = libdexfile_external.so
-# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
namespace.default.link.runtime.shared_libs += libicui18n.so
namespace.default.link.runtime.shared_libs += libicuuc.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
@@ -371,15 +379,15 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = 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.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -391,6 +399,10 @@
[unrestricted]
additional.namespaces = runtime,media,conscrypt,resolv
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
@@ -402,21 +414,21 @@
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.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
namespace.default.links = runtime,resolv
-namespace.default.visible = true
-
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
-# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
namespace.default.link.runtime.shared_libs += libicui18n.so
namespace.default.link.runtime.shared_libs += libicuuc.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.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.default.link.resolv.shared_libs = libnetd_resolv.so
@@ -424,16 +436,18 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
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.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
namespace.runtime.link.default.allow_all_shared_libs = true
###############################################################################
@@ -459,11 +473,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
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
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
index 29e4519..4fee15f 100644
--- a/rootdir/fsverity_init.sh
+++ b/rootdir/fsverity_init.sh
@@ -24,6 +24,9 @@
log -p e -t fsverity_init "Failed to load $cert"
done
-# Prevent future key links to .fs-verity keyring
-/system/bin/mini-keyctl restrict_keyring .fs-verity ||
- log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+DEBUGGABLE=$(getprop ro.debuggable)
+if [ $DEBUGGABLE != "1" ]; then
+ # Prevent future key links to .fs-verity keyring
+ /system/bin/mini-keyctl restrict_keyring .fs-verity ||
+ log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+fi
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c042c48..55a1623 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -32,6 +32,12 @@
# cgroup for system_server and surfaceflinger
mkdir /dev/memcg/system 0550 system system
+ # set RLIMIT_NICE to allow priorities from 19 to -20
+ setrlimit nice 40 40
+
+ # Allow up to 32K FDs per process
+ setrlimit nofile 32768 32768
+
start ueventd
# Run apexd-bootstrap so that APEXes that provide critical libraries
@@ -267,12 +273,6 @@
export DOWNLOAD_CACHE /data/cache
- # set RLIMIT_NICE to allow priorities from 19 to -20
- setrlimit nice 40 40
-
- # Allow up to 32K FDs per process
- setrlimit nofile 32768 32768
-
# This allows the ledtrig-transient properties to be created here so
# that they can be chown'd to system:system later on boot
write /sys/class/leds/vibrator/trigger "transient"
@@ -445,6 +445,7 @@
mkdir /data/apex 0750 root system
mkdir /data/apex/active 0750 root system
mkdir /data/apex/backup 0700 root system
+ mkdir /data/apex/hashtree 0700 root system
mkdir /data/apex/sessions 0700 root system
mkdir /data/app-staging 0750 system system
start apexd
@@ -604,8 +605,9 @@
# 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
+ # Check any timezone data in /data is newer than the copy in the time zone data
+ # module, delete if not.
+ exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/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/shell_and_utilities/README.md b/shell_and_utilities/README.md
index ffda3a5..1926a4f 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -211,3 +211,32 @@
true truncate tty tunctl ulimit umount uname uniq unix2dos unlink
unshare uptime usleep uudecode uuencode uuidgen vconfig vmstat watch
wc which whoami xargs xxd yes zcat
+
+## Android R
+
+BSD: grep fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+one-true-awk: awk
+
+toolbox: getevent getprop setprop start stop
+
+toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze getconf
+getenforce getfattr grep groups gunzip gzip head help hostname hwclock
+i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
+install ionice iorenice iotop kill killall ln load\_policy log logname
+losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
+paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
+printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
+rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
+stat strings stty swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout top touch tr traceroute traceroute6 true truncate tty tunctl
+ulimit umount uname uniq unix2dos unlink unshare uptime usleep uudecode
+uuencode uuidgen vconfig vmstat watch wc which whoami xargs xxd yes zcat
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 1f852ff..9ca5607 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -28,6 +28,8 @@
"toolbox.c",
"getevent.c",
"getprop.cpp",
+ "setprop.cpp",
+ "start.cpp",
],
generated_headers: [
"toolbox_input_labels",
@@ -40,6 +42,9 @@
symlinks: [
"getevent",
"getprop",
+ "setprop",
+ "start",
+ "stop",
],
}
@@ -56,14 +61,6 @@
defaults: ["toolbox_binary_defaults"],
}
-// We only want 'r' on userdebug and eng builds.
-cc_binary {
- name: "r",
- defaults: ["toolbox_defaults"],
- srcs: ["r.c"],
- vendor_available: true,
-}
-
// We build BSD grep separately (but see http://b/111849261).
cc_defaults {
name: "grep_common",
diff --git a/toolbox/r.c b/toolbox/r.c
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#if __LP64__
-#define strtoptr strtoull
-#else
-#define strtoptr strtoul
-#endif
-
-static int usage()
-{
- fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- if(argc < 2) return usage();
-
- int width = 4;
- if(!strcmp(argv[1], "-b")) {
- width = 1;
- argc--;
- argv++;
- } else if(!strcmp(argv[1], "-s")) {
- width = 2;
- argc--;
- argv++;
- }
-
- if(argc < 2) return usage();
- uintptr_t addr = strtoptr(argv[1], 0, 16);
-
- uintptr_t endaddr = 0;
- char* end = strchr(argv[1], '-');
- if (end)
- endaddr = strtoptr(end + 1, 0, 16);
-
- if (!endaddr)
- endaddr = addr + width - 1;
-
- if (endaddr <= addr) {
- fprintf(stderr, "end address <= start address\n");
- return -1;
- }
-
- bool set = false;
- uint32_t value = 0;
- if(argc > 2) {
- set = true;
- value = strtoul(argv[2], 0, 16);
- }
-
- int fd = open("/dev/mem", O_RDWR | O_SYNC);
- if(fd < 0) {
- fprintf(stderr,"cannot open /dev/mem\n");
- return -1;
- }
-
- off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
- size_t mmap_size = endaddr - mmap_start + 1;
- mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
- void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, mmap_start);
-
- if(page == MAP_FAILED){
- fprintf(stderr,"cannot mmap region\n");
- return -1;
- }
-
- while (addr <= endaddr) {
- switch(width){
- case 4: {
- uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
- break;
- }
- case 2: {
- uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
- break;
- }
- case 1: {
- uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
- break;
- }
- }
- addr += width;
- }
- return 0;
-}
diff --git a/toolbox/setprop.cpp b/toolbox/setprop.cpp
new file mode 100644
index 0000000..acf8c3e
--- /dev/null
+++ b/toolbox/setprop.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 <ctype.h>
+#include <stdlib.h>
+#include <sys/system_properties.h>
+
+#include <iostream>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+using android::base::SetProperty;
+using android::base::StartsWith;
+
+extern "C" int setprop_main(int argc, char** argv) {
+ if (argc != 3) {
+ std::cout << "usage: setprop NAME VALUE\n"
+ "\n"
+ "Sets an Android system property."
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ auto name = std::string{argv[1]};
+ auto value = std::string{argv[2]};
+
+ // SetProperty() doesn't tell us why it failed, and actually can't recognize most failures, so
+ // we duplicate some of init's checks here to help the user.
+
+ if (name.front() == '.' || name.back() == '.') {
+ std::cerr << "Property names must not start or end with a '.'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (name.find("..") != std::string::npos) {
+ std::cerr << "'..' is not allowed in a property name" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ for (const auto& c : name) {
+ if (!isalnum(c) && !strchr(":@_.-", c)) {
+ std::cerr << "Invalid character '" << c << "' in name '" << name << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (value.size() >= PROP_VALUE_MAX && !StartsWith(value, "ro.")) {
+ std::cerr << "Value '" << value << "' is too long, " << value.size()
+ << " bytes vs a max of " << PROP_VALUE_MAX << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+ std::cerr << "Value '" << value << "' is not a UTF8 encoded string" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (!SetProperty(name, value)) {
+ std::cerr << "Failed to set property '" << name << "' to '" << value
+ << "'.\nSee dmesg for error reason." << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
new file mode 100644
index 0000000..b87ed15
--- /dev/null
+++ b/toolbox/start.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 <stdlib.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+
+using android::base::GetProperty;
+using android::base::SetProperty;
+using namespace std::literals;
+
+static void ControlService(bool start, const std::string& service) {
+ if (!android::base::SetProperty(start ? "ctl.start" : "ctl.stop", service)) {
+ std::cerr << "Unable to " << (start ? "start" : "stop") << " service '" << service
+ << "'\nSee dmesg for error reason." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void ControlDefaultServices(bool start) {
+ std::vector<std::string> services = {"netd", "surfaceflinger", "zygote"};
+
+ // Only start zygote_secondary if not single arch.
+ std::string zygote_configuration = GetProperty("ro.zygote", "");
+ if (zygote_configuration != "zygote32" && zygote_configuration != "zygote64") {
+ services.emplace_back("zygote_secondary");
+ }
+
+ if (start) {
+ for (const auto& service : services) {
+ ControlService(true, service);
+ }
+ } else {
+ for (auto it = services.crbegin(); it != services.crend(); ++it) {
+ ControlService(false, *it);
+ }
+ }
+}
+
+static int StartStop(int argc, char** argv, bool start) {
+ if (getuid()) {
+ std::cerr << "Must be root" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (argc == 1) {
+ ControlDefaultServices(start);
+ }
+
+ if (argc == 2 && argv[1] == "--help"s) {
+ std::cout << "usage: " << (start ? "start" : "stop")
+ << " [SERVICE...]\n"
+ "\n"
+ << (start ? "Starts" : "Stops")
+ << " the given system service, or netd/surfaceflinger/zygotes." << std::endl;
+ return EXIT_SUCCESS;
+ }
+
+ for (int i = 1; i < argc; ++i) {
+ ControlService(start, argv[i]);
+ }
+ return EXIT_SUCCESS;
+}
+
+extern "C" int start_main(int argc, char** argv) {
+ return StartStop(argc, argv, true);
+}
+
+extern "C" int stop_main(int argc, char** argv) {
+ return StartStop(argc, argv, false);
+}
\ No newline at end of file
diff --git a/toolbox/tools.h b/toolbox/tools.h
index abeb3ef..9a7ebd2 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,3 +1,6 @@
TOOL(getevent)
TOOL(getprop)
+TOOL(setprop)
+TOOL(start)
+TOOL(stop)
TOOL(toolbox)
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
new file mode 100644
index 0000000..b5fc6bf
--- /dev/null
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -0,0 +1,571 @@
+/*
+ **
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.keymaster@4.0-impl.trusty"
+
+#include <authorization_set.h>
+#include <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <trusty_keymaster/TrustyKeymaster4Device.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::AddEntropyRequest;
+using ::keymaster::AddEntropyResponse;
+using ::keymaster::AttestKeyRequest;
+using ::keymaster::AttestKeyResponse;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::BeginOperationRequest;
+using ::keymaster::BeginOperationResponse;
+using ::keymaster::ExportKeyRequest;
+using ::keymaster::ExportKeyResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::GenerateKeyRequest;
+using ::keymaster::GenerateKeyResponse;
+using ::keymaster::GetKeyCharacteristicsRequest;
+using ::keymaster::GetKeyCharacteristicsResponse;
+using ::keymaster::ImportKeyRequest;
+using ::keymaster::ImportKeyResponse;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using ::keymaster::ng::Tag;
+
+namespace keymaster {
+namespace V4_0 {
+namespace {
+
+inline keymaster_tag_t legacy_enum_conversion(const Tag value) {
+ return keymaster_tag_t(value);
+}
+inline Tag legacy_enum_conversion(const keymaster_tag_t value) {
+ return Tag(value);
+}
+inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
+ return keymaster_purpose_t(value);
+}
+inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
+ return keymaster_key_format_t(value);
+}
+
+inline SecurityLevel legacy_enum_conversion(const keymaster_security_level_t value) {
+ return static_cast<SecurityLevel>(value);
+}
+
+inline hw_authenticator_type_t legacy_enum_conversion(const HardwareAuthenticatorType value) {
+ return static_cast<hw_authenticator_type_t>(value);
+}
+
+inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
+ return ErrorCode(value);
+}
+
+inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
+ return keymaster_tag_get_type(tag);
+}
+
+class KmParamSet : public keymaster_key_param_set_t {
+ public:
+ KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
+ params = new keymaster_key_param_t[keyParams.size()];
+ length = keyParams.size();
+ for (size_t i = 0; i < keyParams.size(); ++i) {
+ auto tag = legacy_enum_conversion(keyParams[i].tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
+ break;
+ case KM_UINT:
+ case KM_UINT_REP:
+ params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
+ break;
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
+ break;
+ case KM_DATE:
+ params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
+ break;
+ case KM_BOOL:
+ if (keyParams[i].f.boolValue)
+ params[i] = keymaster_param_bool(tag);
+ else
+ params[i].tag = KM_TAG_INVALID;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],
+ keyParams[i].blob.size());
+ break;
+ case KM_INVALID:
+ default:
+ params[i].tag = KM_TAG_INVALID;
+ /* just skip */
+ break;
+ }
+ }
+ }
+ KmParamSet(KmParamSet&& other) noexcept
+ : keymaster_key_param_set_t{other.params, other.length} {
+ other.length = 0;
+ other.params = nullptr;
+ }
+ KmParamSet(const KmParamSet&) = delete;
+ ~KmParamSet() { delete[] params; }
+};
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);
+ return result;
+}
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);
+ return result;
+}
+
+inline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());
+ return result;
+}
+
+inline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(
+ const keymaster_cert_chain_t& cert_chain) {
+ hidl_vec<hidl_vec<uint8_t>> result;
+ if (!cert_chain.entry_count || !cert_chain.entries) return result;
+
+ result.resize(cert_chain.entry_count);
+ for (size_t i = 0; i < cert_chain.entry_count; ++i) {
+ result[i] = kmBlob2hidlVec(cert_chain.entries[i]);
+ }
+
+ return result;
+}
+
+static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
+ hidl_vec<KeyParameter> result;
+ if (set.length == 0 || set.params == nullptr) return result;
+
+ result.resize(set.length);
+ keymaster_key_param_t* params = set.params;
+ for (size_t i = 0; i < set.length; ++i) {
+ auto tag = params[i].tag;
+ result[i].tag = legacy_enum_conversion(tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ result[i].f.integer = params[i].enumerated;
+ break;
+ case KM_UINT:
+ case KM_UINT_REP:
+ result[i].f.integer = params[i].integer;
+ break;
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ result[i].f.longInteger = params[i].long_integer;
+ break;
+ case KM_DATE:
+ result[i].f.dateTime = params[i].date_time;
+ break;
+ case KM_BOOL:
+ result[i].f.boolValue = params[i].boolean;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),
+ params[i].blob.data_length);
+ break;
+ case KM_INVALID:
+ default:
+ params[i].tag = KM_TAG_INVALID;
+ /* just skip */
+ break;
+ }
+ }
+ return result;
+}
+
+void addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+ ::keymaster::AuthorizationSet* params) {
+ params->Clear();
+ if (clientId.size()) {
+ params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());
+ }
+ if (appData.size()) {
+ params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
+ }
+}
+
+} // anonymous namespace
+
+TrustyKeymaster4Device::TrustyKeymaster4Device(TrustyKeymaster* impl) : impl_(impl) {}
+
+TrustyKeymaster4Device::~TrustyKeymaster4Device() {}
+
+Return<void> TrustyKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) {
+ _hidl_cb(SecurityLevel::TRUSTED_ENVIRONMENT, "TrustyKeymaster", "Google");
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::getHmacSharingParameters(
+ getHmacSharingParameters_cb _hidl_cb) {
+ const GetHmacSharingParametersResponse response = impl_->GetHmacSharingParameters();
+ // response.params is not the same as the HIDL structure, we need to convert it
+ V4_0::HmacSharingParameters params;
+ params.seed.setToExternal(const_cast<uint8_t*>(response.params.seed.data),
+ response.params.seed.data_length);
+ static_assert(sizeof(response.params.nonce) == params.nonce.size(), "Nonce sizes don't match");
+ memcpy(params.nonce.data(), response.params.nonce, params.nonce.size());
+ _hidl_cb(legacy_enum_conversion(response.error), params);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::computeSharedHmac(
+ const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {
+ ComputeSharedHmacRequest request;
+ request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
+ request.params_array.num_params = params.size();
+ for (size_t i = 0; i < params.size(); ++i) {
+ request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};
+ static_assert(sizeof(request.params_array.params_array[i].nonce) ==
+ decltype(params[i].nonce)::size(),
+ "Nonce sizes don't match");
+ memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
+ params[i].nonce.size());
+ }
+
+ auto response = impl_->ComputeSharedHmac(request);
+ hidl_vec<uint8_t> sharing_check;
+ if (response.error == KM_ERROR_OK) {
+ sharing_check = kmBlob2hidlVec(response.sharing_check);
+ }
+
+ _hidl_cb(legacy_enum_conversion(response.error), sharing_check);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::verifyAuthorization(
+ uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,
+ const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {
+ VerifyAuthorizationRequest request;
+ request.challenge = challenge;
+ request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));
+ request.auth_token.challenge = authToken.challenge;
+ request.auth_token.user_id = authToken.userId;
+ request.auth_token.authenticator_id = authToken.authenticatorId;
+ request.auth_token.authenticator_type = legacy_enum_conversion(authToken.authenticatorType);
+ request.auth_token.timestamp = authToken.timestamp;
+ KeymasterBlob mac(authToken.mac.data(), authToken.mac.size());
+ request.auth_token.mac = mac;
+
+ auto response = impl_->VerifyAuthorization(request);
+
+ ::android::hardware::keymaster::V4_0::VerificationToken token;
+ token.challenge = response.token.challenge;
+ token.timestamp = response.token.timestamp;
+ token.parametersVerified = kmParamSet2Hidl(response.token.parameters_verified);
+ token.securityLevel = legacy_enum_conversion(response.token.security_level);
+ token.mac = kmBlob2hidlVec(response.token.mac);
+
+ _hidl_cb(legacy_enum_conversion(response.error), token);
+
+ return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
+ if (data.size() == 0) return ErrorCode::OK;
+ AddEntropyRequest request;
+ request.random_data.Reinitialize(data.data(), data.size());
+
+ AddEntropyResponse response;
+ impl_->AddRngEntropy(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<void> TrustyKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
+ generateKey_cb _hidl_cb) {
+ GenerateKeyRequest request;
+ request.key_description.Reinitialize(KmParamSet(keyParams));
+
+ GenerateKeyResponse response;
+ impl_->GenerateKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ getKeyCharacteristics_cb _hidl_cb) {
+ GetKeyCharacteristicsRequest request;
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ addClientAndAppData(clientId, appData, &request.additional_params);
+
+ GetKeyCharacteristicsResponse response;
+ impl_->GetKeyCharacteristics(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ if (response.error == KM_ERROR_OK) {
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::importKey(const hidl_vec<KeyParameter>& params,
+ KeyFormat keyFormat,
+ const hidl_vec<uint8_t>& keyData,
+ importKey_cb _hidl_cb) {
+ ImportKeyRequest request;
+ request.key_description.Reinitialize(KmParamSet(params));
+ request.key_format = legacy_enum_conversion(keyFormat);
+ request.SetKeyMaterial(keyData.data(), keyData.size());
+
+ ImportKeyResponse response;
+ impl_->ImportKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::importWrappedKey(
+ const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,
+ const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,
+ uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {
+ ImportWrappedKeyRequest request;
+ request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
+ request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
+ request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
+ request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
+ request.password_sid = passwordSid;
+ request.biometric_sid = biometricSid;
+
+ ImportWrappedKeyResponse response;
+ impl_->ImportWrappedKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::exportKey(KeyFormat exportFormat,
+ const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ exportKey_cb _hidl_cb) {
+ ExportKeyRequest request;
+ request.key_format = legacy_enum_conversion(exportFormat);
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ addClientAndAppData(clientId, appData, &request.additional_params);
+
+ ExportKeyResponse response;
+ impl_->ExportKey(request, &response);
+
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob.setToExternal(response.key_data, response.key_data_length);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
+ const hidl_vec<KeyParameter>& attestParams,
+ attestKey_cb _hidl_cb) {
+ AttestKeyRequest request;
+ request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
+ request.attest_params.Reinitialize(KmParamSet(attestParams));
+
+ AttestKeyResponse response;
+ impl_->AttestKey(request, &response);
+
+ hidl_vec<hidl_vec<uint8_t>> resultCertChain;
+ if (response.error == KM_ERROR_OK) {
+ resultCertChain = kmCertChain2Hidl(response.certificate_chain);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+ const hidl_vec<KeyParameter>& upgradeParams,
+ upgradeKey_cb _hidl_cb) {
+ UpgradeKeyRequest request;
+ request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+ request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+ UpgradeKeyResponse response;
+ impl_->UpgradeKey(request, &response);
+
+ if (response.error == KM_ERROR_OK) {
+ _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));
+ } else {
+ _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());
+ }
+ return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+ DeleteKeyRequest request;
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+ DeleteKeyResponse response;
+ impl_->DeleteKey(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::deleteAllKeys() {
+ DeleteAllKeysRequest request;
+ DeleteAllKeysResponse response;
+ impl_->DeleteAllKeys(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::destroyAttestationIds() {
+ return ErrorCode::UNIMPLEMENTED;
+}
+
+Return<void> TrustyKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+ const hidl_vec<KeyParameter>& inParams,
+ const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
+ (void)authToken;
+ BeginOperationRequest request;
+ request.purpose = legacy_enum_conversion(purpose);
+ request.SetKeyMaterial(key.data(), key.size());
+ request.additional_params.Reinitialize(KmParamSet(inParams));
+
+ BeginOperationResponse response;
+ impl_->BeginOperation(request, &response);
+
+ hidl_vec<KeyParameter> resultParams;
+ if (response.error == KM_ERROR_OK) {
+ resultParams = kmParamSet2Hidl(response.output_params);
+ }
+
+ _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::update(uint64_t operationHandle,
+ const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken,
+ update_cb _hidl_cb) {
+ (void)authToken;
+ (void)verificationToken;
+ UpdateOperationRequest request;
+ UpdateOperationResponse response;
+ hidl_vec<KeyParameter> resultParams;
+ hidl_vec<uint8_t> resultBlob;
+ uint32_t resultConsumed = 0;
+
+ request.op_handle = operationHandle;
+ request.additional_params.Reinitialize(KmParamSet(inParams));
+
+ size_t inp_size = input.size();
+ size_t ser_size = request.SerializedSize();
+
+ if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ response.error = KM_ERROR_INVALID_INPUT_LENGTH;
+ } else {
+ if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;
+ }
+ request.input.Reinitialize(input.data(), inp_size);
+
+ impl_->UpdateOperation(request, &response);
+
+ if (response.error == KM_ERROR_OK) {
+ resultConsumed = response.input_consumed;
+ resultParams = kmParamSet2Hidl(response.output_params);
+ resultBlob = kmBuffer2hidlVec(response.output);
+ }
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::finish(uint64_t operationHandle,
+ const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input,
+ const hidl_vec<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken,
+ finish_cb _hidl_cb) {
+ (void)authToken;
+ (void)verificationToken;
+ FinishOperationRequest request;
+ request.op_handle = operationHandle;
+ request.input.Reinitialize(input.data(), input.size());
+ request.signature.Reinitialize(signature.data(), signature.size());
+ request.additional_params.Reinitialize(KmParamSet(inParams));
+
+ FinishOperationResponse response;
+ impl_->FinishOperation(request, &response);
+
+ hidl_vec<KeyParameter> resultParams;
+ hidl_vec<uint8_t> resultBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultParams = kmParamSet2Hidl(response.output_params);
+ resultBlob = kmBuffer2hidlVec(response.output);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);
+ return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::abort(uint64_t operationHandle) {
+ AbortOperationRequest request;
+ request.op_handle = operationHandle;
+
+ AbortOperationResponse response;
+ impl_->AbortOperation(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+} // namespace V4_0
+} // namespace keymaster
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc
new file mode 100644
index 0000000..72c9167
--- /dev/null
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.keymaster-4-0 /vendor/bin/hw/android.hardware.keymaster@4.0-service.trusty
+ class early_hal
+ user nobody
+ group drmrpc
diff --git a/trusty/keymaster/4.0/service.cpp b/trusty/keymaster/4.0/service.cpp
new file mode 100644
index 0000000..96eb584
--- /dev/null
+++ b/trusty/keymaster/4.0/service.cpp
@@ -0,0 +1,43 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <android-base/logging.h>
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <hidl/HidlTransportSupport.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+#include <trusty_keymaster/TrustyKeymaster4Device.h>
+
+int main() {
+ ::android::hardware::configureRpcThreadpool(1, true);
+ auto trustyKeymaster = new keymaster::TrustyKeymaster();
+ int err = trustyKeymaster->Initialize();
+ if (err != 0) {
+ LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
+ return -1;
+ }
+
+ auto keymaster = new ::keymaster::V4_0::TrustyKeymaster4Device(trustyKeymaster);
+
+ auto status = keymaster->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for Keymaster 4.0 (" << status << ")";
+ return -1;
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return -1; // Should never get here.
+}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 819851f..d107b78 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -108,3 +108,34 @@
"android.hardware.keymaster@3.0"
],
}
+
+cc_binary {
+ name: "android.hardware.keymaster@4.0-service.trusty",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["4.0/android.hardware.keymaster@4.0-service.trusty.rc"],
+ srcs: [
+ "4.0/service.cpp",
+ "4.0/TrustyKeymaster4Device.cpp",
+ "ipc/trusty_keymaster_ipc.cpp",
+ "TrustyKeymaster.cpp",
+ ],
+
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libdl",
+ "libbase",
+ "libutils",
+ "libhardware",
+ "libhidlbase",
+ "libhidltransport",
+ "libtrusty",
+ "libkeymaster_messages",
+ "libkeymaster4",
+ "android.hardware.keymaster@4.0"
+ ],
+}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 7f5e87f..f3ef747 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -172,24 +172,25 @@
ForwardCommand(KM_ABORT_OPERATION, request, response);
}
-/* Methods for Keymaster 4.0 functionality -- not yet implemented */
GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
+ // Dummy empty buffer to allow ForwardCommand to have something to serialize
+ Buffer request;
GetHmacSharingParametersResponse response;
- response.error = KM_ERROR_UNIMPLEMENTED;
+ ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
return response;
}
ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
- const ComputeSharedHmacRequest& /* request */) {
+ const ComputeSharedHmacRequest& request) {
ComputeSharedHmacResponse response;
- response.error = KM_ERROR_UNIMPLEMENTED;
+ ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);
return response;
}
VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
- const VerifyAuthorizationRequest& /* request */) {
+ const VerifyAuthorizationRequest& request) {
VerifyAuthorizationResponse response;
- response.error = KM_ERROR_UNIMPLEMENTED;
+ ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);
return response;
}
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
new file mode 100644
index 0000000..2be15bc
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
@@ -0,0 +1,105 @@
+/*
+ **
+ ** Copyright 2017, 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.
+ */
+
+#ifndef keymaster_V4_0_TrustyKeymaster4Device_H_
+#define keymaster_V4_0_TrustyKeymaster4Device_H_
+
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <hidl/Status.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace keymaster {
+
+namespace V4_0 {
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::keymaster::V4_0::ErrorCode;
+using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using ::android::hardware::keymaster::V4_0::HmacSharingParameters;
+using ::android::hardware::keymaster::V4_0::IKeymasterDevice;
+using ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using ::android::hardware::keymaster::V4_0::KeyFormat;
+using ::android::hardware::keymaster::V4_0::KeyParameter;
+using ::android::hardware::keymaster::V4_0::KeyPurpose;
+using ::android::hardware::keymaster::V4_0::SecurityLevel;
+using ::android::hardware::keymaster::V4_0::Tag;
+using ::android::hardware::keymaster::V4_0::VerificationToken;
+
+class TrustyKeymaster4Device : public IKeymasterDevice {
+ public:
+ explicit TrustyKeymaster4Device(TrustyKeymaster* impl);
+ virtual ~TrustyKeymaster4Device();
+
+ Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override;
+ Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override;
+ Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
+ computeSharedHmac_cb) override;
+ Return<void> verifyAuthorization(uint64_t challenge,
+ const hidl_vec<KeyParameter>& parametersToVerify,
+ const HardwareAuthToken& authToken,
+ verifyAuthorization_cb _hidl_cb) override;
+ Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+ Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+ generateKey_cb _hidl_cb) override;
+ Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ getKeyCharacteristics_cb _hidl_cb) override;
+ Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+ const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
+ Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
+ const hidl_vec<uint8_t>& wrappingKeyBlob,
+ const hidl_vec<uint8_t>& maskingKey,
+ const hidl_vec<KeyParameter>& unwrappingParams,
+ uint64_t passwordSid, uint64_t biometricSid,
+ importWrappedKey_cb _hidl_cb) override;
+ Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+ exportKey_cb _hidl_cb) override;
+ Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+ const hidl_vec<KeyParameter>& attestParams,
+ attestKey_cb _hidl_cb) override;
+ Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+ const hidl_vec<KeyParameter>& upgradeParams,
+ upgradeKey_cb _hidl_cb) override;
+ Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+ Return<ErrorCode> deleteAllKeys() override;
+ Return<ErrorCode> destroyAttestationIds() override;
+ Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+ const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+ begin_cb _hidl_cb) override;
+ Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, update_cb _hidl_cb) override;
+ Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, finish_cb _hidl_cb) override;
+ Return<ErrorCode> abort(uint64_t operationHandle) override;
+
+ private:
+ std::unique_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+} // namespace V4_0
+} // namespace keymaster
+
+#endif // keymaster_V4_0_TrustyKeymaster4Device_H_
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 0a0ecec..445d3ce 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,8 +19,13 @@
# to pull in the baseline set of Trusty specific modules.
#
+# For gatekeeper, we include the generic -service and -impl to use legacy
+# HAL loading of gatekeeper.trusty.
+
PRODUCT_PACKAGES += \
- android.hardware.keymaster@3.0-service.trusty \
+ android.hardware.keymaster@4.0-service.trusty \
+ android.hardware.gatekeeper@1.0-service \
+ android.hardware.gatekeeper@1.0-impl \
gatekeeper.trusty
PRODUCT_PROPERTY_OVERRIDES += \
diff --git a/usbd/usbd.cpp b/usbd/usbd.cpp
index 191fb92..6e24d8e 100644
--- a/usbd/usbd.cpp
+++ b/usbd/usbd.cpp
@@ -24,8 +24,6 @@
#include <hidl/HidlTransportSupport.h>
-#define PERSISTENT_USB_CONFIG "persist.sys.usb.config"
-
using android::base::GetProperty;
using android::base::SetProperty;
using android::hardware::configureRpcThreadpool;
@@ -34,14 +32,15 @@
using android::hardware::Return;
int main(int /*argc*/, char** /*argv*/) {
- configureRpcThreadpool(1, true /*callerWillJoin*/);
+ if (GetProperty("ro.bootmode", "") == "charger") exit(0);
+ configureRpcThreadpool(1, true /*callerWillJoin*/);
android::sp<IUsbGadget> gadget = IUsbGadget::getService();
Return<void> ret;
if (gadget != nullptr) {
LOG(INFO) << "Usb HAL found.";
- std::string function = GetProperty(PERSISTENT_USB_CONFIG, "");
+ std::string function = GetProperty("persist.sys.usb.config", "");
if (function == "adb") {
LOG(INFO) << "peristent prop is adb";
SetProperty("ctl.start", "adbd");