Merge "liblog: Fix memory leaks"
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..c47230f
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "adbd_test"
+ }
+ ]
+}
diff --git a/adb/Android.bp b/adb/Android.bp
index bccc71a..00e98fe 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -25,6 +25,7 @@
"-Wvla",
],
rtti: true,
+ cpp_std: "gnu++17",
use_version_lib: true,
@@ -41,7 +42,10 @@
target: {
android: {
- cflags: ["-DADB_HOST=0"],
+ cflags: [
+ "-DADB_HOST=0",
+ "-Wthread-safety",
+ ],
},
host: {
@@ -174,6 +178,12 @@
"libdiagnose_usb",
"libmdnssd",
"libusb",
+ "libandroidfw",
+ "libziparchive",
+ "libz",
+ "libutils",
+ "liblog",
+ "libcutils",
],
}
@@ -256,6 +266,12 @@
"liblog",
"libmdnssd",
"libusb",
+ "libandroidfw",
+ "libziparchive",
+ "libz",
+ "libutils",
+ "liblog",
+ "libcutils",
],
stl: "libc++_static",
@@ -299,6 +315,8 @@
"daemon/auth.cpp",
"daemon/jdwp_service.cpp",
"daemon/usb.cpp",
+ "daemon/usb_ffs.cpp",
+ "daemon/usb_legacy.cpp",
],
local_include_dirs: [
@@ -474,7 +492,7 @@
},
}
-python_binary_host {
+python_test_host {
name: "adb_integration_test_device",
main: "test_device.py",
srcs: [
@@ -483,6 +501,8 @@
libs: [
"adb_py",
],
+ test_config: "adb_integration_test_device.xml",
+ test_suites: ["general-tests"],
version: {
py2: {
enabled: true,
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 62e8908..9c0eeca 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -73,39 +73,6 @@
android::base::GetExecutablePath().c_str());
}
-void fatal(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- char buf[1024];
- vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
- fprintf(stderr, "error: %s\n", buf);
-#else
- LOG(ERROR) << "error: " << buf;
-#endif
-
- va_end(ap);
- abort();
-}
-
-void fatal_errno(const char* fmt, ...) {
- int err = errno;
- va_list ap;
- va_start(ap, fmt);
- char buf[1024];
- vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
- fprintf(stderr, "error: %s: %s\n", buf, strerror(err));
-#else
- LOG(ERROR) << "error: " << buf << ": " << strerror(err);
-#endif
-
- va_end(ap);
- abort();
-}
-
uint32_t calculate_apacket_checksum(const apacket* p) {
uint32_t sum = 0;
for (size_t i = 0; i < p->msg.data_length; ++i) {
@@ -118,7 +85,7 @@
{
apacket* p = new apacket();
if (p == nullptr) {
- fatal("failed to allocate an apacket");
+ LOG(FATAL) << "failed to allocate an apacket";
}
memset(&p->msg, 0, sizeof(p->msg));
diff --git a/adb/adb.h b/adb/adb.h
index f434e2d..8c37c4b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -59,7 +59,7 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 40
+#define ADB_SERVER_VERSION 41
using TransportId = uint64_t;
class atransport;
@@ -124,9 +124,6 @@
void print_packet(const char* label, apacket* p);
-void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
-void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
-
void handle_packet(apacket* p, atransport* t);
int launch_server(const std::string& socket_spec);
diff --git a/adb/adb_integration_test_device.xml b/adb/adb_integration_test_device.xml
new file mode 100644
index 0000000..b892377
--- /dev/null
+++ b/adb/adb_integration_test_device.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config to run adb integration tests for device">
+ <option name="test-suite-tag" value="adb_tests" />
+ <option name="test-suite-tag" value="adb_integration_device" />
+ <target_preparer class="com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer">
+ <option name="disable" value="false" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.adb.AdbStopServerPreparer" />
+ <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+ <option name="par-file-name" value="adb_integration_test_device" />
+ <option name="inject-android-serial" value="true" />
+ <option name="test-timeout" value="2m" />
+ </test>
+</configuration>
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index f4a92e3..051ab73 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <list>
+#include <memory>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -29,7 +30,6 @@
#include "socket_spec.h"
#include "sysdeps.h"
-#include "sysdeps/memory.h"
#include "transport.h"
// A listener is an entity which binds to a local port and, upon receiving a connection on that
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 35017f0..6960345 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -57,11 +57,11 @@
void close_stdin() {
int fd = unix_open(kNullFileName, O_RDONLY);
if (fd == -1) {
- fatal_errno("failed to open %s", kNullFileName);
+ PLOG(FATAL) << "failed to open " << kNullFileName;
}
if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
- fatal_errno("failed to redirect stdin to %s", kNullFileName);
+ PLOG(FATAL) << "failed to redirect stdin to " << kNullFileName;
}
unix_close(fd);
}
@@ -186,6 +186,48 @@
return line;
}
+std::string dump_header(const amessage* msg) {
+ unsigned command = msg->command;
+ int len = msg->data_length;
+ char cmd[9];
+ char arg0[12], arg1[12];
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ int b = (command >> (n * 8)) & 255;
+ if (b < 32 || b >= 127) break;
+ cmd[n] = (char)b;
+ }
+ if (n == 4) {
+ cmd[4] = 0;
+ } else {
+ // There is some non-ASCII name in the command, so dump the hexadecimal value instead
+ snprintf(cmd, sizeof cmd, "%08x", command);
+ }
+
+ if (msg->arg0 < 256U)
+ snprintf(arg0, sizeof arg0, "%d", msg->arg0);
+ else
+ snprintf(arg0, sizeof arg0, "0x%x", msg->arg0);
+
+ if (msg->arg1 < 256U)
+ snprintf(arg1, sizeof arg1, "%d", msg->arg1);
+ else
+ snprintf(arg1, sizeof arg1, "0x%x", msg->arg1);
+
+ return android::base::StringPrintf("[%s] arg0=%s arg1=%s (len=%d) ", cmd, arg0, arg1, len);
+}
+
+std::string dump_packet(const char* name, const char* func, const apacket* p) {
+ std::string result = name;
+ result += ": ";
+ result += func;
+ result += ": ";
+ result += dump_header(&p->msg);
+ result += dump_hex(p->payload.data(), p->payload.size());
+ return result;
+}
+
std::string perror_str(const char* msg) {
return android::base::StringPrintf("%s: %s", msg, strerror(errno));
}
@@ -251,6 +293,9 @@
struct passwd pwent;
struct passwd* result;
int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (pwent_max == -1) {
+ pwent_max = 16384;
+ }
std::vector<char> buf(pwent_max);
int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
if (rc == 0 && result) {
@@ -274,18 +319,6 @@
return android_dir;
}
-int syntax_error(const char* fmt, ...) {
- fprintf(stderr, "adb: usage: ");
-
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- fprintf(stderr, "\n");
- return 1;
-}
-
std::string GetLogFilePath() {
#if defined(_WIN32)
const char log_name[] = "adb.log";
@@ -295,13 +328,13 @@
DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
if (nchars >= arraysize(temp_path) || nchars == 0) {
// If string truncation or some other error.
- fatal("cannot retrieve temporary file path: %s\n",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ LOG(FATAL) << "cannot retrieve temporary file path: "
+ << android::base::SystemErrorCodeToString(GetLastError());
}
std::string temp_path_utf8;
if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
- fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+ PLOG(FATAL) << "cannot convert temporary file path from UTF-16 to UTF-8";
}
return temp_path_utf8 + log_name;
@@ -311,3 +344,33 @@
return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
#endif
}
+
+[[noreturn]] static void error_exit_va(int error, const char* fmt, va_list va) {
+ fflush(stdout);
+ fprintf(stderr, "%s: ", android::base::Basename(android::base::GetExecutablePath()).c_str());
+
+ vfprintf(stderr, fmt, va);
+
+ if (error != 0) {
+ fprintf(stderr, ": %s", strerror(error));
+ }
+
+ putc('\n', stderr);
+ fflush(stderr);
+
+ exit(EXIT_FAILURE);
+}
+
+void error_exit(const char* fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_exit_va(0, fmt, va);
+ va_end(va);
+}
+
+void perror_exit(const char* fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_exit_va(errno, fmt, va);
+ va_end(va);
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f6ce8e2..6d12225 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _ADB_UTILS_H_
-#define _ADB_UTILS_H_
+#pragma once
#include <condition_variable>
#include <mutex>
@@ -24,7 +23,7 @@
#include <android-base/macros.h>
-int syntax_error(const char*, ...) __attribute__((__format__(__printf__, 1, 2)));
+#include "adb.h"
void close_stdin();
@@ -42,12 +41,17 @@
std::string escape_arg(const std::string& s);
std::string dump_hex(const void* ptr, size_t byte_count);
+std::string dump_header(const amessage* msg);
+std::string dump_packet(const char* name, const char* func, const apacket* p);
std::string perror_str(const char* msg);
+[[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);
-extern int adb_close(int fd);
+int adb_close(int fd);
// Given forward/reverse targets, returns true if they look sane. If an error is found, fills
// |error| and returns false.
@@ -90,5 +94,3 @@
};
std::string GetLogFilePath();
-
-#endif
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
index 00c2315..e56ef5a 100755
--- a/adb/benchmark_device.py
+++ b/adb/benchmark_device.py
@@ -60,8 +60,6 @@
if device == None:
device = adb.get_device()
- lock_max(device)
-
remote_path = "/dev/null"
local_path = "/tmp/adb_benchmark_temp"
@@ -69,7 +67,7 @@
f.truncate(file_size_mb * 1024 * 1024)
speeds = list()
- for _ in range(0, 5):
+ for _ in range(0, 10):
begin = time.time()
device.push(local=local_path, remote=remote_path)
end = time.time()
@@ -81,15 +79,13 @@
if device == None:
device = adb.get_device()
- lock_max(device)
-
remote_path = "/data/local/tmp/adb_benchmark_temp"
local_path = "/tmp/adb_benchmark_temp"
device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
"count=" + str(file_size_mb)])
speeds = list()
- for _ in range(0, 5):
+ for _ in range(0, 10):
begin = time.time()
device.pull(remote=remote_path, local=local_path)
end = time.time()
@@ -101,10 +97,8 @@
if device == None:
device = adb.get_device()
- lock_max(device)
-
speeds = list()
- for _ in range(0, 5):
+ for _ in range(0, 10):
begin = time.time()
device.shell(["dd", "if=/dev/zero", "bs=1m",
"count=" + str(file_size_mb)])
@@ -114,7 +108,10 @@
analyze("shell %dMiB" % file_size_mb, speeds)
def main():
- benchmark_pull()
+ device = adb.get_device()
+ unlock(device)
+ benchmark_push(device)
+ benchmark_pull(device)
if __name__ == "__main__":
main()
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index c00d955..0008f72 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -14,42 +14,46 @@
* limitations under the License.
*/
-#define TRACE_TAG ADB
+#include "adb_install.h"
#include <stdio.h>
#include <stdlib.h>
-
-#include "adb.h"
-#include "adb_client.h"
-#include "adb_install.h"
-#include "adb_utils.h"
-#include "client/file_sync_client.h"
-#include "commandline.h"
-#include "fastdeploy.h"
-#include "sysdeps.h"
-
+#include <unistd.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/test_utils.h>
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_utils.h"
+#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "android-base/test_utils.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploy.h"
+#include "sysdeps.h"
static constexpr int kFastDeployMinApi = 24;
-static bool _use_legacy_install() {
+static bool can_use_feature(const char* feature) {
FeatureSet features;
std::string error;
if (!adb_get_feature_set(&features, &error)) {
fprintf(stderr, "error: %s\n", error.c_str());
return true;
}
- return !CanUseFeature(features, kFeatureCmd);
+ return CanUseFeature(features, feature);
+}
+
+static bool use_legacy_install() {
+ return !can_use_feature(kFeatureCmd);
+}
+
+static bool is_apex_supported() {
+ return can_use_feature(kFeatureApex);
}
static int pm_command(int argc, const char** argv) {
@@ -106,7 +110,7 @@
}
int uninstall_app(int argc, const char** argv) {
- if (_use_legacy_install()) {
+ if (use_legacy_install()) {
return uninstall_app_legacy(argc, argv);
}
return uninstall_app_streamed(argc, argv);
@@ -137,8 +141,21 @@
// The last argument must be the APK file
const char* file = argv[argc - 1];
- if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
- return syntax_error("filename doesn't end .apk: %s", file);
+ if (!android::base::EndsWithIgnoreCase(file, ".apk") &&
+ !android::base::EndsWithIgnoreCase(file, ".apex")) {
+ error_exit("filename doesn't end .apk or .apex: %s", file);
+ }
+
+ bool is_apex = false;
+ if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+ is_apex = true;
+ }
+ if (is_apex && !is_apex_supported()) {
+ error_exit(".apex is not supported on the target device");
+ }
+
+ if (is_apex && use_fastdeploy) {
+ error_exit("--fastdeploy doesn't support .apex files");
}
if (use_fastdeploy == true) {
@@ -146,41 +163,16 @@
TemporaryFile patchTmpFile;
FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
- int metadata_len = extract_metadata(file, metadataFile);
+ extract_metadata(file, metadataFile);
fclose(metadataFile);
- int result = -1;
- if (metadata_len <= 0) {
- printf("failed to extract metadata %d\n", metadata_len);
- return 1;
- } else {
- int create_patch_result = create_patch(file, metadataTmpFile.path, patchTmpFile.path);
- if (create_patch_result != 0) {
- printf("Patch creation failure, error code: %d\n", create_patch_result);
- result = create_patch_result;
- goto cleanup_streamed_apk;
- } else {
- std::vector<const char*> pm_args;
- // pass all but 1st (command) and last (apk path) parameters through to pm for
- // session creation
- for (int i = 1; i < argc - 1; i++) {
- pm_args.push_back(argv[i]);
- }
- int apply_patch_result =
- install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
- if (apply_patch_result != 0) {
- printf("Patch application failure, error code: %d\n", apply_patch_result);
- result = apply_patch_result;
- goto cleanup_streamed_apk;
- }
- }
- }
-
- cleanup_streamed_apk:
- if (use_fastdeploy == true) {
- delete_device_patch_file(file);
- }
- return result;
+ create_patch(file, metadataTmpFile.path, patchTmpFile.path);
+ // pass all but 1st (command) and last (apk path) parameters through to pm for
+ // session creation
+ std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
+ install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
+ delete_device_patch_file(file);
+ return 0;
} else {
struct stat sb;
if (stat(file, &sb) == -1) {
@@ -206,6 +198,10 @@
// do last to override any user specified value
cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+ if (is_apex) {
+ cmd += " --apex";
+ }
+
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
@@ -247,13 +243,16 @@
// All other arguments passed through verbatim.
int last_apk = -1;
for (int i = argc - 1; i >= 0; i--) {
+ if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
+ error_exit("APEX packages are only compatible with Streamed Install");
+ }
if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
last_apk = i;
break;
}
}
- if (last_apk == -1) return syntax_error("need APK file on command line");
+ if (last_apk == -1) error_exit("need APK file on command line");
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
@@ -265,29 +264,11 @@
TemporaryFile patchTmpFile;
FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
- int metadata_len = extract_metadata(apk_file[0], metadataFile);
+ extract_metadata(apk_file[0], metadataFile);
fclose(metadataFile);
- if (metadata_len <= 0) {
- printf("failed to extract metadata %d\n", metadata_len);
- return 1;
- } else {
- int create_patch_result =
- create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
- if (create_patch_result != 0) {
- printf("Patch creation failure, error code: %d\n", create_patch_result);
- result = create_patch_result;
- goto cleanup_apk;
- } else {
- int apply_patch_result =
- apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
- if (apply_patch_result != 0) {
- printf("Patch application failure, error code: %d\n", apply_patch_result);
- result = apply_patch_result;
- goto cleanup_apk;
- }
- }
- }
+ create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
+ apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
} else {
if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
}
@@ -347,19 +328,18 @@
use_localagent = true;
#endif
}
- // TODO: --installlog <filename>
}
if (installMode == INSTALL_DEFAULT) {
- if (_use_legacy_install()) {
+ if (use_legacy_install()) {
installMode = INSTALL_PUSH;
} else {
installMode = INSTALL_STREAM;
}
}
- if (installMode == INSTALL_STREAM && _use_legacy_install() == true) {
- return syntax_error("Attempting to use streaming install on unsupported deivce.");
+ if (installMode == INSTALL_STREAM && use_legacy_install() == true) {
+ error_exit("Attempting to use streaming install on unsupported device");
}
if (use_fastdeploy == true && is_reinstall == false) {
@@ -407,8 +387,13 @@
uint64_t total_size = 0;
for (int i = argc - 1; i >= 0; i--) {
const char* file = argv[i];
+ if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
+ error_exit("APEX packages are not compatible with install-multiple");
+ }
- if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+ if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+ android::base::EndsWithIgnoreCase(file, ".dm") ||
+ android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
struct stat sb;
if (stat(file, &sb) != -1) total_size += sb.st_size;
first_apk = i;
@@ -417,10 +402,10 @@
}
}
- if (first_apk == -1) return syntax_error("need APK file on command line");
+ if (first_apk == -1) error_exit("need APK file on command line");
std::string install_cmd;
- if (_use_legacy_install()) {
+ if (use_legacy_install()) {
install_cmd = "exec:pm";
} else {
install_cmd = "exec:cmd package";
@@ -470,9 +455,9 @@
}
std::string cmd =
- android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+ android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
- session_id, i, android::base::Basename(file).c_str());
+ session_id, android::base::Basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
index e9410a9..5b6c4cb 100644
--- a/adb/client/adb_install.h
+++ b/adb/client/adb_install.h
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-#ifndef ADB_INSTALL_H
-#define ADB_INSTALL_H
+#pragma once
-#include "fastdeploy.h"
+#include <string>
int install_app(int argc, const char** argv);
int install_multiple_app(int argc, const char** argv);
@@ -26,4 +25,3 @@
int delete_device_file(const std::string& filename);
int delete_host_file(const std::string& filename);
-#endif
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index fe98737..8ca44e8 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -197,7 +197,7 @@
};
int Bugreport::DoIt(int argc, const char** argv) {
- if (argc > 2) return syntax_error("adb bugreport [PATH]");
+ if (argc > 2) error_exit("usage: adb bugreport [PATH]");
// Gets bugreportz version.
std::string bugz_stdout, bugz_stderr;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index e38e305..b5bed28 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -63,7 +63,6 @@
#include "services.h"
#include "shell_protocol.h"
#include "sysdeps/chrono.h"
-#include "sysdeps/memory.h"
extern int gListenAll;
@@ -72,8 +71,7 @@
static std::string product_file(const std::string& file) {
const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
if (ANDROID_PRODUCT_OUT == nullptr) {
- fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
- exit(1);
+ error_exit("product directory not specified; set $ANDROID_PRODUCT_OUT");
}
return std::string{ANDROID_PRODUCT_OUT} + OS_PATH_SEPARATOR_STR + file;
}
@@ -206,8 +204,8 @@
" sideload OTAPACKAGE sideload the given full OTA package\n"
" root restart adbd with root permissions\n"
" unroot restart adbd without root permissions\n"
- " usb restart adb server listening on USB\n"
- " tcpip PORT restart adb server listening on TCP on PORT\n"
+ " usb restart adbd listening on USB\n"
+ " tcpip PORT restart adbd listening on TCP on PORT\n"
"\n"
"internal debugging:\n"
" start-server ensure that there is a server running\n"
@@ -319,7 +317,7 @@
#ifdef _WIN32
old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
if (old_stdin_mode == -1) {
- fatal_errno("could not set stdin to binary");
+ PLOG(FATAL) << "could not set stdin to binary";
}
#endif
}
@@ -328,7 +326,7 @@
if (outFd == STDOUT_FILENO) {
old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
if (old_stdout_mode == -1) {
- fatal_errno("could not set stdout to binary");
+ PLOG(FATAL) << "could not set stdout to binary";
}
}
#endif
@@ -339,7 +337,7 @@
stdin_raw_restore();
#ifdef _WIN32
if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
- fatal_errno("could not restore stdin mode");
+ PLOG(FATAL) << "could not restore stdin mode";
}
#endif
}
@@ -347,7 +345,7 @@
#ifdef _WIN32
if (outFd == STDOUT_FILENO) {
if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
- fatal_errno("could not restore stdout mode");
+ PLOG(FATAL) << "could not restore stdout mode";
}
}
#endif
@@ -661,9 +659,9 @@
static int adb_shell(int argc, const char** argv) {
FeatureSet features;
- std::string error;
- if (!adb_get_feature_set(&features, &error)) {
- fprintf(stderr, "error: %s\n", error.c_str());
+ std::string error_message;
+ if (!adb_get_feature_set(&features, &error_message)) {
+ fprintf(stderr, "error: %s\n", error_message.c_str());
return 1;
}
@@ -686,7 +684,7 @@
switch (opt) {
case 'e':
if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
- return syntax_error("-e requires a single-character argument or 'none'");
+ error_exit("-e requires a single-character argument or 'none'");
}
escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
break;
@@ -934,31 +932,29 @@
*/
static int ppp(int argc, const char** argv) {
#if defined(_WIN32)
- fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
- return -1;
+ error_exit("adb %s not implemented on Win32", argv[0]);
+ __builtin_unreachable();
#else
- if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
+ if (argc < 2) error_exit("usage: adb %s <adb service name> [ppp opts]", argv[0]);
const char* adb_service_name = argv[1];
- std::string error;
- int fd = adb_connect(adb_service_name, &error);
+ std::string error_message;
+ int fd = adb_connect(adb_service_name, &error_message);
if (fd < 0) {
- fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
- return 1;
+ error_exit("could not open adb service %s: %s", adb_service_name, error_message.c_str());
}
pid_t pid = fork();
+ if (pid == -1) {
+ perror_exit("fork failed");
+ }
- if (pid < 0) {
- perror("from fork()");
- return 1;
- } else if (pid == 0) {
- int err;
+ if (pid == 0) {
+ // child side
int i;
- const char **ppp_args;
// copy args
- ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+ const char** ppp_args = (const char**)alloca(sizeof(char*) * argc + 1);
ppp_args[0] = "pppd";
for (i = 2 ; i < argc ; i++) {
//argv[2] and beyond become ppp_args[1] and beyond
@@ -966,25 +962,18 @@
}
ppp_args[i-1] = nullptr;
- // child side
-
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
adb_close(STDERR_FILENO);
adb_close(fd);
- err = execvp("pppd", (char * const *)ppp_args);
-
- if (err < 0) {
- perror("execing pppd");
- }
- exit(-1);
- } else {
- // parent side
-
- adb_close(fd);
- return 0;
+ execvp("pppd", (char* const*)ppp_args);
+ perror_exit("exec pppd failed");
}
+
+ // parent side
+ adb_close(fd);
+ return 0;
#endif /* !defined(_WIN32) */
}
@@ -1162,7 +1151,7 @@
/* find, extract, and use any -f argument */
for (int i = 1; i < argc; i++) {
if (!strcmp("-f", argv[i])) {
- if (i == argc - 1) return syntax_error("backup -f passed with no filename");
+ if (i == argc - 1) error_exit("backup -f passed with no filename");
filename = argv[i+1];
for (int j = i+2; j <= argc; ) {
argv[i++] = argv[j++];
@@ -1174,7 +1163,7 @@
// Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
// a list of packages is required.
- if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
+ if (argc < 2) error_exit("backup either needs a list of packages or -all/-shared");
adb_unlink(filename);
int outFd = adb_creat(filename, 0640);
@@ -1210,7 +1199,7 @@
}
static int restore(int argc, const char** argv) {
- if (argc != 2) return syntax_error("restore requires an argument");
+ if (argc != 2) error_exit("restore requires an argument");
const char* filename = argv[1];
int tarFd = adb_open(filename, O_RDONLY);
@@ -1264,8 +1253,7 @@
} else if (!strcmp(*arg, "--")) {
ignore_flags = true;
} else {
- syntax_error("unrecognized option '%s'", *arg);
- exit(1);
+ error_exit("unrecognized option '%s'", *arg);
}
}
++arg;
@@ -1343,7 +1331,7 @@
/* this is a special flag used only when the ADB client launches the ADB Server */
is_daemon = true;
} else if (!strcmp(argv[0], "--reply-fd")) {
- if (argc < 2) return syntax_error("--reply-fd requires an argument");
+ if (argc < 2) error_exit("--reply-fd requires an argument");
const char* reply_fd_str = argv[1];
argc--;
argv++;
@@ -1356,7 +1344,7 @@
if (isdigit(argv[0][2])) {
serial = argv[0] + 2;
} else {
- if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
+ if (argc < 2 || argv[0][2] != '\0') error_exit("-s requires an argument");
serial = argv[1];
argc--;
argv++;
@@ -1372,7 +1360,7 @@
}
transport_id = strtoll(id, const_cast<char**>(&id), 10);
if (*id != '\0') {
- return syntax_error("invalid transport id");
+ error_exit("invalid transport id");
}
} else if (!strcmp(argv[0],"-d")) {
transport_type = kTransportUsb;
@@ -1382,7 +1370,7 @@
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return syntax_error("-H requires an argument");
+ if (argc < 2) error_exit("-H requires an argument");
server_host_str = argv[1];
argc--;
argv++;
@@ -1391,7 +1379,7 @@
}
} else if (!strncmp(argv[0], "-P", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return syntax_error("-P requires an argument");
+ if (argc < 2) error_exit("-P requires an argument");
server_port_str = argv[1];
argc--;
argv++;
@@ -1399,7 +1387,7 @@
server_port_str = argv[0] + 2;
}
} else if (!strcmp(argv[0], "-L")) {
- if (argc < 2) return syntax_error("-L requires an argument");
+ if (argc < 2) error_exit("-L requires an argument");
server_socket_str = argv[1];
argc--;
argv++;
@@ -1412,7 +1400,7 @@
}
if ((server_host_str || server_port_str) && server_socket_str) {
- return syntax_error("-L is incompatible with -H or -P");
+ error_exit("-L is incompatible with -H or -P");
}
// If -L, -H, or -P are specified, ignore environment variables.
@@ -1429,11 +1417,10 @@
server_port_str = server_port_str ? server_port_str : getenv("ANDROID_ADB_SERVER_PORT");
if (server_port_str && strlen(server_port_str) > 0) {
if (!android::base::ParseInt(server_port_str, &server_port, 1, 65535)) {
- fprintf(stderr,
- "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive"
- " number less than 65535. Got \"%s\"\n",
+ error_exit(
+ "$ANDROID_ADB_SERVER_PORT must be a positive number less than 65535: "
+ "got \"%s\"",
server_port_str);
- exit(1);
}
}
@@ -1445,7 +1432,7 @@
rc = asprintf(&temp, "tcp:%d", server_port);
}
if (rc < 0) {
- fatal("failed to allocate server socket specification");
+ LOG(FATAL) << "failed to allocate server socket specification";
}
server_socket_str = temp;
}
@@ -1507,7 +1494,7 @@
} else if (argc == 2 && !strcmp(argv[1], "-l")) {
listopt = argv[1];
} else {
- return syntax_error("adb devices [-l]");
+ error_exit("adb devices [-l]");
}
std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1515,13 +1502,13 @@
return adb_query_command(query);
}
else if (!strcmp(argv[0], "connect")) {
- if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
+ if (argc != 2) error_exit("usage: adb connect <host>[:<port>]");
std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
return adb_query_command(query);
}
else if (!strcmp(argv[0], "disconnect")) {
- if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
+ if (argc > 2) error_exit("usage: adb disconnect [<host>[:<port>]]");
std::string query = android::base::StringPrintf("host:disconnect:%s",
(argc == 2) ? argv[1] : "");
@@ -1536,7 +1523,7 @@
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
- if (argc < 2) return syntax_error("adb %s command", argv[0]);
+ if (argc < 2) error_exit("usage: adb %s command", argv[0]);
std::string cmd = "exec:";
cmd += argv[1];
@@ -1566,29 +1553,34 @@
return adb_kill_server() ? 0 : 1;
}
else if (!strcmp(argv[0], "sideload")) {
- if (argc != 2) return syntax_error("sideload requires an argument");
+ if (argc != 2) error_exit("sideload requires an argument");
if (adb_sideload_host(argv[1])) {
return 1;
} else {
return 0;
}
} else if (!strcmp(argv[0], "tcpip")) {
- if (argc != 2) return syntax_error("tcpip requires an argument");
+ if (argc != 2) error_exit("tcpip requires an argument");
int port;
if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
- return syntax_error("tcpip: invalid port: %s", argv[1]);
+ error_exit("tcpip: invalid port: %s", argv[1]);
}
return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
}
+ // clang-format off
else if (!strcmp(argv[0], "remount") ||
!strcmp(argv[0], "reboot") ||
!strcmp(argv[0], "reboot-bootloader") ||
+ !strcmp(argv[0], "reboot-fastboot") ||
!strcmp(argv[0], "usb") ||
!strcmp(argv[0], "disable-verity") ||
!strcmp(argv[0], "enable-verity")) {
+ // clang-format on
std::string command;
if (!strcmp(argv[0], "reboot-bootloader")) {
command = "reboot:bootloader";
+ } else if (!strcmp(argv[0], "reboot-fastboot")) {
+ command = "reboot:fastboot";
} else if (argc > 1) {
command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
} else {
@@ -1603,7 +1595,7 @@
} else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
bool reverse = !strcmp(argv[0], "reverse");
--argc;
- if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
+ if (argc < 1) error_exit("%s requires an argument", argv[0]);
++argv;
// Determine the <host-prefix> for this command.
@@ -1622,46 +1614,44 @@
}
}
- std::string cmd, error;
+ std::string cmd, error_message;
if (strcmp(argv[0], "--list") == 0) {
- if (argc != 1) return syntax_error("--list doesn't take any arguments");
+ if (argc != 1) error_exit("--list doesn't take any arguments");
return adb_query_command(host_prefix + ":list-forward");
} else if (strcmp(argv[0], "--remove-all") == 0) {
- if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
+ if (argc != 1) error_exit("--remove-all doesn't take any arguments");
cmd = host_prefix + ":killforward-all";
} else if (strcmp(argv[0], "--remove") == 0) {
// forward --remove <local>
- if (argc != 2) return syntax_error("--remove requires an argument");
+ if (argc != 2) error_exit("--remove requires an argument");
cmd = host_prefix + ":killforward:" + argv[1];
} else if (strcmp(argv[0], "--no-rebind") == 0) {
// forward --no-rebind <local> <remote>
- if (argc != 3) return syntax_error("--no-rebind takes two arguments");
- if (forward_targets_are_valid(argv[1], argv[2], &error)) {
+ if (argc != 3) error_exit("--no-rebind takes two arguments");
+ if (forward_targets_are_valid(argv[1], argv[2], &error_message)) {
cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
}
} else {
// forward <local> <remote>
- if (argc != 2) return syntax_error("forward takes two arguments");
- if (forward_targets_are_valid(argv[0], argv[1], &error)) {
+ if (argc != 2) error_exit("forward takes two arguments");
+ if (forward_targets_are_valid(argv[0], argv[1], &error_message)) {
cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
}
}
- if (!error.empty()) {
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
+ if (!error_message.empty()) {
+ error_exit("error: %s", error_message.c_str());
}
- int fd = adb_connect(cmd, &error);
- if (fd < 0 || !adb_status(fd, &error)) {
+ int fd = adb_connect(cmd, &error_message);
+ if (fd < 0 || !adb_status(fd, &error_message)) {
adb_close(fd);
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
+ error_exit("error: %s", error_message.c_str());
}
// Server or device may optionally return a resolved TCP port number.
std::string resolved_port;
- if (ReadProtocolString(fd, &resolved_port, &error) && !resolved_port.empty()) {
+ if (ReadProtocolString(fd, &resolved_port, &error_message) && !resolved_port.empty()) {
printf("%s\n", resolved_port.c_str());
}
@@ -1670,7 +1660,7 @@
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
- if (argc != 2) return syntax_error("ls requires an argument");
+ if (argc != 2) error_exit("ls requires an argument");
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
@@ -1680,7 +1670,7 @@
const char* dst = nullptr;
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
- if (srcs.empty() || !dst) return syntax_error("push requires an argument");
+ if (srcs.empty() || !dst) error_exit("push requires an argument");
return do_sync_push(srcs, dst, sync) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
@@ -1689,19 +1679,19 @@
const char* dst = ".";
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
- if (srcs.empty()) return syntax_error("pull requires an argument");
+ if (srcs.empty()) error_exit("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
- if (argc < 2) return syntax_error("install requires an argument");
+ if (argc < 2) error_exit("install requires an argument");
return install_app(argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
- if (argc < 2) return syntax_error("install-multiple requires an argument");
+ if (argc < 2) error_exit("install-multiple requires an argument");
return install_multiple_app(argc, argv);
}
else if (!strcmp(argv[0], "uninstall")) {
- if (argc < 2) return syntax_error("uninstall requires an argument");
+ if (argc < 2) error_exit("uninstall requires an argument");
return uninstall_app(argc, argv);
}
else if (!strcmp(argv[0], "sync")) {
@@ -1715,7 +1705,7 @@
} else if (argc == 2) {
src = argv[1];
} else {
- return syntax_error("adb sync [-l] [PARTITION]");
+ error_exit("usage: adb sync [-l] [PARTITION]");
}
if (src.empty()) src = "all";
@@ -1730,7 +1720,8 @@
if (!do_sync_sync(src_dir, "/" + partition, list_only)) return 1;
}
}
- return found ? 0 : syntax_error("don't know how to sync %s partition", src.c_str());
+ if (!found) error_exit("don't know how to sync %s partition", src.c_str());
+ return 0;
}
/* passthrough commands */
else if (!strcmp(argv[0],"get-state") ||
@@ -1761,7 +1752,7 @@
return restore(argc, argv);
}
else if (!strcmp(argv[0], "keygen")) {
- if (argc != 2) return syntax_error("keygen requires an argument");
+ if (argc != 2) error_exit("keygen requires an argument");
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
@@ -1776,7 +1767,7 @@
return adb_connect_command("host:track-devices");
} else if (!strcmp(argv[0], "raw")) {
if (argc != 2) {
- return syntax_error("adb raw SERVICE");
+ error_exit("usage: adb raw SERVICE");
}
return adb_connect_command(argv[1]);
}
@@ -1819,11 +1810,11 @@
std::string err;
return adb_query_command("host:reconnect-offline");
} else {
- return syntax_error("adb reconnect [device|offline]");
+ error_exit("usage: adb reconnect [device|offline]");
}
}
}
- syntax_error("unknown command %s", argv[0]);
- return 1;
+ error_exit("unknown command %s", argv[0]);
+ __builtin_unreachable();
}
diff --git a/adb/client/console.cpp b/adb/client/console.cpp
index 9563eac..4e8a3f8 100644
--- a/adb/client/console.cpp
+++ b/adb/client/console.cpp
@@ -135,6 +135,7 @@
// preventing the emulator from reading the command that adb has sent.
// https://code.google.com/p/android/issues/detail?id=21021
int result;
+ std::string emulator_output;
do {
char buf[BUFSIZ];
result = adb_read(fd, buf, sizeof(buf));
@@ -146,8 +147,37 @@
// appended above, and that causes the emulator to close the socket
// which should cause zero bytes (orderly/graceful shutdown) to be
// returned.
+ if (result > 0) emulator_output.append(buf, result);
} while (result > 0);
+ // Note: the following messages are expected to be quite stable from emulator.
+ //
+ // Emulator console will send the following message upon connection:
+ //
+ // Android Console: Authentication required
+ // Android Console: type 'auth <auth_token>' to authenticate
+ // Android Console: you can find your <auth_token> in
+ // '/<path-to-home>/.emulator_console_auth_token'
+ // OK\r\n
+ //
+ // and the following after authentication:
+ // Android Console: type 'help' for a list of commands
+ // OK\r\n
+ //
+ // So try search and skip first two "OK\r\n", print the rest.
+ //
+ const std::string delims = "OK\r\n";
+ size_t found = 0;
+ for (int i = 0; i < 2; ++i) {
+ const size_t result = emulator_output.find(delims, found);
+ if (result == std::string::npos) {
+ break;
+ } else {
+ found = result + delims.size();
+ }
+ }
+
+ printf("%s", emulator_output.c_str() + found);
adb_close(fd);
return 0;
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index d3f35c8..45f3cca 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-#include <libgen.h>
+#include "fastdeploy.h"
+
#include <algorithm>
#include <array>
+#include <memory>
#include "android-base/file.h"
#include "android-base/strings.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/ZipFileRO.h"
#include "client/file_sync_client.h"
#include "commandline.h"
-#include "fastdeploy.h"
#include "fastdeploycallbacks.h"
-#include "utils/String16.h"
+#include "sysdeps.h"
+
+#include "adb_utils.h"
static constexpr long kRequiredAgentVersion = 0x00000001;
@@ -69,13 +74,14 @@
static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
std::string adb_dir = android::base::GetExecutableDirectory();
if (adb_dir.empty()) {
- fatal("Could not determine location of adb!");
+ error_exit("Could not determine location of adb!");
}
if (g_use_localagent) {
const char* product_out = getenv("ANDROID_PRODUCT_OUT");
if (product_out == nullptr) {
- fatal("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined", local_path);
+ error_exit("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined",
+ local_path);
}
return android::base::StringPrintf("%s%s", product_out, local_path);
} else {
@@ -100,10 +106,10 @@
android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
int ret = send_shell_command(chmodCommand);
if (ret != 0) {
- fatal("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+ error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
}
} else {
- fatal("Error pushing agent files to device");
+ error_exit("Error pushing agent files to device");
}
return true;
@@ -133,65 +139,89 @@
agent_version = get_agent_version();
if (agent_version != kRequiredAgentVersion) {
- fatal("After update agent version remains incorrect! Expected %ld but version is %ld",
- kRequiredAgentVersion, agent_version);
+ error_exit("After update agent version remains incorrect! Expected %ld but version is %ld",
+ kRequiredAgentVersion, agent_version);
}
}
-static std::string get_aapt2_path() {
- if (g_use_localagent) {
- // This should never happen on a Windows machine
- const char* host_out = getenv("ANDROID_HOST_OUT");
- if (host_out == nullptr) {
- fatal("Could not locate aapt2 because $ANDROID_HOST_OUT is not defined");
- }
- return android::base::StringPrintf("%s/bin/aapt2", host_out);
+static std::string get_string_from_utf16(const char16_t* input, int input_len) {
+ ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
+ if (utf8_length <= 0) {
+ return {};
}
-
- std::string adb_dir = android::base::GetExecutableDirectory();
- if (adb_dir.empty()) {
- fatal("Could not locate aapt2");
- }
- return adb_dir + "/aapt2";
+ std::string utf8;
+ utf8.resize(utf8_length);
+ utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
+ return utf8;
}
-static int system_capture(const char* cmd, std::string& output) {
- FILE* pipe = popen(cmd, "re");
- int fd = -1;
-
- if (pipe != nullptr) {
- fd = fileno(pipe);
- }
-
- if (fd == -1) {
- fatal_errno("Could not create pipe for process '%s'", cmd);
- }
-
- if (!android::base::ReadFdToString(fd, &output)) {
- fatal_errno("Error reading from process '%s'", cmd);
- }
-
- return pclose(pipe);
-}
-
-// output is required to point to a valid output string (non-null)
static std::string get_packagename_from_apk(const char* apkPath) {
- const char* kAapt2DumpNameCommandPattern = R"(%s dump packagename "%s")";
- std::string aapt2_path_string = get_aapt2_path();
- std::string getPackagenameCommand = android::base::StringPrintf(
- kAapt2DumpNameCommandPattern, aapt2_path_string.c_str(), apkPath);
-
- std::string package_name;
- int exit_code = system_capture(getPackagenameCommand.c_str(), package_name);
- if (exit_code != 0) {
- fatal("Error executing '%s' exitcode: %d", getPackagenameCommand.c_str(), exit_code);
+#undef open
+ std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+#define open ___xxx_unix_open
+ if (zipFile == nullptr) {
+ perror_exit("Could not open %s", apkPath);
}
-
- // strip any line end characters from the output
- return android::base::Trim(package_name);
+ android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
+ if (entry == nullptr) {
+ error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
+ }
+ uint32_t manifest_len = 0;
+ if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
+ error_exit("Could not read AndroidManifest.xml inside %s", apkPath);
+ }
+ std::vector<char> manifest_data(manifest_len);
+ if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
+ error_exit("Could not uncompress AndroidManifest.xml inside %s", apkPath);
+ }
+ android::ResXMLTree tree;
+ android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
+ if (setto_status != android::OK) {
+ error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
+ }
+ android::ResXMLParser::event_code_t code;
+ while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
+ code != android::ResXMLParser::END_DOCUMENT) {
+ switch (code) {
+ case android::ResXMLParser::START_TAG: {
+ size_t element_name_length;
+ const char16_t* element_name = tree.getElementName(&element_name_length);
+ if (element_name == nullptr) {
+ continue;
+ }
+ std::u16string element_name_string(element_name, element_name_length);
+ if (element_name_string == u"manifest") {
+ for (size_t i = 0; i < tree.getAttributeCount(); i++) {
+ size_t attribute_name_length;
+ const char16_t* attribute_name_text =
+ tree.getAttributeName(i, &attribute_name_length);
+ if (attribute_name_text == nullptr) {
+ continue;
+ }
+ std::u16string attribute_name_string(attribute_name_text,
+ attribute_name_length);
+ if (attribute_name_string == u"package") {
+ size_t attribute_value_length;
+ const char16_t* attribute_value_text =
+ tree.getAttributeStringValue(i, &attribute_value_length);
+ if (attribute_value_text == nullptr) {
+ continue;
+ }
+ return get_string_from_utf16(attribute_value_text,
+ attribute_value_length);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
}
-int extract_metadata(const char* apkPath, FILE* outputFp) {
+void extract_metadata(const char* apkPath, FILE* outputFp) {
std::string packageName = get_packagename_from_apk(apkPath);
const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
std::string extractCommand =
@@ -200,13 +230,10 @@
std::vector<char> extractErrorBuffer;
int statusCode;
DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
- int ret = send_shell_command(extractCommand, false, &cb);
-
- if (ret == 0) {
- return cb.getBytesWritten();
+ int returnCode = send_shell_command(extractCommand, false, &cb);
+ if (returnCode != 0) {
+ error_exit("Executing %s returned %d", extractCommand.c_str(), returnCode);
}
-
- return ret;
}
static std::string get_patch_generator_command() {
@@ -214,8 +241,9 @@
// This should never happen on a Windows machine
const char* host_out = getenv("ANDROID_HOST_OUT");
if (host_out == nullptr) {
- fatal("Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT is not "
- "defined");
+ error_exit(
+ "Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT "
+ "is not defined");
}
return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
host_out);
@@ -223,17 +251,20 @@
std::string adb_dir = android::base::GetExecutableDirectory();
if (adb_dir.empty()) {
- fatal("Could not locate deploypatchgenerator.jar");
+ error_exit("Could not locate deploypatchgenerator.jar");
}
return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
adb_dir.c_str());
}
-int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
+void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
std::string generatePatchCommand = android::base::StringPrintf(
R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
patchPath);
- return system(generatePatchCommand.c_str());
+ int returnCode = system(generatePatchCommand.c_str());
+ if (returnCode != 0) {
+ error_exit("Executing %s returned %d", generatePatchCommand.c_str(), returnCode);
+ }
}
std::string get_patch_path(const char* apkPath) {
@@ -243,36 +274,38 @@
return patchDevicePath;
}
-int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
+void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
std::string packageName = get_packagename_from_apk(apkPath);
std::string patchDevicePath = get_patch_path(apkPath);
std::vector<const char*> srcs = {patchPath};
bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
-
if (!push_ok) {
- return -1;
+ error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
}
std::string applyPatchCommand =
android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
patchDevicePath.c_str(), outputPath);
- return send_shell_command(applyPatchCommand);
+ int returnCode = send_shell_command(applyPatchCommand);
+ if (returnCode != 0) {
+ error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+ }
}
-int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
+void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
std::string packageName = get_packagename_from_apk(apkPath);
- std::vector<const char*> srcs;
+
std::string patchDevicePath =
android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
- srcs.push_back(patchPath);
- bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
+ std::vector<const char*> srcs{patchPath};
+ bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
if (!push_ok) {
- return -1;
+ error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
}
std::vector<unsigned char> applyOutputBuffer;
@@ -287,5 +320,8 @@
std::string applyPatchCommand =
android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
patchDevicePath.c_str(), argsString.c_str());
- return send_shell_command(applyPatchCommand);
+ int returnCode = send_shell_command(applyPatchCommand);
+ if (returnCode != 0) {
+ error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+ }
}
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index e5e7663..a6b10d3 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -16,7 +16,7 @@
#pragma once
-#include "adb.h"
+#include <string>
enum FastDeploy_AgentUpdateStrategy {
FastDeploy_AgentUpdateAlways,
@@ -27,8 +27,8 @@
void fastdeploy_set_local_agent(bool use_localagent);
int get_device_api_level();
void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy);
-int extract_metadata(const char* apkPath, FILE* outputFp);
-int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
-int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
-int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
+void extract_metadata(const char* apkPath, FILE* outputFp);
+void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
+void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
+void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
std::string get_patch_path(const char* apkPath);
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index cb9bcfa..f0f9a80 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -325,12 +325,12 @@
memset(st, 0, sizeof(*st));
if (have_stat_v2_) {
if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
- fatal_errno("protocol fault: failed to read stat response");
+ PLOG(FATAL) << "protocol fault: failed to read stat response";
}
if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
- fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
- msg.stat_v2.id);
+ PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+ << msg.stat_v2.id;
}
if (msg.stat_v2.error != 0) {
@@ -351,12 +351,12 @@
return true;
} else {
if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
- fatal_errno("protocol fault: failed to read stat response");
+ PLOG(FATAL) << "protocol fault: failed to read stat response";
}
if (msg.stat_v1.id != ID_LSTAT_V1) {
- fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
- msg.stat_v1.id);
+ PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+ << msg.stat_v1.id;
}
if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
@@ -894,7 +894,8 @@
//
// TODO(b/25457350): We don't preserve permissions on directories.
// TODO: Find all of the leaves and `mkdir -p` them instead?
- if (CanUseFeature(sc.Features(), kFeatureShell2)) {
+ if (!CanUseFeature(sc.Features(), kFeatureFixedPushMkdir) &&
+ CanUseFeature(sc.Features(), kFeatureShell2)) {
SilentStandardStreamsCallbackInterface cb;
std::string cmd = "mkdir";
for (const auto& dir : directory_list) {
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index a7e454d..fb581a6 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -42,13 +42,13 @@
const std::string log_file_path(GetLogFilePath());
int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
if (fd == -1) {
- fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
+ PLOG(FATAL) << "cannot open " << log_file_path;
}
if (dup2(fd, STDOUT_FILENO) == -1) {
- fatal("cannot redirect stdout: %s", strerror(errno));
+ PLOG(FATAL) << "cannot redirect stdout";
}
if (dup2(fd, STDERR_FILENO) == -1) {
- fatal("cannot redirect stderr: %s", strerror(errno));
+ PLOG(FATAL) << "cannot redirect stderr";
}
unix_close(fd);
@@ -81,10 +81,10 @@
// This also keeps stderr unbuffered when it is redirected to adb.log.
if (is_daemon) {
if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
- fatal("cannot make stdout unbuffered: %s", strerror(errno));
+ PLOG(FATAL) << "cannot make stdout unbuffered";
}
if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
- fatal("cannot make stderr unbuffered: %s", strerror(errno));
+ PLOG(FATAL) << "cannot make stderr unbuffered";
}
}
@@ -137,7 +137,7 @@
while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
INSTALL_STATUS_OK) {
if (std::chrono::steady_clock::now() - start > 0.5s) {
- fatal("could not install *smartsocket* listener: %s", error.c_str());
+ LOG(FATAL) << "could not install *smartsocket* listener: " << error;
}
std::this_thread::sleep_for(100ms);
@@ -153,7 +153,7 @@
// setsid will fail with EPERM if it's already been a lead process of new session.
// Ignore such error.
if (setsid() == -1 && errno != EPERM) {
- fatal("setsid() failed: %s", strerror(errno));
+ PLOG(FATAL) << "setsid() failed";
}
#endif
@@ -171,19 +171,19 @@
const DWORD bytes_to_write = arraysize(ack) - 1;
DWORD written = 0;
if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
- fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ LOG(FATAL) << "cannot write ACK to handle " << ack_reply_handle
+ << android::base::SystemErrorCodeToString(GetLastError());
}
if (written != bytes_to_write) {
- fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
- written);
+ LOG(FATAL) << "cannot write " << bytes_to_write << " bytes of ACK: only wrote "
+ << written << " bytes";
}
CloseHandle(ack_reply_handle);
#else
// TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
// "OKAY".
if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
- fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+ PLOG(FATAL) << "error writing ACK to fd " << ack_reply_fd;
}
unix_close(ack_reply_fd);
#endif
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 00aeb9b..cfa5cf4 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -212,8 +212,8 @@
const HINSTANCE instance = GetModuleHandleW(nullptr);
if (!instance) {
// This is such a common API call that this should never fail.
- fatal("GetModuleHandleW failed: %s",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ LOG(FATAL) << "GetModuleHandleW failed: "
+ << android::base::SystemErrorCodeToString(GetLastError());
}
WNDCLASSEXW wndclass;
@@ -223,15 +223,15 @@
wndclass.hInstance = instance;
wndclass.lpszClassName = kPowerNotificationWindowClassName;
if (!RegisterClassExW(&wndclass)) {
- fatal("RegisterClassExW failed: %s",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ LOG(FATAL) << "RegisterClassExW failed: "
+ << android::base::SystemErrorCodeToString(GetLastError());
}
if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
instance, nullptr)) {
- fatal("CreateWindowExW failed: %s",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ LOG(FATAL) << "CreateWindowExW failed: "
+ << android::base::SystemErrorCodeToString(GetLastError());
}
MSG msg;
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 8c39a20..d55096a 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -32,6 +32,10 @@
#include <unistd.h>
#include <utime.h>
+#include <memory>
+#include <string>
+#include <vector>
+
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -47,6 +51,7 @@
#include "security_log_tags.h"
#include "sysdeps/errno.h"
+using android::base::Dirname;
using android::base::StringPrintf;
static bool should_use_fs_config(const std::string& path) {
@@ -219,7 +224,7 @@
}
if (fd < 0 && errno == ENOENT) {
- if (!secure_mkdirs(android::base::Dirname(path))) {
+ if (!secure_mkdirs(Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
goto fail;
}
@@ -327,8 +332,6 @@
#else
static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
syncmsg msg;
- unsigned int len;
- int ret;
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -337,24 +340,28 @@
return false;
}
- len = msg.data.size;
+ unsigned int len = msg.data.size;
if (len > buffer.size()) { // TODO: resize buffer?
SendSyncFail(s, "oversize data message");
return false;
}
if (!ReadFdExactly(s, &buffer[0], len)) return false;
- ret = symlink(&buffer[0], path.c_str());
- if (ret && errno == ENOENT) {
- if (!secure_mkdirs(android::base::Dirname(path))) {
- SendSyncFailErrno(s, "secure_mkdirs failed");
+ std::string buf_link;
+ if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
+ adb_unlink(path.c_str());
+ auto ret = symlink(&buffer[0], path.c_str());
+ if (ret && errno == ENOENT) {
+ if (!secure_mkdirs(Dirname(path))) {
+ SendSyncFailErrno(s, "secure_mkdirs failed");
+ return false;
+ }
+ ret = symlink(&buffer[0], path.c_str());
+ }
+ if (ret) {
+ SendSyncFailErrno(s, "symlink failed");
return false;
}
- ret = symlink(&buffer[0], path.c_str());
- }
- if (ret) {
- SendSyncFailErrno(s, "symlink failed");
- return false;
}
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -391,7 +398,8 @@
// Don't delete files before copying if they are not "regular" or symlinks.
struct stat st;
- bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+ bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
+ (S_ISLNK(st.st_mode) && !S_ISLNK(mode));
if (do_unlink) {
adb_unlink(path.c_str());
}
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 8d28c49..2a6418a 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -32,6 +32,7 @@
#include "adb.h"
#include "adb_io.h"
+#include "adb_utils.h"
#include "fdevent.h"
/* TODO:
@@ -78,7 +79,7 @@
const char* command = "screencap";
const char *args[2] = {command, nullptr};
execvp(command, (char**)args);
- exit(1);
+ perror_exit("exec screencap failed");
}
adb_close(fds[1]);
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index 78e4cd5..3213f69 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -62,3 +62,5 @@
};
usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
+bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out,
+ android::base::unique_fd* bulk_in);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index b40faee..1363976 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -136,7 +136,7 @@
this->fde = fdevent_create(socket, jdwp_process_event, this);
if (!this->fde) {
- fatal("could not create fdevent for new JDWP process");
+ LOG(FATAL) << "could not create fdevent for new JDWP process";
}
/* start by waiting for the PID */
@@ -200,7 +200,7 @@
// Message is length-prefixed with 4 hex digits in ASCII.
static constexpr size_t header_len = 4;
if (bufferlen < header_len) {
- fatal("invalid JDWP process list buffer size: %zu", bufferlen);
+ LOG(FATAL) << "invalid JDWP process list buffer size: " << bufferlen;
}
char head[header_len + 1];
@@ -212,6 +212,7 @@
static void jdwp_process_event(int socket, unsigned events, void* _proc) {
JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
+ CHECK_EQ(socket, proc->socket);
if (events & FDE_READ) {
if (proc->pid < 0) {
@@ -225,82 +226,50 @@
D("Adding pid %d to jdwp process list", proc->pid);
jdwp_process_list_updated();
} else {
- /* the pid was read, if we get there it's probably because the connection
- * was closed (e.g. the JDWP process exited or crashed) */
- char buf[32];
-
- while (true) {
- int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
-
- if (len == 0) {
- D("terminating JDWP %d connection: EOF", proc->pid);
- break;
- } else if (len < 0) {
- if (len < 0 && errno == EAGAIN) {
- return;
- }
-
- D("terminating JDWP %d connection: EOF", proc->pid);
- break;
- } else {
- D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
- len);
- }
- }
-
+ // We already have the PID, if we can read from the socket, we've probably hit EOF.
+ D("terminating JDWP connection %d", proc->pid);
goto CloseProcess;
}
}
if (events & FDE_WRITE) {
D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
- if (!proc->out_fds.empty()) {
- int fd = proc->out_fds.back().get();
- struct cmsghdr* cmsg;
- struct msghdr msg;
- struct iovec iov;
- char dummy = '!';
- char buffer[sizeof(struct cmsghdr) + sizeof(int)];
+ CHECK(!proc->out_fds.empty());
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = buffer;
- msg.msg_controllen = sizeof(buffer);
+ int fd = proc->out_fds.back().get();
+ struct cmsghdr* cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ char dummy = '!';
+ char buffer[sizeof(struct cmsghdr) + sizeof(int)];
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = msg.msg_controllen;
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)CMSG_DATA(cmsg))[0] = fd;
+ iov.iov_base = &dummy;
+ iov.iov_len = 1;
+ msg.msg_name = nullptr;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = buffer;
+ msg.msg_controllen = sizeof(buffer);
- if (!set_file_block_mode(proc->socket, true)) {
- VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
- goto CloseProcess;
- }
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = msg.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ ((int*)CMSG_DATA(cmsg))[0] = fd;
- int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
- if (ret < 0) {
- D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
- goto CloseProcess;
- }
+ int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
+ if (ret < 0) {
+ D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
+ goto CloseProcess;
+ }
- D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
+ D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
- proc->out_fds.pop_back();
-
- if (!set_file_block_mode(proc->socket, false)) {
- VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
- goto CloseProcess;
- }
-
- if (proc->out_fds.empty()) {
- fdevent_del(proc->fde, FDE_WRITE);
- }
+ proc->out_fds.pop_back();
+ if (proc->out_fds.empty()) {
+ fdevent_del(proc->fde, FDE_WRITE);
}
}
@@ -406,9 +375,10 @@
return 0;
}
-static void jdwp_control_event(int s, unsigned events, void* _control) {
+static void jdwp_control_event(int fd, unsigned events, void* _control) {
JdwpControl* control = (JdwpControl*)_control;
+ CHECK_EQ(fd, control->listen_socket);
if (events & FDE_READ) {
int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
if (s < 0) {
@@ -425,7 +395,7 @@
auto proc = std::make_unique<JdwpProcess>(s);
if (!proc) {
- fatal("failed to allocate JdwpProcess");
+ LOG(FATAL) << "failed to allocate JdwpProcess";
}
_jdwp_list.emplace_back(std::move(proc));
@@ -484,7 +454,7 @@
JdwpSocket* s = new JdwpSocket();
if (!s) {
- fatal("failed to allocate JdwpSocket");
+ LOG(FATAL) << "failed to allocate JdwpSocket";
}
install_local_socket(s);
@@ -561,7 +531,7 @@
asocket* create_jdwp_tracker_service_socket(void) {
auto t = std::make_unique<JdwpTracker>();
if (!t) {
- fatal("failed to allocate JdwpTracker");
+ LOG(FATAL) << "failed to allocate JdwpTracker";
}
memset(t.get(), 0, sizeof(asocket));
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 2bac486..720ec6a 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -202,6 +202,27 @@
return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
}
+static void spin_service(unique_fd fd) {
+ if (!__android_log_is_debuggable()) {
+ WriteFdExactly(fd.get(), "refusing to spin on non-debuggable build\n");
+ return;
+ }
+
+ // A service that creates an fdevent that's always pending, and then ignores it.
+ unique_fd pipe_read, pipe_write;
+ if (!Pipe(&pipe_read, &pipe_write)) {
+ WriteFdExactly(fd.get(), "failed to create pipe\n");
+ return;
+ }
+
+ fdevent_run_on_main_thread([fd = pipe_read.release()]() {
+ fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
+ fdevent_add(fde, FDE_READ);
+ });
+
+ WriteFdExactly(fd.get(), "spinning\n");
+}
+
unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
if (!strncmp("dev:", name, 4)) {
return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
@@ -254,6 +275,9 @@
} else if (!strcmp(name, "reconnect")) {
return create_service_thread(
"reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
+ } else if (!strcmp(name, "spin")) {
+ return create_service_thread("spin", spin_service);
}
+
return unique_fd{};
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 9d495b0..f603d13 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -18,528 +18,568 @@
#include "sysdeps.h"
-#include <dirent.h>
#include <errno.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/functionfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
-#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
-#include <algorithm>
-#include <atomic>
-#include <chrono>
-#include <condition_variable>
+#include <linux/usb/functionfs.h>
+#include <sys/eventfd.h>
+
+#include <array>
+#include <future>
+#include <memory>
#include <mutex>
-#include <thread>
+#include <optional>
+#include <vector>
+
+#include <asyncio/AsyncIO.h>
#include <android-base/logging.h>
+#include <android-base/macros.h>
#include <android-base/properties.h>
+#include <android-base/thread_annotations.h>
-#include "adb.h"
-#include "adbd/usb.h"
+#include <adbd/usb.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps/chrono.h"
#include "transport.h"
+#include "types.h"
-using namespace std::chrono_literals;
+using android::base::StringPrintf;
-#define MAX_PACKET_SIZE_FS 64
-#define MAX_PACKET_SIZE_HS 512
-#define MAX_PACKET_SIZE_SS 1024
+static constexpr size_t kUsbReadQueueDepth = 16;
+static constexpr size_t kUsbReadSize = 16384;
-#define USB_FFS_BULK_SIZE 16384
+static constexpr size_t kUsbWriteQueueDepth = 16;
-// 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 cpu_to_le16(x) htole16(x)
-#define cpu_to_le32(x) htole32(x)
-
-static unique_fd& dummy_fd = *new unique_fd();
-
-struct func_desc {
- struct usb_interface_descriptor intf;
- struct usb_endpoint_descriptor_no_audio source;
- struct usb_endpoint_descriptor_no_audio sink;
-} __attribute__((packed));
-
-struct ss_func_desc {
- struct usb_interface_descriptor intf;
- struct usb_endpoint_descriptor_no_audio source;
- struct usb_ss_ep_comp_descriptor source_comp;
- struct usb_endpoint_descriptor_no_audio sink;
- struct usb_ss_ep_comp_descriptor sink_comp;
-} __attribute__((packed));
-
-struct desc_v1 {
- struct usb_functionfs_descs_head_v1 {
- __le32 magic;
- __le32 length;
- __le32 fs_count;
- __le32 hs_count;
- } __attribute__((packed)) header;
- struct func_desc fs_descs, hs_descs;
-} __attribute__((packed));
-
-struct desc_v2 {
- struct usb_functionfs_descs_head_v2 header;
- // The rest of the structure depends on the flags in the header.
- __le32 fs_count;
- __le32 hs_count;
- __le32 ss_count;
- __le32 os_count;
- struct func_desc fs_descs, hs_descs;
- struct ss_func_desc ss_descs;
- struct usb_os_desc_header os_header;
- struct usb_ext_compat_desc os_desc;
-} __attribute__((packed));
-
-static struct func_desc fs_descriptors = {
- .intf = {
- .bLength = sizeof(fs_descriptors.intf),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = ADB_CLASS,
- .bInterfaceSubClass = ADB_SUBCLASS,
- .bInterfaceProtocol = ADB_PROTOCOL,
- .iInterface = 1, /* first string from the provided table */
- },
- .source = {
- .bLength = sizeof(fs_descriptors.source),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 1 | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_FS,
- },
- .sink = {
- .bLength = sizeof(fs_descriptors.sink),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 2 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_FS,
- },
-};
-
-static struct func_desc hs_descriptors = {
- .intf = {
- .bLength = sizeof(hs_descriptors.intf),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = ADB_CLASS,
- .bInterfaceSubClass = ADB_SUBCLASS,
- .bInterfaceProtocol = ADB_PROTOCOL,
- .iInterface = 1, /* first string from the provided table */
- },
- .source = {
- .bLength = sizeof(hs_descriptors.source),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 1 | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_HS,
- },
- .sink = {
- .bLength = sizeof(hs_descriptors.sink),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 2 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_HS,
- },
-};
-
-static struct ss_func_desc ss_descriptors = {
- .intf = {
- .bLength = sizeof(ss_descriptors.intf),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = ADB_CLASS,
- .bInterfaceSubClass = ADB_SUBCLASS,
- .bInterfaceProtocol = ADB_PROTOCOL,
- .iInterface = 1, /* first string from the provided table */
- },
- .source = {
- .bLength = sizeof(ss_descriptors.source),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 1 | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_SS,
- },
- .source_comp = {
- .bLength = sizeof(ss_descriptors.source_comp),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- .bMaxBurst = 4,
- },
- .sink = {
- .bLength = sizeof(ss_descriptors.sink),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 2 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_SS,
- },
- .sink_comp = {
- .bLength = sizeof(ss_descriptors.sink_comp),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- .bMaxBurst = 4,
- },
-};
-
-struct usb_ext_compat_desc os_desc_compat = {
- .bFirstInterfaceNumber = 0,
- .Reserved1 = cpu_to_le32(1),
- .CompatibleID = {0},
- .SubCompatibleID = {0},
- .Reserved2 = {0},
-};
-
-static struct usb_os_desc_header os_desc_header = {
- .interface = cpu_to_le32(1),
- .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
- .bcdVersion = cpu_to_le32(1),
- .wIndex = cpu_to_le32(4),
- .bCount = cpu_to_le32(1),
- .Reserved = cpu_to_le32(0),
-};
-
-#define STR_INTERFACE_ "ADB Interface"
-
-static const struct {
- struct usb_functionfs_strings_head header;
- struct {
- __le16 code;
- const char str1[sizeof(STR_INTERFACE_)];
- } __attribute__((packed)) lang0;
-} __attribute__((packed)) strings = {
- .header = {
- .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
- .length = cpu_to_le32(sizeof(strings)),
- .str_count = cpu_to_le32(1),
- .lang_count = cpu_to_le32(1),
- },
- .lang0 = {
- cpu_to_le16(0x0409), /* en-us */
- STR_INTERFACE_,
- },
-};
-
-static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
- aiob->iocb.resize(num_bufs);
- aiob->iocbs.resize(num_bufs);
- aiob->events.resize(num_bufs);
- aiob->num_submitted = 0;
- for (unsigned i = 0; i < num_bufs; i++) {
- aiob->iocbs[i] = &aiob->iocb[i];
- }
- memset(&aiob->ctx, 0, sizeof(aiob->ctx));
- if (io_setup(num_bufs, &aiob->ctx)) {
- D("[ aio: got error on io_setup (%d) ]", errno);
+static const char* to_string(enum usb_functionfs_event_type type) {
+ switch (type) {
+ case FUNCTIONFS_BIND:
+ return "FUNCTIONFS_BIND";
+ case FUNCTIONFS_UNBIND:
+ return "FUNCTIONFS_UNBIND";
+ case FUNCTIONFS_ENABLE:
+ return "FUNCTIONFS_ENABLE";
+ case FUNCTIONFS_DISABLE:
+ return "FUNCTIONFS_DISABLE";
+ case FUNCTIONFS_SETUP:
+ return "FUNCTIONFS_SETUP";
+ case FUNCTIONFS_SUSPEND:
+ return "FUNCTIONFS_SUSPEND";
+ case FUNCTIONFS_RESUME:
+ return "FUNCTIONFS_RESUME";
}
}
-static int getMaxPacketSize(int ffs_fd) {
- usb_endpoint_descriptor desc;
- if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
- D("[ could not get endpoint descriptor! (%d) ]", errno);
- return MAX_PACKET_SIZE_HS;
- } else {
- return desc.wMaxPacketSize;
+enum class TransferDirection : uint64_t {
+ READ = 0,
+ WRITE = 1,
+};
+
+struct TransferId {
+ TransferDirection direction : 1;
+ uint64_t id : 63;
+
+ TransferId() : TransferId(TransferDirection::READ, 0) {}
+
+ private:
+ TransferId(TransferDirection direction, uint64_t id) : direction(direction), id(id) {}
+
+ public:
+ explicit operator uint64_t() const {
+ uint64_t result;
+ static_assert(sizeof(*this) == sizeof(result));
+ memcpy(&result, this, sizeof(*this));
+ return result;
}
-}
-static bool init_functionfs(struct usb_handle* h) {
- LOG(INFO) << "initializing functionfs";
+ static TransferId read(uint64_t id) { return TransferId(TransferDirection::READ, id); }
+ static TransferId write(uint64_t id) { return TransferId(TransferDirection::WRITE, id); }
- ssize_t ret;
- struct desc_v1 v1_descriptor;
- struct desc_v2 v2_descriptor;
+ static TransferId from_value(uint64_t value) {
+ TransferId result;
+ memcpy(&result, &value, sizeof(value));
+ return result;
+ }
+};
- v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
- v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
- v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
- v2_descriptor.fs_count = 3;
- v2_descriptor.hs_count = 3;
- v2_descriptor.ss_count = 5;
- v2_descriptor.os_count = 1;
- 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;
+struct IoBlock {
+ bool pending;
+ struct iocb control;
+ Block payload;
- if (h->control < 0) { // might have already done this before
- LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
- h->control.reset(adb_open(USB_FFS_ADB_EP0, O_WRONLY));
- if (h->control < 0) {
- PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
- goto err;
+ TransferId id() const { return TransferId::from_value(control.aio_data); }
+};
+
+struct ScopedAioContext {
+ ScopedAioContext() = default;
+ ~ScopedAioContext() { reset(); }
+
+ ScopedAioContext(ScopedAioContext&& move) { reset(move.release()); }
+ ScopedAioContext(const ScopedAioContext& copy) = delete;
+
+ ScopedAioContext& operator=(ScopedAioContext&& move) {
+ reset(move.release());
+ return *this;
+ }
+ ScopedAioContext& operator=(const ScopedAioContext& copy) = delete;
+
+ static ScopedAioContext Create(size_t max_events) {
+ aio_context_t ctx = 0;
+ if (io_setup(max_events, &ctx) != 0) {
+ PLOG(FATAL) << "failed to create aio_context_t";
+ }
+ ScopedAioContext result;
+ result.reset(ctx);
+ return result;
+ }
+
+ aio_context_t release() {
+ aio_context_t result = context_;
+ context_ = 0;
+ return result;
+ }
+
+ void reset(aio_context_t new_context = 0) {
+ if (context_ != 0) {
+ io_destroy(context_);
}
- ret = adb_write(h->control.get(), &v2_descriptor, sizeof(v2_descriptor));
- if (ret < 0) {
- v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
- v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
- v1_descriptor.header.fs_count = 3;
- v1_descriptor.header.hs_count = 3;
- v1_descriptor.fs_descs = fs_descriptors;
- v1_descriptor.hs_descs = hs_descriptors;
- D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
- ret = adb_write(h->control.get(), &v1_descriptor, sizeof(v1_descriptor));
- if (ret < 0) {
- D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
- goto err;
+ context_ = new_context;
+ }
+
+ aio_context_t get() { return context_; }
+
+ private:
+ aio_context_t context_ = 0;
+};
+
+struct UsbFfsConnection : public Connection {
+ UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
+ std::promise<void> destruction_notifier)
+ : stopped_(false),
+ destruction_notifier_(std::move(destruction_notifier)),
+ control_fd_(std::move(control)),
+ read_fd_(std::move(read)),
+ write_fd_(std::move(write)) {
+ LOG(INFO) << "UsbFfsConnection constructed";
+ event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (event_fd_ == -1) {
+ PLOG(FATAL) << "failed to create eventfd";
+ }
+
+ aio_context_ = ScopedAioContext::Create(kUsbReadQueueDepth + kUsbWriteQueueDepth);
+ }
+
+ ~UsbFfsConnection() {
+ LOG(INFO) << "UsbFfsConnection being destroyed";
+ Stop();
+ monitor_thread_.join();
+ destruction_notifier_.set_value();
+ }
+
+ virtual bool Write(std::unique_ptr<apacket> packet) override final {
+ LOG(DEBUG) << "USB write: " << dump_header(&packet->msg);
+ Block header(sizeof(packet->msg));
+ memcpy(header.data(), &packet->msg, sizeof(packet->msg));
+
+ std::lock_guard<std::mutex> lock(write_mutex_);
+ write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
+ if (!packet->payload.empty()) {
+ write_requests_.push_back(
+ CreateWriteBlock(std::move(packet->payload), next_write_id_++));
+ }
+ SubmitWrites();
+ return true;
+ }
+
+ virtual void Start() override final { StartMonitor(); }
+
+ virtual void Stop() override final {
+ if (stopped_.exchange(true)) {
+ return;
+ }
+ stopped_ = true;
+ uint64_t notify = 1;
+ ssize_t rc = adb_write(event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+ }
+ CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+ }
+
+ private:
+ void StartMonitor() {
+ // This is a bit of a mess.
+ // It's possible for io_submit to end up blocking, if we call it as the endpoint
+ // becomes disabled. Work around this by having a monitor thread to listen for functionfs
+ // lifecycle events. If we notice an error condition (either we've become disabled, or we
+ // were never enabled in the first place), we send interruption signals to the worker thread
+ // until it dies, and then report failure to the transport via HandleError, which will
+ // eventually result in the transport being destroyed, which will result in UsbFfsConnection
+ // being destroyed, which unblocks the open thread and restarts this entire process.
+ static constexpr int kInterruptionSignal = SIGUSR1;
+ static std::once_flag handler_once;
+ std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
+
+ monitor_thread_ = std::thread([this]() {
+ adb_thread_setname("UsbFfs-monitor");
+
+ bool bound = false;
+ bool started = false;
+ bool running = true;
+ while (running) {
+ if (!bound || !started) {
+ adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
+ int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
+ if (rc == -1) {
+ PLOG(FATAL) << "poll on USB control fd failed";
+ } else if (rc == 0) {
+ // Something in the kernel presumably went wrong.
+ // Close our endpoints, wait for a bit, and then try again.
+ aio_context_.reset();
+ read_fd_.reset();
+ write_fd_.reset();
+ control_fd_.reset();
+ std::this_thread::sleep_for(5s);
+ HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
+ return;
+ }
+ }
+
+ struct usb_functionfs_event event;
+ if (TEMP_FAILURE_RETRY(adb_read(control_fd_.get(), &event, sizeof(event))) !=
+ sizeof(event)) {
+ PLOG(FATAL) << "failed to read functionfs event";
+ }
+
+ LOG(INFO) << "USB event: "
+ << to_string(static_cast<usb_functionfs_event_type>(event.type));
+
+ switch (event.type) {
+ case FUNCTIONFS_BIND:
+ CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+ bound = true;
+ break;
+
+ case FUNCTIONFS_ENABLE:
+ CHECK(!started) << "received FUNCTIONFS_ENABLE while already running?";
+ started = true;
+ StartWorker();
+ break;
+
+ case FUNCTIONFS_DISABLE:
+ running = false;
+ break;
+ }
+ }
+
+ pthread_t worker_thread_handle = worker_thread_.native_handle();
+ while (true) {
+ int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+ if (rc != 0) {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ break;
+ }
+
+ std::this_thread::sleep_for(100ms);
+
+ rc = pthread_kill(worker_thread_handle, 0);
+ if (rc == 0) {
+ continue;
+ } else if (rc == ESRCH) {
+ break;
+ } else {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ }
+ }
+
+ worker_thread_.join();
+
+ aio_context_.reset();
+ read_fd_.reset();
+ write_fd_.reset();
+ });
+ }
+
+ void StartWorker() {
+ worker_thread_ = std::thread([this]() {
+ adb_thread_setname("UsbFfs-worker");
+ for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
+ read_requests_[i] = CreateReadBlock(next_read_id_++);
+ SubmitRead(&read_requests_[i]);
+ }
+
+ while (!stopped_) {
+ uint64_t dummy;
+ ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+ if (rc == -1) {
+ PLOG(FATAL) << "failed to read from eventfd";
+ } else if (rc == 0) {
+ LOG(FATAL) << "hit EOF on eventfd";
+ }
+
+ WaitForEvents();
+ }
+ });
+ }
+
+ void PrepareReadBlock(IoBlock* block, uint64_t id) {
+ block->pending = false;
+ block->payload.resize(kUsbReadSize);
+ block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+ block->control.aio_nbytes = block->payload.size();
+ }
+
+ IoBlock CreateReadBlock(uint64_t id) {
+ IoBlock block;
+ PrepareReadBlock(&block, id);
+ block.control.aio_rw_flags = 0;
+ block.control.aio_lio_opcode = IOCB_CMD_PREAD;
+ block.control.aio_reqprio = 0;
+ block.control.aio_fildes = read_fd_.get();
+ block.control.aio_offset = 0;
+ block.control.aio_flags = IOCB_FLAG_RESFD;
+ block.control.aio_resfd = event_fd_.get();
+ return block;
+ }
+
+ void WaitForEvents() {
+ static constexpr size_t kMaxEvents = kUsbReadQueueDepth + kUsbWriteQueueDepth;
+ struct io_event events[kMaxEvents];
+ struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
+ int rc = io_getevents(aio_context_.get(), 0, kMaxEvents, events, &timeout);
+ if (rc == -1) {
+ HandleError(StringPrintf("io_getevents failed while reading: %s", strerror(errno)));
+ return;
+ }
+
+ for (int event_idx = 0; event_idx < rc; ++event_idx) {
+ auto& event = events[event_idx];
+ TransferId id = TransferId::from_value(event.data);
+
+ if (event.res < 0) {
+ std::string error =
+ StringPrintf("%s %" PRIu64 " failed with error %s",
+ id.direction == TransferDirection::READ ? "read" : "write",
+ id.id, strerror(-event.res));
+ HandleError(error);
+ return;
+ }
+
+ if (id.direction == TransferDirection::READ) {
+ HandleRead(id, event.res);
+ } else {
+ HandleWrite(id);
+ }
+ }
+ }
+
+ void HandleRead(TransferId id, int64_t size) {
+ uint64_t read_idx = id.id % kUsbReadQueueDepth;
+ IoBlock* block = &read_requests_[read_idx];
+ block->pending = false;
+ block->payload.resize(size);
+
+ // Notification for completed reads can be received out of order.
+ if (block->id().id != needed_read_id_) {
+ LOG(VERBOSE) << "read " << block->id().id << " completed while waiting for "
+ << needed_read_id_;
+ return;
+ }
+
+ for (uint64_t id = needed_read_id_;; ++id) {
+ size_t read_idx = id % kUsbReadQueueDepth;
+ IoBlock* current_block = &read_requests_[read_idx];
+ if (current_block->pending) {
+ break;
+ }
+ ProcessRead(current_block);
+ ++needed_read_id_;
+ }
+ }
+
+ void ProcessRead(IoBlock* block) {
+ if (!block->payload.empty()) {
+ if (!incoming_header_.has_value()) {
+ CHECK_EQ(sizeof(amessage), block->payload.size());
+ amessage msg;
+ memcpy(&msg, block->payload.data(), sizeof(amessage));
+ LOG(DEBUG) << "USB read:" << dump_header(&msg);
+ incoming_header_ = msg;
+ } else {
+ size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
+ Block payload = std::move(block->payload);
+ CHECK_LE(payload.size(), bytes_left);
+ incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
+ }
+
+ if (incoming_header_->data_length == incoming_payload_.size()) {
+ auto packet = std::make_unique<apacket>();
+ packet->msg = *incoming_header_;
+
+ // TODO: Make apacket contain an IOVector so we don't have to coalesce.
+ packet->payload = incoming_payload_.coalesce();
+ read_callback_(this, std::move(packet));
+
+ incoming_header_.reset();
+ incoming_payload_.clear();
}
}
- ret = adb_write(h->control.get(), &strings, sizeof(strings));
- if (ret < 0) {
- D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
- goto err;
+ PrepareReadBlock(block, block->id().id + kUsbReadQueueDepth);
+ SubmitRead(block);
+ }
+
+ void SubmitRead(IoBlock* block) {
+ block->pending = true;
+ struct iocb* iocb = &block->control;
+ if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+ HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
+ return;
}
- //Signal only when writing the descriptors to ffs
- android::base::SetProperty("sys.usb.ffs.ready", "1");
}
- h->bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
- if (h->bulk_out < 0) {
- PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
- goto err;
+ void HandleWrite(TransferId id) {
+ std::lock_guard<std::mutex> lock(write_mutex_);
+ auto it =
+ std::find_if(write_requests_.begin(), write_requests_.end(), [id](const auto& req) {
+ return static_cast<uint64_t>(req->id()) == static_cast<uint64_t>(id);
+ });
+ CHECK(it != write_requests_.end());
+
+ write_requests_.erase(it);
+ size_t outstanding_writes = --writes_submitted_;
+ LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
+
+ SubmitWrites();
}
- h->bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
- if (h->bulk_in < 0) {
- PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
- goto err;
+ std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+ auto block = std::make_unique<IoBlock>();
+ block->payload = std::move(payload);
+ block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
+ block->control.aio_rw_flags = 0;
+ block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
+ block->control.aio_reqprio = 0;
+ block->control.aio_fildes = write_fd_.get();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+ block->control.aio_nbytes = block->payload.size();
+ block->control.aio_offset = 0;
+ block->control.aio_flags = IOCB_FLAG_RESFD;
+ block->control.aio_resfd = event_fd_.get();
+ return block;
}
- h->read_aiob.fd = h->bulk_out;
- h->write_aiob.fd = h->bulk_in;
- h->reads_zero_packets = true;
- return true;
+ void SubmitWrites() REQUIRES(write_mutex_) {
+ if (writes_submitted_ == kUsbWriteQueueDepth) {
+ return;
+ }
-err:
- h->bulk_in.reset();
- h->bulk_out.reset();
- h->control.reset();
- return false;
-}
+ ssize_t writes_to_submit = std::min(kUsbWriteQueueDepth - writes_submitted_,
+ write_requests_.size() - writes_submitted_);
+ CHECK_GE(writes_to_submit, 0);
+ if (writes_to_submit == 0) {
+ return;
+ }
-static void usb_ffs_open_thread(usb_handle *usb) {
+ struct iocb* iocbs[kUsbWriteQueueDepth];
+ for (int i = 0; i < writes_to_submit; ++i) {
+ CHECK(!write_requests_[writes_submitted_ + i]->pending);
+ write_requests_[writes_submitted_ + i]->pending = true;
+ iocbs[i] = &write_requests_[writes_submitted_ + i]->control;
+ LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
+ }
+
+ int rc = io_submit(aio_context_.get(), writes_to_submit, iocbs);
+ if (rc == -1) {
+ HandleError(StringPrintf("failed to submit write requests: %s", strerror(errno)));
+ return;
+ } else if (rc != writes_to_submit) {
+ LOG(FATAL) << "failed to submit all writes: wanted to submit " << writes_to_submit
+ << ", actually submitted " << rc;
+ }
+
+ writes_submitted_ += rc;
+ }
+
+ void HandleError(const std::string& error) {
+ std::call_once(error_flag_, [&]() {
+ error_callback_(this, error);
+ if (!stopped_) {
+ Stop();
+ }
+ });
+ }
+
+ std::thread monitor_thread_;
+ std::thread worker_thread_;
+
+ std::atomic<bool> stopped_;
+ std::promise<void> destruction_notifier_;
+ std::once_flag error_flag_;
+
+ unique_fd event_fd_;
+
+ ScopedAioContext aio_context_;
+ unique_fd control_fd_;
+ unique_fd read_fd_;
+ unique_fd write_fd_;
+
+ std::optional<amessage> incoming_header_;
+ IOVector incoming_payload_;
+
+ std::array<IoBlock, kUsbReadQueueDepth> read_requests_;
+ IOVector read_data_;
+
+ // ID of the next request that we're going to send out.
+ size_t next_read_id_ = 0;
+
+ // ID of the next packet we're waiting for.
+ size_t needed_read_id_ = 0;
+
+ std::mutex write_mutex_;
+ std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
+ size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
+ size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+};
+
+static void usb_ffs_open_thread() {
adb_thread_setname("usb ffs open");
while (true) {
- // wait until the USB device needs opening
- std::unique_lock<std::mutex> lock(usb->lock);
- while (!usb->open_new_connection) {
- usb->notify.wait(lock);
- }
- usb->open_new_connection = false;
- lock.unlock();
-
- while (true) {
- if (init_functionfs(usb)) {
- LOG(INFO) << "functionfs successfully initialized";
- break;
- }
+ unique_fd control;
+ unique_fd bulk_out;
+ unique_fd bulk_in;
+ if (!open_functionfs(&control, &bulk_out, &bulk_in)) {
std::this_thread::sleep_for(1s);
- }
-
- LOG(INFO) << "registering usb transport";
- register_usb_transport(usb, nullptr, nullptr, 1);
- }
-
- // never gets here
- abort();
-}
-
-static int usb_ffs_write(usb_handle* h, const void* data, int len) {
- D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
-
- const char* buf = static_cast<const char*>(data);
- int orig_len = len;
- while (len > 0) {
- int write_len = std::min(USB_FFS_BULK_SIZE, len);
- int n = adb_write(h->bulk_in, buf, write_len);
- if (n < 0) {
- D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
- return -1;
- }
- buf += n;
- len -= n;
- }
-
- D("[ done fd=%d ]", h->bulk_in.get());
- return orig_len;
-}
-
-static int usb_ffs_read(usb_handle* h, void* data, int len) {
- D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
-
- char* buf = static_cast<char*>(data);
- int orig_len = len;
- while (len > 0) {
- int read_len = std::min(USB_FFS_BULK_SIZE, len);
- int n = adb_read(h->bulk_out, buf, read_len);
- if (n < 0) {
- D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
- return -1;
- }
- buf += n;
- len -= n;
- }
-
- D("[ done fd=%d ]", h->bulk_out.get());
- return orig_len;
-}
-
-static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
- aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
- bool zero_packet = false;
-
- int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
- const char* cur_data = reinterpret_cast<const char*>(data);
- int packet_size = getMaxPacketSize(aiob->fd);
-
- if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
- 0) {
- D("[ Failed to madvise: %d ]", errno);
- }
-
- for (int i = 0; i < num_bufs; i++) {
- int buf_len = std::min(len, static_cast<int>(h->io_size));
- io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
-
- len -= buf_len;
- cur_data += buf_len;
-
- if (len == 0 && buf_len % packet_size == 0 && read) {
- // adb does not expect the device to send a zero packet after data transfer,
- // but the host *does* send a zero packet for the device to read.
- zero_packet = h->reads_zero_packets;
- }
- }
- if (zero_packet) {
- io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
- packet_size, 0, read);
- num_bufs += 1;
- }
-
- while (true) {
- if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
- PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
- return -1;
- }
- if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
- nullptr)) < num_bufs) {
- PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
- return -1;
- }
- if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
continue;
}
- int ret = 0;
- for (int i = 0; i < num_bufs; i++) {
- if (aiob->events[i].res < 0) {
- errno = -aiob->events[i].res;
- PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
- << " total bufs " << num_bufs;
- return -1;
- }
- ret += aiob->events[i].res;
- }
- return ret;
+
+ atransport* transport = new atransport();
+ transport->serial = "UsbFfs";
+ std::promise<void> destruction_notifier;
+ std::future<void> future = destruction_notifier.get_future();
+ transport->SetConnection(std::make_unique<UsbFfsConnection>(
+ std::move(control), std::move(bulk_out), std::move(bulk_in),
+ std::move(destruction_notifier)));
+ register_transport(transport);
+ future.wait();
}
}
-static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
- return usb_ffs_do_aio(h, data, len, true);
-}
-
-static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
- return usb_ffs_do_aio(h, data, len, false);
-}
-
-static void usb_ffs_kick(usb_handle* h) {
- int err;
-
- err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
- if (err < 0) {
- D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
- }
-
- err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
- if (err < 0) {
- D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
- }
-
- // don't close ep0 here, since we may not need to reinitialize it with
- // the same descriptors again. if however ep1/ep2 fail to re-open in
- // init_functionfs, only then would we close and open ep0 again.
- // Ditto the comment in usb_adb_kick.
- h->kicked = true;
- TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
- TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
-}
-
-static void usb_ffs_close(usb_handle* h) {
- LOG(INFO) << "closing functionfs transport";
-
- h->kicked = false;
- h->bulk_out.reset();
- h->bulk_in.reset();
-
- // Notify usb_adb_open_thread to open a new connection.
- h->lock.lock();
- h->open_new_connection = true;
- h->lock.unlock();
- h->notify.notify_one();
-}
-
-usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size) {
- usb_handle* h = new usb_handle();
-
- if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
- // Devices on older kernels (< 3.18) will not have aio support for ffs
- // unless backported. Fall back on the non-aio functions instead.
- h->write = usb_ffs_write;
- h->read = usb_ffs_read;
- } else {
- h->write = usb_ffs_aio_write;
- h->read = usb_ffs_aio_read;
- aio_block_init(&h->read_aiob, num_bufs);
- aio_block_init(&h->write_aiob, num_bufs);
- }
- h->io_size = io_size;
- h->kick = usb_ffs_kick;
- h->close = usb_ffs_close;
- return h;
-}
-
+void usb_init_legacy();
void usb_init() {
- D("[ usb_init - using FunctionFS ]");
- dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
- CHECK_NE(-1, dummy_fd.get());
-
- std::thread(usb_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)).detach();
-}
-
-int usb_write(usb_handle* h, const void* data, int len) {
- return h->write(h, data, len);
-}
-
-int usb_read(usb_handle* h, void* data, int len) {
- return h->read(h, data, len);
-}
-
-int usb_close(usb_handle* h) {
- h->close(h);
- return 0;
-}
-
-void usb_kick(usb_handle* h) {
- h->kick(h);
+ if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
+ usb_init_legacy();
+ } else {
+ std::thread(usb_ffs_open_thread).detach();
+ }
}
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
new file mode 100644
index 0000000..07b4ba8
--- /dev/null
+++ b/adb/daemon/usb_ffs.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#define USB_FFS_BULK_SIZE 16384
+
+// 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 cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+struct func_desc {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct ss_func_desc {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_ss_ep_comp_descriptor source_comp;
+ struct usb_endpoint_descriptor_no_audio sink;
+ struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct desc_v1 {
+ struct usb_functionfs_descs_head_v1 {
+ __le32 magic;
+ __le32 length;
+ __le32 fs_count;
+ __le32 hs_count;
+ } __attribute__((packed)) header;
+ struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+struct desc_v2 {
+ struct usb_functionfs_descs_head_v2 header;
+ // The rest of the structure depends on the flags in the header.
+ __le32 fs_count;
+ __le32 hs_count;
+ __le32 ss_count;
+ __le32 os_count;
+ struct func_desc fs_descs, hs_descs;
+ struct ss_func_desc ss_descs;
+ struct usb_os_desc_header os_header;
+ struct usb_ext_compat_desc os_desc;
+} __attribute__((packed));
+
+// clang-format off
+static struct func_desc fs_descriptors = {
+ .intf = {
+ .bLength = sizeof(fs_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(fs_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+ .sink = {
+ .bLength = sizeof(fs_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ },
+};
+
+static struct func_desc hs_descriptors = {
+ .intf = {
+ .bLength = sizeof(hs_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(hs_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+ .sink = {
+ .bLength = sizeof(hs_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ },
+};
+
+static struct ss_func_desc ss_descriptors = {
+ .intf = {
+ .bLength = sizeof(ss_descriptors.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = ADB_CLASS,
+ .bInterfaceSubClass = ADB_SUBCLASS,
+ .bInterfaceProtocol = ADB_PROTOCOL,
+ .iInterface = 1, /* first string from the provided table */
+ },
+ .source = {
+ .bLength = sizeof(ss_descriptors.source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+ },
+ .source_comp = {
+ .bLength = sizeof(ss_descriptors.source_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 4,
+ },
+ .sink = {
+ .bLength = sizeof(ss_descriptors.sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+ },
+ .sink_comp = {
+ .bLength = sizeof(ss_descriptors.sink_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 4,
+ },
+};
+
+struct usb_ext_compat_desc os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = cpu_to_le32(1),
+ .CompatibleID = {0},
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+ .interface = cpu_to_le32(1),
+ .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+ .bcdVersion = cpu_to_le32(1),
+ .wIndex = cpu_to_le32(4),
+ .bCount = cpu_to_le32(1),
+ .Reserved = cpu_to_le32(0),
+};
+
+#define STR_INTERFACE_ "ADB Interface"
+
+static const struct {
+ struct usb_functionfs_strings_head header;
+ struct {
+ __le16 code;
+ const char str1[sizeof(STR_INTERFACE_)];
+ } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = cpu_to_le32(sizeof(strings)),
+ .str_count = cpu_to_le32(1),
+ .lang_count = cpu_to_le32(1),
+ },
+ .lang0 = {
+ cpu_to_le16(0x0409), /* en-us */
+ STR_INTERFACE_,
+ },
+};
+// clang-format on
+
+bool open_functionfs(android::base::unique_fd* out_control, android::base::unique_fd* out_bulk_out,
+ android::base::unique_fd* out_bulk_in) {
+ unique_fd control, bulk_out, bulk_in;
+ struct desc_v1 v1_descriptor = {};
+ struct desc_v2 v2_descriptor = {};
+
+ v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+ v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+ v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+ FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
+ v2_descriptor.fs_count = 3;
+ v2_descriptor.hs_count = 3;
+ v2_descriptor.ss_count = 5;
+ v2_descriptor.os_count = 1;
+ 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;
+
+ if (out_control->get() < 0) { // might have already done this before
+ LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
+ control.reset(adb_open(USB_FFS_ADB_EP0, O_RDWR));
+ if (control < 0) {
+ PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
+ return false;
+ }
+
+ if (adb_write(control.get(), &v2_descriptor, sizeof(v2_descriptor)) < 0) {
+ D("[ %s: Switching to V1_descriptor format errno=%s ]", USB_FFS_ADB_EP0,
+ strerror(errno));
+ v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+ v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+ v1_descriptor.header.fs_count = 3;
+ v1_descriptor.header.hs_count = 3;
+ v1_descriptor.fs_descs = fs_descriptors;
+ v1_descriptor.hs_descs = hs_descriptors;
+ if (adb_write(control.get(), &v1_descriptor, sizeof(v1_descriptor)) < 0) {
+ PLOG(ERROR) << "failed to write USB descriptors";
+ return false;
+ }
+ }
+
+ if (adb_write(control.get(), &strings, sizeof(strings)) < 0) {
+ PLOG(ERROR) << "failed to write USB strings";
+ return false;
+ }
+ // Signal only when writing the descriptors to ffs
+ android::base::SetProperty("sys.usb.ffs.ready", "1");
+ }
+
+ bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
+ if (bulk_out < 0) {
+ PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
+ return false;
+ }
+
+ bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
+ if (bulk_in < 0) {
+ PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
+ return false;
+ }
+
+ *out_control = std::move(control);
+ *out_bulk_in = std::move(bulk_in);
+ *out_bulk_out = std::move(bulk_out);
+ return true;
+}
diff --git a/adb/daemon/usb_legacy.cpp b/adb/daemon/usb_legacy.cpp
new file mode 100644
index 0000000..7ace59d
--- /dev/null
+++ b/adb/daemon/usb_legacy.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+#include "transport.h"
+
+using namespace std::chrono_literals;
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#define USB_FFS_BULK_SIZE 16384
+
+// 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)
+
+static unique_fd& dummy_fd = *new unique_fd();
+
+static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
+ aiob->iocb.resize(num_bufs);
+ aiob->iocbs.resize(num_bufs);
+ aiob->events.resize(num_bufs);
+ aiob->num_submitted = 0;
+ for (unsigned i = 0; i < num_bufs; i++) {
+ aiob->iocbs[i] = &aiob->iocb[i];
+ }
+ memset(&aiob->ctx, 0, sizeof(aiob->ctx));
+ if (io_setup(num_bufs, &aiob->ctx)) {
+ D("[ aio: got error on io_setup (%d) ]", errno);
+ }
+}
+
+static int getMaxPacketSize(int ffs_fd) {
+ usb_endpoint_descriptor desc;
+ if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+ D("[ could not get endpoint descriptor! (%d) ]", errno);
+ return MAX_PACKET_SIZE_HS;
+ } else {
+ return desc.wMaxPacketSize;
+ }
+}
+
+static bool init_functionfs(struct usb_handle* h) {
+ LOG(INFO) << "initializing functionfs";
+ if (!open_functionfs(&h->control, &h->bulk_out, &h->bulk_in)) {
+ return false;
+ }
+
+ h->read_aiob.fd = h->bulk_out.get();
+ h->write_aiob.fd = h->bulk_in.get();
+ h->reads_zero_packets = true;
+ return true;
+}
+
+static void usb_legacy_ffs_open_thread(usb_handle* usb) {
+ adb_thread_setname("usb legacy ffs open");
+
+ while (true) {
+ // wait until the USB device needs opening
+ std::unique_lock<std::mutex> lock(usb->lock);
+ while (!usb->open_new_connection) {
+ usb->notify.wait(lock);
+ }
+ usb->open_new_connection = false;
+ lock.unlock();
+
+ while (true) {
+ if (init_functionfs(usb)) {
+ LOG(INFO) << "functionfs successfully initialized";
+ break;
+ }
+ std::this_thread::sleep_for(1s);
+ }
+
+ LOG(INFO) << "registering usb transport";
+ register_usb_transport(usb, nullptr, nullptr, 1);
+ }
+
+ // never gets here
+ abort();
+}
+
+static int usb_ffs_write(usb_handle* h, const void* data, int len) {
+ D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
+
+ const char* buf = static_cast<const char*>(data);
+ int orig_len = len;
+ while (len > 0) {
+ int write_len = std::min(USB_FFS_BULK_SIZE, len);
+ int n = adb_write(h->bulk_in, buf, write_len);
+ if (n < 0) {
+ D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
+ return -1;
+ }
+ buf += n;
+ len -= n;
+ }
+
+ D("[ done fd=%d ]", h->bulk_in.get());
+ return orig_len;
+}
+
+static int usb_ffs_read(usb_handle* h, void* data, int len) {
+ D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
+
+ char* buf = static_cast<char*>(data);
+ int orig_len = len;
+ while (len > 0) {
+ int read_len = std::min(USB_FFS_BULK_SIZE, len);
+ int n = adb_read(h->bulk_out, buf, read_len);
+ if (n < 0) {
+ D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
+ return -1;
+ }
+ buf += n;
+ len -= n;
+ }
+
+ D("[ done fd=%d ]", h->bulk_out.get());
+ return orig_len;
+}
+
+static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
+ aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+ bool zero_packet = false;
+
+ int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
+ const char* cur_data = reinterpret_cast<const char*>(data);
+ int packet_size = getMaxPacketSize(aiob->fd);
+
+ if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
+ 0) {
+ D("[ Failed to madvise: %d ]", errno);
+ }
+
+ for (int i = 0; i < num_bufs; i++) {
+ int buf_len = std::min(len, static_cast<int>(h->io_size));
+ io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
+
+ len -= buf_len;
+ cur_data += buf_len;
+
+ if (len == 0 && buf_len % packet_size == 0 && read) {
+ // adb does not expect the device to send a zero packet after data transfer,
+ // but the host *does* send a zero packet for the device to read.
+ zero_packet = h->reads_zero_packets;
+ }
+ }
+ if (zero_packet) {
+ io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
+ packet_size, 0, read);
+ num_bufs += 1;
+ }
+
+ while (true) {
+ if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
+ PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
+ return -1;
+ }
+ if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
+ nullptr)) < num_bufs) {
+ PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
+ return -1;
+ }
+ if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
+ continue;
+ }
+ int ret = 0;
+ for (int i = 0; i < num_bufs; i++) {
+ if (aiob->events[i].res < 0) {
+ errno = -aiob->events[i].res;
+ PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
+ << " total bufs " << num_bufs;
+ return -1;
+ }
+ ret += aiob->events[i].res;
+ }
+ return ret;
+ }
+}
+
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
+ return usb_ffs_do_aio(h, data, len, true);
+}
+
+static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
+ return usb_ffs_do_aio(h, data, len, false);
+}
+
+static void usb_ffs_kick(usb_handle* h) {
+ int err;
+
+ err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
+ if (err < 0) {
+ D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
+ }
+
+ err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
+ if (err < 0) {
+ D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
+ }
+
+ // don't close ep0 here, since we may not need to reinitialize it with
+ // the same descriptors again. if however ep1/ep2 fail to re-open in
+ // init_functionfs, only then would we close and open ep0 again.
+ // Ditto the comment in usb_adb_kick.
+ h->kicked = true;
+ TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
+ TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
+}
+
+static void usb_ffs_close(usb_handle* h) {
+ LOG(INFO) << "closing functionfs transport";
+
+ h->kicked = false;
+ h->bulk_out.reset();
+ h->bulk_in.reset();
+
+ // Notify usb_adb_open_thread to open a new connection.
+ h->lock.lock();
+ h->open_new_connection = true;
+ h->lock.unlock();
+ h->notify.notify_one();
+}
+
+usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size) {
+ usb_handle* h = new usb_handle();
+
+ if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+ // Devices on older kernels (< 3.18) will not have aio support for ffs
+ // unless backported. Fall back on the non-aio functions instead.
+ h->write = usb_ffs_write;
+ h->read = usb_ffs_read;
+ } else {
+ h->write = usb_ffs_aio_write;
+ h->read = usb_ffs_aio_read;
+ aio_block_init(&h->read_aiob, num_bufs);
+ aio_block_init(&h->write_aiob, num_bufs);
+ }
+ h->io_size = io_size;
+ h->kick = usb_ffs_kick;
+ h->close = usb_ffs_close;
+ return h;
+}
+
+void usb_init_legacy() {
+ D("[ usb_init - using legacy FunctionFS ]");
+ dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
+ CHECK_NE(-1, dummy_fd.get());
+
+ std::thread(usb_legacy_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE))
+ .detach();
+}
+
+int usb_write(usb_handle* h, const void* data, int len) {
+ return h->write(h, data, len);
+}
+
+int usb_read(usb_handle* h, void* data, int len) {
+ return h->read(h, data, len);
+}
+
+int usb_close(usb_handle* h) {
+ h->close(h);
+ return 0;
+}
+
+void usb_kick(usb_handle* h) {
+ h->kick(h);
+}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 98a73eb..e096560 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -35,6 +35,8 @@
#include <unordered_map>
#include <vector>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
@@ -44,6 +46,7 @@
#include "adb_trace.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
+#include "sysdeps/chrono.h"
#define FDE_EVENTMASK 0x00ff
#define FDE_STATEMASK 0xff00
@@ -247,6 +250,7 @@
}
CHECK_GT(pollfds.size(), 0u);
D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+
int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
if (ret == -1) {
PLOG(ERROR) << "poll(), ret = " << ret;
@@ -367,10 +371,66 @@
}
}
+static void fdevent_check_spin(uint64_t cycle) {
+ // Check to see if we're spinning because we forgot about an fdevent
+ // by keeping track of how long fdevents have been continuously pending.
+ struct SpinCheck {
+ fdevent* fde;
+ android::base::boot_clock::time_point timestamp;
+ uint64_t cycle;
+ };
+ static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+ static auto last_cycle = android::base::boot_clock::now();
+
+ auto now = android::base::boot_clock::now();
+ if (now - last_cycle > 10ms) {
+ // We're not spinning.
+ g_continuously_pending.clear();
+ last_cycle = now;
+ return;
+ }
+ last_cycle = now;
+
+ for (auto* fde : g_pending_list) {
+ auto it = g_continuously_pending.find(fde->id);
+ if (it == g_continuously_pending.end()) {
+ g_continuously_pending[fde->id] =
+ SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
+ } else {
+ it->second.cycle = cycle;
+ }
+ }
+
+ for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
+ if (it->second.cycle != cycle) {
+ it = g_continuously_pending.erase(it);
+ } else {
+ // Use an absurdly long window, since all we really care about is
+ // getting a bugreport eventually.
+ if (now - it->second.timestamp > 300s) {
+ LOG(FATAL_WITHOUT_ABORT)
+ << "detected spin in fdevent: " << dump_fde(it->second.fde);
+#if defined(__linux__)
+ int fd = it->second.fde->fd.get();
+ std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+ std::string path;
+ if (!android::base::Readlink(fd_path, &path)) {
+ PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
+ }
+ LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
+#endif
+ abort();
+ }
+ ++it;
+ }
+ }
+}
+
void fdevent_loop() {
set_main_thread();
fdevent_run_setup();
+ uint64_t cycle = 0;
while (true) {
if (terminate_loop) {
return;
@@ -380,6 +440,8 @@
fdevent_process();
+ fdevent_check_spin(cycle++);
+
while (!g_pending_list.empty()) {
fdevent* fde = g_pending_list.front();
g_pending_list.pop_front();
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 0cb2439..816134f 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -19,6 +19,7 @@
#include <gtest/gtest.h>
#include <limits>
+#include <memory>
#include <queue>
#include <string>
#include <thread>
@@ -26,7 +27,6 @@
#include "adb_io.h"
#include "fdevent_test.h"
-#include "sysdeps/memory.h"
class FdHandler {
public:
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index 5a417e0..8d853c3 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -20,6 +20,7 @@
#include <mutex>
#include <thread>
+#include "adb_io.h"
#include "socket.h"
#include "sysdeps.h"
#include "sysdeps/chrono.h"
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 1534792..8b07f74 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -78,7 +78,7 @@
// Socket ids should never be 0.
if (local_socket_next_id == 0) {
- fatal("local socket id overflow");
+ LOG(FATAL) << "local socket id overflow";
}
local_socket_list.push_back(s);
@@ -451,7 +451,7 @@
// Returns a new non-NULL asocket handle.
asocket* create_remote_socket(unsigned id, atransport* t) {
if (id == 0) {
- fatal("invalid remote socket id (0)");
+ LOG(FATAL) << "invalid remote socket id (0)";
}
asocket* s = new asocket();
s->id = id;
@@ -477,9 +477,7 @@
p->payload.assign(destination, destination + strlen(destination) + 1);
p->msg.data_length = p->payload.size();
- if (p->msg.data_length > s->get_max_payload()) {
- fatal("destination oversized");
- }
+ CHECK_LE(p->msg.data_length, s->get_max_payload());
send_packet(p, s->transport);
}
diff --git a/adb/sysdeps/errno.cpp b/adb/sysdeps/errno.cpp
index 6869947..9a37ea2 100644
--- a/adb/sysdeps/errno.cpp
+++ b/adb/sysdeps/errno.cpp
@@ -24,10 +24,6 @@
#include "adb.h"
-#if defined(_WIN32)
-#define ETXTBSY EBUSY
-#endif
-
// Use the linux asm-generic values for errno (which are used on all android archs but mips).
#define ERRNO_VALUES() \
ERRNO_VALUE(EACCES, 13); \
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
deleted file mode 100644
index 4108aff..0000000
--- a/adb/sysdeps/memory.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-/*
- * 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 <memory>
-#include <type_traits>
-
-#if defined(_WIN32)
-// We don't have C++14 on Windows yet.
-// Reimplement std::make_unique ourselves until we do.
-
-namespace internal {
-
-template <typename T>
-struct array_known_bounds;
-
-template <typename T>
-struct array_known_bounds<T[]> {
- constexpr static bool value = false;
-};
-
-template <typename T, size_t N>
-struct array_known_bounds<T[N]> {
- constexpr static bool value = true;
-};
-
-} // namespace internal
-
-namespace std {
-
-template <typename T, typename... Args>
-typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(
- Args&&... args) {
- return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
-template <typename T>
-typename std::enable_if<std::is_array<T>::value && !internal::array_known_bounds<T>::value,
- std::unique_ptr<T>>::type
-make_unique(std::size_t size) {
- return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
-}
-
-template <typename T, typename... Args>
-typename std::enable_if<std::is_array<T>::value && internal::array_known_bounds<T>::value,
- std::unique_ptr<T>>::type
-make_unique(Args&&... args) = delete;
-
-} // namespace std
-
-#endif
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 8784757..8a6541d 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -35,6 +35,7 @@
#include <cutils/sockets.h>
#include <android-base/errors.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
@@ -46,8 +47,6 @@
#include "sysdeps/uio.h"
-extern void fatal(const char *fmt, ...);
-
/* forward declarations */
typedef const struct FHClassRec_* FHClass;
@@ -94,10 +93,9 @@
_fh_socket_writev,
};
-#define assert(cond) \
- do { \
- if (!(cond)) fatal("assertion failed '%s' on %s:%d\n", #cond, __FILE__, __LINE__); \
- } while (0)
+#if defined(assert)
+#undef assert
+#endif
void handle_deleter::operator()(HANDLE h) {
// CreateFile() is documented to return INVALID_HANDLE_FILE on error,
@@ -726,8 +724,8 @@
WSADATA wsaData;
int rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (rc != 0) {
- fatal("adb: could not initialize Winsock: %s",
- android::base::SystemErrorCodeToString(rc).c_str());
+ LOG(FATAL) << "could not initialize Winsock: "
+ << android::base::SystemErrorCodeToString(rc);
}
// Note that we do not call atexit() to register WSACleanup to be called
@@ -1283,11 +1281,11 @@
}
if (read_count == 0) { // should be impossible
- fatal("ReadConsoleInputA returned 0");
+ LOG(FATAL) << "ReadConsoleInputA returned 0";
}
if (read_count != 1) { // should be impossible
- fatal("ReadConsoleInputA did not return one input record");
+ LOG(FATAL) << "ReadConsoleInputA did not return one input record";
}
// If the console window is resized, emulate SIGWINCH by breaking out
@@ -1305,8 +1303,7 @@
if ((input_record->EventType == KEY_EVENT) &&
(input_record->Event.KeyEvent.bKeyDown)) {
if (input_record->Event.KeyEvent.wRepeatCount == 0) {
- fatal("ReadConsoleInputA returned a key event with zero repeat"
- " count");
+ LOG(FATAL) << "ReadConsoleInputA returned a key event with zero repeat count";
}
// Got an interesting INPUT_RECORD, so return
@@ -2189,7 +2186,7 @@
for (int i = 0; i < argc; ++i) {
std::string arg_narrow;
if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
- fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+ PLOG(FATAL) << "cannot convert argument from UTF-16 to UTF-8";
}
narrow_args[i] = strdup(arg_narrow.c_str());
}
@@ -2640,7 +2637,7 @@
// If _wenviron is null, then -municode probably wasn't used. That
// linker flag will cause the entry point to setup _wenviron. It will
// also require an implementation of wmain() (which we provide above).
- fatal("_wenviron is not set, did you link with -municode?");
+ LOG(FATAL) << "_wenviron is not set, did you link with -municode?";
}
// Read name/value pairs from UTF-16 _wenviron and write new name/value
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 7e73818..430fc3d 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -31,6 +31,7 @@
import threading
import time
import unittest
+import warnings
@contextlib.contextmanager
@@ -229,6 +230,10 @@
stderr=subprocess.STDOUT)
try:
+ # We get warnings for unclosed files for the subprocess's pipes,
+ # and it's somewhat cumbersome to close them, so just ignore this.
+ warnings.simplefilter("ignore", ResourceWarning)
+
# Run the adb client and have it start the adb server.
proc = subprocess.Popen(["adb", "-P", str(port), "start-server"],
stdin=subprocess.PIPE,
diff --git a/adb/test_device.py b/adb/test_device.py
index c3166ff..34f8fd9 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -750,7 +750,7 @@
if host_dir is not None:
shutil.rmtree(host_dir)
- def test_push_empty(self):
+ def disabled_test_push_empty(self):
"""Push an empty directory to the device."""
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 95df490..03a9f30 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -17,7 +17,6 @@
#define TRACE_TAG TRANSPORT
#include "sysdeps.h"
-#include "sysdeps/memory.h"
#include "transport.h"
@@ -32,6 +31,7 @@
#include <algorithm>
#include <deque>
#include <list>
+#include <memory>
#include <mutex>
#include <set>
#include <thread>
@@ -52,7 +52,6 @@
#include "fdevent.h"
#include "sysdeps/chrono.h"
-static void register_transport(atransport* transport);
static void remove_transport(atransport* transport);
static void transport_unref(atransport* transport);
@@ -67,6 +66,8 @@
const char* const kFeatureStat2 = "stat_v2";
const char* const kFeatureLibusb = "libusb";
const char* const kFeaturePushSync = "push_sync";
+const char* const kFeatureApex = "apex";
+const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
namespace {
@@ -408,42 +409,6 @@
fd_.reset();
}
-static std::string dump_packet(const char* name, const char* func, apacket* p) {
- unsigned command = p->msg.command;
- int len = p->msg.data_length;
- char cmd[9];
- char arg0[12], arg1[12];
- int n;
-
- for (n = 0; n < 4; n++) {
- int b = (command >> (n * 8)) & 255;
- if (b < 32 || b >= 127) break;
- cmd[n] = (char)b;
- }
- if (n == 4) {
- cmd[4] = 0;
- } else {
- /* There is some non-ASCII name in the command, so dump
- * the hexadecimal value instead */
- snprintf(cmd, sizeof cmd, "%08x", command);
- }
-
- if (p->msg.arg0 < 256U)
- snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
- else
- snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
-
- if (p->msg.arg1 < 256U)
- snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
- else
- snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
-
- std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
- func, cmd, arg0, arg1, len);
- result += dump_hex(p->payload.data(), p->payload.size());
- return result;
-}
-
void send_packet(apacket* p, atransport* t) {
p->msg.magic = p->msg.command ^ 0xffffffff;
// compute a checksum for connection/auth packets for compatibility reasons
@@ -456,7 +421,7 @@
VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "to remote", p);
if (t == nullptr) {
- fatal("Transport is null");
+ LOG(FATAL) << "Transport is null";
}
if (t->Write(p) != 0) {
@@ -562,7 +527,7 @@
asocket* create_device_tracker(bool long_output) {
device_tracker* tracker = new device_tracker();
- if (tracker == nullptr) fatal("cannot allocate device tracker");
+ if (tracker == nullptr) LOG(FATAL) << "cannot allocate device tracker";
D("device tracker %p created", tracker);
@@ -668,7 +633,7 @@
}
if (transport_read_action(_fd, &m)) {
- fatal_errno("cannot read transport registration socket");
+ PLOG(FATAL) << "cannot read transport registration socket";
}
t = m.transport;
@@ -707,7 +672,7 @@
return true;
});
t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
- D("%s: connection terminated: %s", t->serial.c_str(), error.c_str());
+ LOG(INFO) << t->serial_name() << ": connection terminated: " << error;
fdevent_run_on_main_thread([t]() {
handle_offline(t);
transport_unref(t);
@@ -742,7 +707,7 @@
int s[2];
if (adb_socketpair(s)) {
- fatal_errno("cannot open transport registration socketpair");
+ PLOG(FATAL) << "cannot open transport registration socketpair";
}
D("socketpair: (%d,%d)", s[0], s[1]);
@@ -766,13 +731,13 @@
}
/* the fdevent select pump is single threaded */
-static void register_transport(atransport* transport) {
+void register_transport(atransport* transport) {
tmsg m;
m.transport = transport;
m.action = 1;
D("transport: %s registered", transport->serial.c_str());
if (transport_write_action(transport_registration_send, &m)) {
- fatal_errno("cannot write transport registration socket\n");
+ PLOG(FATAL) << "cannot write transport registration socket";
}
}
@@ -782,7 +747,7 @@
m.action = 0;
D("transport: %s removed", transport->serial.c_str());
if (transport_write_action(transport_registration_send, &m)) {
- fatal_errno("cannot write transport registration socket\n");
+ PLOG(FATAL) << "cannot write transport registration socket";
}
}
@@ -794,6 +759,7 @@
CHECK_GT(t->ref_count, 0u);
t->ref_count--;
if (t->ref_count == 0) {
+ LOG(INFO) << "destroying transport " << t->serial_name();
t->connection()->Stop();
#if ADB_HOST
if (t->IsTcpDevice() && !t->kicked()) {
@@ -1043,7 +1009,10 @@
const FeatureSet& supported_features() {
// Local static allocation to avoid global non-POD variables.
static const FeatureSet* features = new FeatureSet{
- kFeatureShell2, kFeatureCmd, kFeatureStat2,
+ kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir,
+#if ADB_HOST
+ kFeatureApex
+#endif
// Increment ADB_SERVER_VERSION whenever the feature list changes to
// make sure that the adb client and server features stay in sync
// (http://b/24370690).
@@ -1329,6 +1298,7 @@
register_transport(t);
}
+#if ADB_HOST
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
@@ -1340,6 +1310,7 @@
return false;
});
}
+#endif
bool check_header(apacket* p, atransport* t) {
if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
diff --git a/adb/transport.h b/adb/transport.h
index f854ce5..9894bdf 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -57,8 +57,12 @@
extern const char* const kFeatureStat2;
// The server is running with libusb enabled.
extern const char* const kFeatureLibusb;
-// The server supports `push --sync`.
+// adbd supports `push --sync`.
extern const char* const kFeaturePushSync;
+// adbd supports installing .apex packages.
+extern const char* const kFeatureApex;
+// adbd has b/110953234 fixed.
+extern const char* const kFeatureFixedPushMkdir;
TransportId NextTransportId();
@@ -362,6 +366,7 @@
void kick_all_tcp_devices();
void kick_all_transports();
+void register_transport(atransport* transport);
void register_usb_transport(usb_handle* h, const char* serial,
const char* devpath, unsigned writeable);
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index 85f3c52..ec61279 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -17,6 +17,7 @@
#include <stdint.h>
#include <deque>
+#include <memory>
#include <mutex>
#include <string>
#include <thread>
@@ -28,7 +29,6 @@
#include "adb_unique_fd.h"
#include "adb_utils.h"
#include "sysdeps.h"
-#include "sysdeps/memory.h"
#include "transport.h"
#include "types.h"
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 8353d89..dc87ac7 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -26,6 +26,7 @@
#include <sys/types.h>
#include <condition_variable>
+#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
@@ -45,7 +46,6 @@
#include "adb_unique_fd.h"
#include "adb_utils.h"
#include "sysdeps/chrono.h"
-#include "sysdeps/memory.h"
#if ADB_HOST
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 8c628d8..b66f8fa 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,13 +19,16 @@
#include <gtest/gtest.h>
#include "adb.h"
+#include "fdevent_test.h"
+
+struct TransportTest : public FdeventTest {};
static void DisconnectFunc(void* arg, atransport*) {
int* count = reinterpret_cast<int*>(arg);
++*count;
}
-TEST(transport, RunDisconnects) {
+TEST_F(TransportTest, RunDisconnects) {
atransport t;
// RunDisconnects() can be called with an empty atransport.
t.RunDisconnects();
@@ -49,7 +52,7 @@
ASSERT_EQ(0, count);
}
-TEST(transport, SetFeatures) {
+TEST_F(TransportTest, SetFeatures) {
atransport t;
ASSERT_EQ(0U, t.features().size());
@@ -77,8 +80,7 @@
ASSERT_EQ(0U, t.features().size());
}
-TEST(transport, parse_banner_no_features) {
- set_main_thread();
+TEST_F(TransportTest, parse_banner_no_features) {
atransport t;
parse_banner("host::", &t);
@@ -91,7 +93,7 @@
ASSERT_EQ(std::string(), t.device);
}
-TEST(transport, parse_banner_product_features) {
+TEST_F(TransportTest, parse_banner_product_features) {
atransport t;
const char banner[] =
@@ -107,9 +109,8 @@
ASSERT_EQ(std::string("baz"), t.device);
}
-TEST(transport, parse_banner_features) {
+TEST_F(TransportTest, parse_banner_features) {
atransport t;
-
const char banner[] =
"host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;"
"features=woodly,doodly";
@@ -126,7 +127,7 @@
ASSERT_EQ(std::string("baz"), t.device);
}
-TEST(transport, test_matches_target) {
+TEST_F(TransportTest, test_matches_target) {
std::string serial = "foo";
std::string devpath = "/path/to/bar";
std::string product = "test_product";
@@ -157,7 +158,7 @@
}
}
-TEST(transport, test_matches_target_local) {
+TEST_F(TransportTest, test_matches_target_local) {
std::string serial = "100.100.100.100:5555";
atransport t;
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 602970c..c471bf9 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -16,8 +16,9 @@
#define TRACE_TAG TRANSPORT
+#include <memory>
+
#include "sysdeps.h"
-#include "sysdeps/memory.h"
#include "transport.h"
#include <stdio.h>
diff --git a/adb/types.h b/adb/types.h
index a3e5d48..0c71c3a 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -25,7 +25,6 @@
#include <android-base/logging.h>
-#include "sysdeps/memory.h"
#include "sysdeps/uio.h"
// Essentially std::vector<char>, except without zero initialization or reallocation.
@@ -42,14 +41,14 @@
}
Block(const Block& copy) = delete;
- Block(Block&& move) {
+ Block(Block&& move) noexcept {
std::swap(data_, move.data_);
std::swap(capacity_, move.capacity_);
std::swap(size_, move.size_);
}
Block& operator=(const Block& copy) = delete;
- Block& operator=(Block&& move) {
+ Block& operator=(Block&& move) noexcept {
clear();
std::swap(data_, move.data_);
@@ -147,12 +146,10 @@
}
IOVector(const IOVector& copy) = delete;
- IOVector(IOVector&& move) : IOVector() {
- *this = std::move(move);
- }
+ IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
IOVector& operator=(const IOVector& copy) = delete;
- IOVector& operator=(IOVector&& move) {
+ IOVector& operator=(IOVector&& move) noexcept {
chain_ = std::move(move.chain_);
chain_length_ = move.chain_length_;
begin_offset_ = move.begin_offset_;
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
index 31ab90a..1fbd2ca 100644
--- a/adb/types_test.cpp
+++ b/adb/types_test.cpp
@@ -16,7 +16,7 @@
#include <gtest/gtest.h>
-#include "sysdeps/memory.h"
+#include <memory>
#include "types.h"
static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
diff --git a/base/Android.bp b/base/Android.bp
index daa820a..741664b 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -47,6 +47,7 @@
"chrono_utils.cpp",
"file.cpp",
"logging.cpp",
+ "mapped_file.cpp",
"parsenetaddress.cpp",
"properties.cpp",
"quick_exit.cpp",
@@ -124,6 +125,7 @@
"file_test.cpp",
"logging_test.cpp",
"macros_test.cpp",
+ "mapped_file_test.cpp",
"parsedouble_test.cpp",
"parseint_test.cpp",
"parsenetaddress_test.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index d6fe753..3834ed4 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -38,7 +38,6 @@
#endif
#if defined(_WIN32)
#include <windows.h>
-#define O_CLOEXEC O_NOINHERIT
#define O_NOFOLLOW 0
#endif
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 908690b..86d537d 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -20,13 +20,16 @@
#include <sys/types.h>
#include <string>
+#include "android-base/off64_t.h"
+
#if !defined(_WIN32) && !defined(O_BINARY)
+/** Windows needs O_BINARY, but Unix never mangles line endings. */
#define O_BINARY 0
#endif
-#if defined(__APPLE__)
-/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-typedef off_t off64_t;
+#if defined(_WIN32) && !defined(O_CLOEXEC)
+/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
+#define O_CLOEXEC O_NOINHERIT
#endif
namespace android {
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 1748665..5abf514 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -75,46 +75,6 @@
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions. It's less safe than arraysize as it accepts some
-// (although not all) pointers. Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
-//
-// "warning: division by zero in ..."
-//
-// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element). If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array. Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size. Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-#define ARRAYSIZE_UNSAFE(a) \
- ((sizeof(a) / sizeof(*(a))) / \
- static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-
#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
// Changing this definition will cause you a lot of pain. A majority of
@@ -153,23 +113,23 @@
// case 42:
// ...
//
-// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
-// followed by a semicolon. It is designed to mimic control-flow statements
-// like 'break;', so it can be placed in most places where 'break;' can, but
-// only if there are no statements on the execution path between it and the
-// next switch label.
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
//
-// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
-// [[clang::fallthrough]] attribute, which is analysed when performing switch
-// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
-// documentation on language extensions for details:
-// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+// [[clang::fallthrough]] attribute, which is analysed when performing switch
+// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+// documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
//
-// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
-// effect on diagnostics.
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
//
-// In either case this macro has no effect on runtime behavior and performance
-// of code.
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
#ifndef FALLTHROUGH_INTENDED
#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
#endif
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
new file mode 100644
index 0000000..80513b1
--- /dev/null
+++ b/base/include/android-base/mapped_file.h
@@ -0,0 +1,75 @@
+/*
+ * 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 "android-base/macros.h"
+#include "android-base/off64_t.h"
+
+#include <sys/types.h>
+
+#include <memory>
+
+#if defined(_WIN32)
+#include <windows.h>
+#define PROT_READ 1
+#define PROT_WRITE 2
+#else
+#include <sys/mman.h>
+#endif
+
+namespace android {
+namespace base {
+
+/**
+ * A region of a file mapped into memory.
+ */
+class MappedFile {
+ public:
+ /**
+ * Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives,
+ * `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);
+
+ /**
+ * Removes the mapping.
+ */
+ ~MappedFile();
+
+ char* data() { return base_ + offset_; }
+ size_t size() { return size_; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
+
+ char* base_;
+ size_t size_;
+
+ size_t offset_;
+
+#if defined(_WIN32)
+ MappedFile(char* base, size_t size, size_t offset, HANDLE handle)
+ : base_(base), size_(size), offset_(offset), handle_(handle) {}
+ HANDLE handle_;
+#else
+ MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {}
+#endif
+};
+
+} // namespace base
+} // namespace android
diff --git a/libbacktrace/GetPss.h b/base/include/android-base/off64_t.h
similarity index 74%
rename from libbacktrace/GetPss.h
rename to base/include/android-base/off64_t.h
index 787c33d..e6b71b8 100644
--- a/libbacktrace/GetPss.h
+++ b/base/include/android-base/off64_t.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef _LIBBACKTRACE_GET_PSS_H
-#define _LIBBACKTRACE_GET_PSS_H
+#pragma once
-size_t GetPssBytes();
-
-#endif // _LIBBACKTRACE_GET_PSS_H
+#if defined(__APPLE__)
+/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 9444fdd..be8b97b 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -22,6 +22,7 @@
#include <limits>
#include <string>
+#include <type_traits>
namespace android {
namespace base {
@@ -33,6 +34,7 @@
template <typename T>
bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
bool allow_suffixes = false) {
+ static_assert(std::is_unsigned<T>::value, "ParseUint can only be used with unsigned types");
while (isspace(*s)) {
s++;
}
@@ -96,6 +98,7 @@
bool ParseInt(const char* s, T* out,
T min = std::numeric_limits<T>::min(),
T max = std::numeric_limits<T>::max()) {
+ static_assert(std::is_signed<T>::value, "ParseInt can only be used with signed types");
while (isspace(*s)) {
s++;
}
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index e6a9d10..5a224d6 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -28,7 +28,7 @@
public:
ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
- ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
that.active_ = false;
}
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 9c35560..fc5c1ce 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -61,6 +61,7 @@
bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
bool StartsWith(const std::string& s, const std::string& prefix);
bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool StartsWith(const std::string& s, char prefix);
// Tests whether 's' ends with 'suffix'.
// TODO: string_view
@@ -68,6 +69,7 @@
bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
bool EndsWith(const std::string& s, const std::string& suffix);
bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
+bool EndsWith(const std::string& s, char suffix);
// Tests whether 'lhs' equals 'rhs', ignoring case.
bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 71025ad..cd2dc04 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -90,8 +90,8 @@
explicit unique_fd_impl(int fd) { reset(fd); }
~unique_fd_impl() { reset(); }
- unique_fd_impl(unique_fd_impl&& other) { reset(other.release()); }
- unique_fd_impl& operator=(unique_fd_impl&& s) {
+ unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
+ unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
int fd = s.fd_;
s.fd_ = -1;
reset(fd, &s);
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 4b91623..1a414ec 100644
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -17,6 +17,7 @@
#pragma once
#ifdef _WIN32
+#include <sys/types.h>
#include <string>
#else
// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
diff --git a/base/logging.cpp b/base/logging.cpp
index d60d91d..bd09069 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -53,6 +53,7 @@
#include <unistd.h>
#endif
+#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -71,14 +72,8 @@
static char progname[MAX_PATH] = {};
if (first) {
- CHAR longname[MAX_PATH];
- DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname));
- if ((nchars >= arraysize(longname)) || (nchars == 0)) {
- // String truncation or some other error.
- strcpy(progname, "<unknown>");
- } else {
- strcpy(progname, basename(longname));
- }
+ snprintf(progname, sizeof(progname), "%s",
+ android::base::Basename(android::base::GetExecutablePath()).c_str());
first = false;
}
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
new file mode 100644
index 0000000..f7901af
--- /dev/null
+++ b/base/mapped_file.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 "android-base/mapped_file.h"
+
+namespace android {
+namespace base {
+
+static off64_t InitPageSize() {
+#if defined(_WIN32)
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwAllocationGranularity;
+#else
+ return sysconf(_SC_PAGE_SIZE);
+#endif
+}
+
+std::unique_ptr<MappedFile> MappedFile::FromFd(int 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;
+ off64_t file_length = length + slop;
+
+#if defined(_WIN32)
+ HANDLE handle =
+ CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
+ (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+ if (handle == nullptr) return nullptr;
+ void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
+ file_offset, file_length);
+ if (base == nullptr) {
+ CloseHandle(handle);
+ return nullptr;
+ }
+ 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);
+ if (base == MAP_FAILED) return nullptr;
+ return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
+#endif
+}
+
+MappedFile::~MappedFile() {
+#if defined(_WIN32)
+ if (base_ != nullptr) UnmapViewOfFile(base_);
+ if (handle_ != nullptr) CloseHandle(handle_);
+#else
+ if (base_ != nullptr) munmap(base_, size_);
+#endif
+
+ base_ = nullptr;
+ offset_ = size_ = 0;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
new file mode 100644
index 0000000..57fde6f
--- /dev/null
+++ b/base/mapped_file_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "android-base/mapped_file.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include "android-base/unique_fd.h"
+
+TEST(mapped_file, smoke) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("hello world", tf.fd));
+
+ auto m = android::base::MappedFile::FromFd(tf.fd, 3, 2, PROT_READ);
+ ASSERT_EQ(2u, m->size());
+ ASSERT_EQ('l', m->data()[0]);
+ ASSERT_EQ('o', m->data()[1]);
+}
diff --git a/base/strings.cpp b/base/strings.cpp
index a8bb2a9..2d6eef0 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -95,6 +95,10 @@
return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
}
+bool StartsWith(const std::string& s, char prefix) {
+ return *s.c_str() == prefix; // Use c_str() to guarantee there is at least a '\0'.
+}
+
bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
}
@@ -121,6 +125,10 @@
return EndsWith(s, suffix.c_str(), suffix.size(), true);
}
+bool EndsWith(const std::string& s, char suffix) {
+ return EndsWith(s, &suffix, 1, true);
+}
+
bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
return EndsWith(s, suffix, strlen(suffix), false);
}
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index b8639ea..9d74094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -198,6 +198,12 @@
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
}
+TEST(strings, StartsWith_char) {
+ ASSERT_FALSE(android::base::StartsWith("", 'f'));
+ ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
+ ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
+}
+
TEST(strings, EndsWith_empty) {
ASSERT_FALSE(android::base::EndsWith("", "foo"));
ASSERT_TRUE(android::base::EndsWith("", ""));
@@ -273,6 +279,12 @@
ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
}
+TEST(strings, EndsWith_char) {
+ ASSERT_FALSE(android::base::EndsWith("", 'o'));
+ ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
+ ASSERT_FALSE(android::base::EndsWith("foo", "f"));
+}
+
TEST(strings, EqualsIgnoreCase) {
ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index ed7423b..15c0265 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -250,11 +250,12 @@
}
uint64_t expected = pack_thread_fd(-1, -1);
- if (!trace_output.compare_exchange_strong(expected,
- pack_thread_fd(tid, pipe_write.release()))) {
+ int sent_fd = pipe_write.release();
+ if (!trace_output.compare_exchange_strong(expected, pack_thread_fd(tid, sent_fd))) {
auto [tid, fd] = unpack_thread_fd(expected);
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"thread %d is already outputting to fd %d?", tid, fd);
+ close(sent_fd);
return false;
}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 8bdc02f..d0c5234 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -382,6 +382,8 @@
case TRAP_TRACE: return "TRAP_TRACE";
case TRAP_BRANCH: return "TRAP_BRANCH";
case TRAP_HWBKPT: return "TRAP_HWBKPT";
+ case TRAP_UNK:
+ return "TRAP_UNDIAGNOSED";
}
if ((si->si_code & 0xff) == SIGTRAP) {
switch ((si->si_code >> 8) & 0xff) {
@@ -403,7 +405,7 @@
return "PTRACE_EVENT_STOP";
}
}
- static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+ static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
break;
}
// Then the other codes...
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 2eaf006..705da33 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -63,3 +63,4 @@
#define FB_VAR_VARIANT "variant"
#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
+#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b02d968..6e45133 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -27,6 +27,7 @@
#include <android-base/unique_fd.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <uuid/uuid.h>
@@ -97,6 +98,7 @@
{FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
{FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
{FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+ {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
{FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
if (args.size() < 2) {
@@ -129,6 +131,11 @@
if (args.size() < 2) {
return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
}
+
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
+ }
+
PartitionHandle handle;
if (!OpenPartition(device, args[1], &handle)) {
return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
@@ -161,9 +168,15 @@
if (args.size() < 2) {
return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
}
+
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL,
+ "Download is not allowed on locked devices");
+ }
+
// arg[0] is the command name, arg[1] contains size of data to be downloaded
unsigned int size;
- if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) {
+ if (!android::base::ParseUint("0x" + args[1], &size, kMaxDownloadSizeDefault)) {
return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
}
device->download_data().resize(size);
@@ -201,6 +214,11 @@
return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
}
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL,
+ "set_active command is not allowed on locked devices");
+ }
+
// Slot suffix needs to be between 'a' and 'z'.
Slot slot;
if (!GetSlotNumber(args[1], &slot)) {
@@ -219,7 +237,13 @@
CommandResult ret;
auto cb = [&ret](CommandResult result) { ret = result; };
auto result = boot_control_hal->setActiveBootSlot(slot, cb);
- if (result.isOk() && ret.success) return device->WriteStatus(FastbootResult::OKAY, "");
+ if (result.isOk() && ret.success) {
+ // Save as slot suffix to match the suffix format as returned from
+ // the boot control HAL.
+ auto current_slot = "_" + args[1];
+ device->set_active_slot(current_slot);
+ return device->WriteStatus(FastbootResult::OKAY, "");
+ }
return device->WriteStatus(FastbootResult::FAIL, "Unable to set slot");
}
@@ -310,7 +334,7 @@
};
PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
- auto super_device = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+ auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name());
if (!super_device) {
return;
}
@@ -353,13 +377,7 @@
return device->WriteFail("Partition already exists");
}
- // Make a random UUID, since they're not currently used.
- uuid_t uuid;
- char uuid_str[37];
- uuid_generate_random(uuid);
- uuid_unparse(uuid, uuid_str);
-
- Partition* partition = builder->AddPartition(partition_name, uuid_str, 0);
+ Partition* partition = builder->AddPartition(partition_name, 0);
if (!partition) {
return device->WriteFail("Failed to add partition");
}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 9df43a9..bb1f988 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -19,6 +19,8 @@
#include <string>
#include <vector>
+constexpr unsigned int kMaxDownloadSizeDefault = 0x20000000;
+
class FastbootDevice;
enum class FastbootResult {
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index b843c05..7be721a 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -57,7 +57,8 @@
transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()),
health_hal_(get_health_service()),
- fastboot_hal_(IFastboot::getService()) {}
+ fastboot_hal_(IFastboot::getService()),
+ active_slot_("") {}
FastbootDevice::~FastbootDevice() {
CloseDevice();
@@ -68,6 +69,11 @@
}
std::string FastbootDevice::GetCurrentSlot() {
+ // Check if a set_active ccommand was issued earlier since the boot control HAL
+ // returns the slot that is currently booted into.
+ if (!active_slot_.empty()) {
+ return active_slot_;
+ }
// Non-A/B devices must not have boot control HALs.
if (!boot_control_hal_) {
return "";
@@ -131,7 +137,7 @@
std::string cmd_name;
if (android::base::StartsWith(command, "oem ")) {
args = {command};
- cmd_name = "oem";
+ cmd_name = FB_CMD_OEM;
} else {
args = android::base::Split(command, ":");
cmd_name = args[0];
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 2eb7177..091aadf 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -56,6 +56,8 @@
}
android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
+ void set_active_slot(const std::string& active_slot) { active_slot_ = active_slot; }
+
private:
const std::unordered_map<std::string, CommandHandler> kCommandMap;
@@ -64,4 +66,5 @@
android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
+ std::string active_slot_;
};
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index a383c54..66b90bf 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -21,10 +21,15 @@
#include <algorithm>
#include <memory>
+#include <set>
+#include <string>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <sparse/sparse.h>
@@ -32,13 +37,35 @@
#include "fastboot_device.h"
#include "utility.h"
+using namespace android::fs_mgr;
+using namespace std::literals;
+
namespace {
constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a;
-} // namespace
+void WipeOverlayfsForPartition(FastbootDevice* device, const std::string& partition_name) {
+ // May be called, in the case of sparse data, multiple times so cache/skip.
+ static std::set<std::string> wiped;
+ if (wiped.find(partition_name) != wiped.end()) return;
+ wiped.insert(partition_name);
+ // Following appears to have a first time 2% impact on flashing speeds.
-using namespace android::fs_mgr;
+ // Convert partition_name to a validated mount point and wipe.
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+ for (auto i = 0; i < fstab->num_entries; i++) {
+ const auto mount_point = fstab->recs[i].mount_point;
+ if (!mount_point) continue;
+ auto partition = android::base::Basename(mount_point);
+ if ("/"s == mount_point) partition = "system";
+ if ((partition + device->GetCurrentSlot()) == partition_name) {
+ fs_mgr_overlayfs_teardown(mount_point);
+ }
+ }
+}
+
+} // namespace
int FlashRawDataChunk(int fd, const char* data, size_t len) {
size_t ret = 0;
@@ -72,7 +99,7 @@
}
int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
- struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, true);
+ struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
if (!file) {
return -ENOENT;
}
@@ -101,15 +128,11 @@
} else if (data.size() > get_block_device_size(handle.fd())) {
return -EOVERFLOW;
}
+ WipeOverlayfsForPartition(device, partition_name);
return FlashBlockDevice(handle.fd(), data);
}
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe) {
- std::optional<std::string> super = FindPhysicalPartition(partition_name);
- if (!super) {
- return device->WriteFail("Could not find partition: " + partition_name);
- }
-
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
std::vector<char> data = std::move(device->download_data());
if (data.empty()) {
return device->WriteFail("No data available");
@@ -125,48 +148,17 @@
// image.
std::string slot_suffix = device->GetCurrentSlot();
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
- std::unique_ptr<LpMetadata> metadata = ReadMetadata(super->c_str(), slot_number);
- if (!metadata || wipe) {
- if (!FlashPartitionTable(super.value(), *new_metadata.get())) {
+ if (wipe || !ReadMetadata(super_name, slot_number)) {
+ if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
return device->WriteOkay("Successfully flashed partition table");
}
- // There's a working super partition, and we don't want to wipe it - it may
- // may contain partitions created for the user. Instead, we create a zero-
- // sized partition for each entry in the new partition table. It is then
- // the host's responsibility to size it correctly via resize-logical-partition.
- std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*metadata.get());
- if (!builder) {
- return device->WriteFail("Unable to create a metadata builder");
- }
- for (const auto& partition : new_metadata->partitions) {
- std::string name = GetPartitionName(partition);
- if (builder->FindPartition(name)) {
- continue;
- }
- std::string guid = GetPartitionGuid(partition);
- if (!builder->AddPartition(name, guid, partition.attributes)) {
- return device->WriteFail("Unable to add partition: " + name);
- }
- }
-
- // The scratch partition may exist as temporary storage, created for
- // use by adb remount for overlayfs. If we're performing a flashall
- // operation then we want to start over with a clean slate, so we
- // remove the scratch partition until it is requested again.
- builder->RemovePartition("scratch");
-
- new_metadata = builder->Export();
- if (!new_metadata) {
- return device->WriteFail("Unable to export new partition table");
- }
-
// Write the new table to every metadata slot.
bool ok = true;
for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
- ok &= UpdatePartitionTable(super.value(), *new_metadata.get(), i);
+ ok &= UpdatePartitionTable(super_name, *new_metadata.get(), i);
}
if (!ok) {
diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h
index 89e20fc..b15f28b 100644
--- a/fastboot/device/flashing.h
+++ b/fastboot/device/flashing.h
@@ -22,4 +22,4 @@
class FastbootDevice;
int Flash(FastbootDevice* device, const std::string& partition_name);
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe);
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe);
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 02f6f2c..b844b9f 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -23,6 +23,8 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <liblp/liblp.h>
@@ -44,7 +46,7 @@
static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
PartitionHandle* handle) {
- std::optional<std::string> path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+ std::optional<std::string> path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
if (!path) {
return false;
}
@@ -81,6 +83,10 @@
}
std::optional<std::string> FindPhysicalPartition(const std::string& name) {
+ // Check for an invalid file name
+ if (android::base::StartsWith(name, "../") || name.find("/../") != std::string::npos) {
+ return {};
+ }
std::string path = "/dev/block/by-name/" + name;
if (access(path.c_str(), W_OK) < 0) {
return {};
@@ -100,7 +106,7 @@
bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
bool* is_zero_length) {
- auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+ auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
if (!path) {
return false;
}
@@ -149,7 +155,7 @@
}
// Next get logical partitions.
- if (auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME)) {
+ if (auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name())) {
uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
for (const auto& partition : metadata->partitions) {
@@ -163,6 +169,9 @@
bool GetDeviceLockStatus() {
std::string cmdline;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ // Return lock status true if unable to read kernel command line.
+ if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+ return true;
+ }
return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
}
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 01415d7..cbd2856 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -36,7 +36,6 @@
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
-constexpr int kMaxDownloadSizeDefault = 0x20000000;
constexpr char kFastbootProtocolVersion[] = "0.4";
bool GetVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
@@ -96,6 +95,56 @@
return true;
}
+bool GetBatteryVoltageHelper(FastbootDevice* device, int32_t* battery_voltage) {
+ using android::hardware::health::V2_0::HealthInfo;
+ using android::hardware::health::V2_0::Result;
+
+ auto health_hal = device->health_hal();
+ if (!health_hal) {
+ return false;
+ }
+
+ Result ret;
+ auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
+ *battery_voltage = info.legacy.batteryVoltage;
+ ret = result;
+ });
+ if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ int32_t battery_voltage = 0;
+ if (!GetBatteryVoltageHelper(device, &battery_voltage)) {
+ *message = "Unable to read battery voltage";
+ return false;
+ }
+
+ auto fastboot_hal = device->fastboot_hal();
+ if (!fastboot_hal) {
+ *message = "Fastboot HAL not found";
+ return false;
+ }
+
+ Result ret;
+ auto ret_val = fastboot_hal->getBatteryVoltageFlashingThreshold(
+ [&](int32_t voltage_threshold, Result result) {
+ *message = battery_voltage >= voltage_threshold ? "yes" : "no";
+ ret = result;
+ });
+
+ if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+ *message = "Unable to get battery voltage flashing threshold";
+ return false;
+ }
+
+ return true;
+}
+
bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& /* args */,
std::string* message) {
auto fastboot_hal = device->fastboot_hal();
@@ -120,26 +169,13 @@
bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& /* args */,
std::string* message) {
- using android::hardware::health::V2_0::HealthInfo;
- using android::hardware::health::V2_0::Result;
-
- auto health_hal = device->health_hal();
- if (!health_hal) {
- *message = "Health HAL not found";
- return false;
+ int32_t battery_voltage = 0;
+ if (GetBatteryVoltageHelper(device, &battery_voltage)) {
+ *message = std::to_string(battery_voltage);
+ return true;
}
-
- Result ret;
- auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
- *message = std::to_string(info.legacy.batteryVoltage);
- ret = result;
- });
- if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
- *message = "Unable to get battery voltage";
- return false;
- }
-
- return true;
+ *message = "Unable to get battery voltage";
+ return false;
}
bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
@@ -252,7 +288,7 @@
bool is_zero_length;
if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
is_zero_length) {
- *message = "0";
+ *message = "0x0";
return true;
}
// Otherwise, open the partition as normal.
@@ -272,7 +308,14 @@
*message = "Missing argument";
return false;
}
+
std::string partition_name = args[0];
+ if (!FindPhysicalPartition(partition_name) &&
+ !LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+ *message = "Invalid partition";
+ return false;
+ }
+
auto fastboot_hal = device->fastboot_hal();
if (!fastboot_hal) {
*message = "Fastboot HAL not found";
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index e7c3c7c..59b71e8 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -57,6 +57,9 @@
std::string* message);
bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5962650..3c6b1b7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -64,6 +64,7 @@
#include <ziparchive/zip_archive.h>
#include "bootimg_utils.h"
+#include "constants.h"
#include "diagnose_usb.h"
#include "fastboot_driver.h"
#include "fs.h"
@@ -78,10 +79,6 @@
using android::base::Trim;
using android::base::unique_fd;
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
static const char* serial = nullptr;
static bool g_long_listing = false;
@@ -155,10 +152,10 @@
{ nullptr, "system_other.img", "system.sig", "system", true, ImageType::Normal },
{ "userdata", "userdata.img", "userdata.sig", "userdata", true, ImageType::Extra },
{ "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, ImageType::BootCritical },
- { "vbmeta_mainline",
- "vbmeta_mainline.img",
- "vbmeta_mainline.sig",
- "vbmeta_mainline",
+ { "vbmeta_system",
+ "vbmeta_system.img",
+ "vbmeta_system.sig",
+ "vbmeta_system",
true, ImageType::BootCritical },
{ "vendor", "vendor.img", "vendor.sig", "vendor", true, ImageType::Normal },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
@@ -855,7 +852,7 @@
buf->image_size = sz;
}
- lseek64(fd, 0, SEEK_SET);
+ lseek(fd, 0, SEEK_SET);
int64_t limit = get_sparse_limit(sz);
if (limit) {
sparse_file** s = load_sparse_files(fd, limit);
@@ -1095,12 +1092,20 @@
}
}
+static bool is_logical(const std::string& partition) {
+ std::string value;
+ return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+}
+
static void do_flash(const char* pname, const char* fname) {
struct fastboot_buffer buf;
if (!load_buf(fname, &buf)) {
die("cannot load '%s': %s", fname, strerror(errno));
}
+ if (is_logical(pname)) {
+ fb->ResizePartition(pname, std::to_string(buf.image_size));
+ }
flash_buf(pname, &buf);
}
@@ -1143,11 +1148,6 @@
return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
}
-static bool is_logical(const std::string& partition) {
- std::string value;
- return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
-}
-
static void reboot_to_userspace_fastboot() {
fb->RebootTo("fastboot");
@@ -1315,6 +1315,9 @@
if (!is_userspace_fastboot()) {
reboot_to_userspace_fastboot();
}
+ if (!is_userspace_fastboot()) {
+ die("Failed to boot into userspace; one or more components might be unbootable.");
+ }
fb->Download("super", fd, get_file_size(fd));
std::string command = "update-super:super";
@@ -1697,10 +1700,10 @@
while (!args.empty()) {
std::string command = next_arg(&args);
- if (command == "getvar") {
+ if (command == FB_CMD_GETVAR) {
std::string variable = next_arg(&args);
DisplayVarOrError(variable, variable);
- } else if (command == "erase") {
+ } else if (command == FB_CMD_ERASE) {
std::string partition = next_arg(&args);
auto erase = [&](const std::string& partition) {
std::string partition_type;
@@ -1742,7 +1745,7 @@
if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
fb->Download("signature", data);
fb->RawCommand("signature", "installing signature");
- } else if (command == "reboot") {
+ } else if (command == FB_CMD_REBOOT) {
wants_reboot = true;
if (args.size() == 1) {
@@ -1762,15 +1765,15 @@
}
if (!args.empty()) syntax_error("junk after reboot command");
- } else if (command == "reboot-bootloader") {
+ } else if (command == FB_CMD_REBOOT_BOOTLOADER) {
wants_reboot_bootloader = true;
- } else if (command == "reboot-recovery") {
+ } else if (command == FB_CMD_REBOOT_RECOVERY) {
wants_reboot_recovery = true;
- } else if (command == "reboot-fastboot") {
+ } else if (command == FB_CMD_REBOOT_FASTBOOT) {
wants_reboot_fastboot = true;
- } else if (command == "continue") {
+ } else if (command == FB_CMD_CONTINUE) {
fb->Continue();
- } else if (command == "boot") {
+ } else if (command == FB_CMD_BOOT) {
std::string kernel = next_arg(&args);
std::string ramdisk;
if (!args.empty()) ramdisk = next_arg(&args);
@@ -1780,7 +1783,7 @@
auto data = LoadBootableImage(kernel, ramdisk, second_stage);
fb->Download("boot.img", data);
fb->Boot();
- } else if (command == "flash") {
+ } else if (command == FB_CMD_FLASH) {
std::string pname = next_arg(&args);
std::string fname;
@@ -1827,7 +1830,7 @@
}
do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
wants_reboot = true;
- } else if (command == "set_active") {
+ } else if (command == FB_CMD_SET_ACTIVE) {
std::string slot = verify_slot(next_arg(&args), false);
fb->SetActive(slot);
} else if (command == "stage") {
@@ -1841,8 +1844,8 @@
} else if (command == "get_staged") {
std::string filename = next_arg(&args);
fb->Upload(filename);
- } else if (command == "oem") {
- do_oem_command("oem", &args);
+ } else if (command == FB_CMD_OEM) {
+ do_oem_command(FB_CMD_OEM, &args);
} else if (command == "flashing") {
if (args.empty()) {
syntax_error("missing 'flashing' command");
@@ -1854,14 +1857,14 @@
} else {
syntax_error("unknown 'flashing' command %s", args[0].c_str());
}
- } else if (command == "create-logical-partition") {
+ } else if (command == FB_CMD_CREATE_PARTITION) {
std::string partition = next_arg(&args);
std::string size = next_arg(&args);
fb->CreatePartition(partition, size);
- } else if (command == "delete-logical-partition") {
+ } else if (command == FB_CMD_DELETE_PARTITION) {
std::string partition = next_arg(&args);
fb->DeletePartition(partition);
- } else if (command == "resize-logical-partition") {
+ } else if (command == FB_CMD_RESIZE_PARTITION) {
std::string partition = next_arg(&args);
std::string size = next_arg(&args);
fb->ResizePartition(partition, size);
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index b1f3bc9..65a5247 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -41,10 +41,10 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/mapped_file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <utils/FileMap.h>
#include "constants.h"
#include "transport.h"
@@ -467,15 +467,14 @@
while (remaining) {
// Memory map the file
- android::FileMap filemap;
size_t len = std::min(remaining, MAX_MAP_SIZE);
-
- if (!filemap.create(NULL, fd, offset, len, true)) {
+ auto mapping{android::base::MappedFile::FromFd(fd, offset, len, PROT_READ)};
+ if (!mapping) {
error_ = "Creating filemap failed";
return IO_ERROR;
}
- if ((ret = SendBuffer(filemap.getDataPtr(), len))) {
+ if ((ret = SendBuffer(mapping->data(), mapping->size()))) {
return ret;
}
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 62bbe52..af02637 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -37,15 +37,14 @@
#include <bootimg.h>
#include <inttypes.h>
#include <sparse/sparse.h>
+
+#include "constants.h"
#include "transport.h"
class Transport;
namespace fastboot {
-static constexpr int FB_COMMAND_SZ = 64;
-static constexpr int FB_RESPONSE_SZ = 64;
-
enum RetCode : int {
SUCCESS = 0,
BAD_ARG,
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index c18af1d..280cfb6 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -86,6 +86,12 @@
return true;
}
+bool FastBootTest::UserSpaceFastboot() {
+ std::string value;
+ fb->GetVar("is-userspace", &value);
+ return value == "yes";
+}
+
RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
std::vector<std::string>* info) {
return fb->DownloadCommand(size, response, info);
@@ -158,6 +164,12 @@
return;
}
+ // User space fastboot implementations are not allowed to communicate to
+ // secure hardware and hence cannot lock/unlock the device.
+ if (UserSpaceFastboot()) {
+ return;
+ }
+
std::string resp;
std::vector<std::string> info;
// To avoid risk of bricking device, make sure unlock ability is set to 1
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index e47d0fd..e0f829e 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -47,6 +47,7 @@
static int MatchFastboot(usb_ifc_info* info, const char* local_serial = nullptr);
bool UsbStillAvailible();
+ bool UserSpaceFastboot();
protected:
RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 90a2e74..479a06a 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -43,6 +43,7 @@
#include <thread>
#include <vector>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <sparse/sparse.h>
@@ -289,6 +290,12 @@
TEST_F(Conformance, UnlockAbility) {
std::string resp;
std::vector<std::string> info;
+ // Userspace fastboot implementations do not have a way to get this
+ // information.
+ if (UserSpaceFastboot()) {
+ GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+ return;
+ }
EXPECT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
<< "'flashing get_unlock_ability' failed";
// There are two ways this can be reported, through info or the actual response
@@ -325,8 +332,9 @@
<< cmd + " responded with a string with leading whitespace";
EXPECT_FALSE(resp.compare(0, 2, "0x"))
<< cmd + "responded with a string that does not start with 0x...";
- int64_t size = strtoll(resp.c_str(), nullptr, 16);
- EXPECT_GT(size, 0) << "'" + resp + "' is not a valid response from " + cmd;
+ uint64_t size;
+ ASSERT_TRUE(android::base::ParseUint(resp, &size))
+ << "'" + resp + "' is not a valid response from " + cmd;
}
}
@@ -412,6 +420,10 @@
ASSERT_TRUE(resp == "yes" || resp == "no")
<< "Device did not respond with 'yes' or 'no' for getvar:unlocked";
bool curr = resp == "yes";
+ if (UserSpaceFastboot()) {
+ GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+ return;
+ }
for (int i = 0; i < 2; i++) {
std::string action = !curr ? "unlock" : "lock";
@@ -735,7 +747,7 @@
}
TEST_F(Fuzz, BadCommandTooLarge) {
- std::string s = RandomString(fastboot::FB_COMMAND_SZ + 1, rand_legal);
+ std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);
EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
<< "Device did not respond with failure after sending length " << s.size()
<< " string of random ASCII chars";
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index d29ccf4..ae2e2fe 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -37,6 +37,7 @@
#include <memory>
#include <string>
#include <thread>
+#include <utility>
#include <vector>
#include <android-base/file.h>
@@ -49,12 +50,12 @@
#include <cutils/partition_utils.h>
#include <cutils/properties.h>
#include <ext4_utils/ext4.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <ext4_utils/ext4_sb.h>
#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/wipe.h>
#include <fs_mgr_overlayfs.h>
#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
#include <linux/fs.h>
#include <linux/loop.h>
#include <linux/magic.h>
@@ -529,8 +530,18 @@
errno = 0;
ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
save_errno = errno;
- PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
- << ",type=" << rec->fs_type << ")=" << ret;
+ const char* target_missing = "";
+ const char* source_missing = "";
+ if (save_errno == ENOENT) {
+ if (access(target, F_OK)) {
+ target_missing = "(missing)";
+ } else if (access(source, F_OK)) {
+ source_missing = "(missing)";
+ }
+ errno = save_errno;
+ }
+ PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
+ << target_missing << ",type=" << rec->fs_type << ")=" << ret;
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
fs_mgr_set_blk_ro(source);
}
@@ -839,23 +850,115 @@
return true;
}
-bool fs_mgr_update_checkpoint_partition(struct fstab_rec* rec) {
- if (fs_mgr_is_checkpoint(rec)) {
- if (!strcmp(rec->fs_type, "f2fs")) {
- std::string opts(rec->fs_options);
+class CheckpointManager {
+ public:
+ CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
- opts += ",checkpoint=disable";
- free(rec->fs_options);
- rec->fs_options = strdup(opts.c_str());
- } else {
- LERROR << rec->fs_type << " does not implement checkpoints.";
+ bool Update(struct fstab_rec* rec) {
+ if (!fs_mgr_is_checkpoint(rec)) {
+ return true;
}
- } else if (rec->fs_mgr_flags & MF_CHECKPOINT_BLK) {
- LERROR << "Block based checkpoint not implemented.";
- return false;
+
+ if (fs_mgr_is_checkpoint_blk(rec)) {
+ call_vdc({"checkpoint", "restoreCheckpoint", rec->blk_device});
+ }
+
+ if (needs_checkpoint_ == UNKNOWN &&
+ !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
+ LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+ needs_checkpoint_ = NO;
+ }
+
+ if (needs_checkpoint_ != YES) {
+ return true;
+ }
+
+ if (!UpdateCheckpointPartition(rec)) {
+ LERROR << "Could not set up checkpoint partition, skipping!";
+ return false;
+ }
+
+ return true;
}
- return true;
-}
+
+ bool Revert(struct fstab_rec* rec) {
+ if (!fs_mgr_is_checkpoint(rec)) {
+ return true;
+ }
+
+ if (device_map_.find(rec->blk_device) == device_map_.end()) {
+ return true;
+ }
+
+ std::string bow_device = rec->blk_device;
+ free(rec->blk_device);
+ rec->blk_device = strdup(device_map_[bow_device].c_str());
+ device_map_.erase(bow_device);
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.DeleteDevice("bow")) {
+ PERROR << "Failed to remove bow device";
+ }
+
+ return true;
+ }
+
+ private:
+ bool UpdateCheckpointPartition(struct fstab_rec* rec) {
+ if (fs_mgr_is_checkpoint_fs(rec)) {
+ if (!strcmp(rec->fs_type, "f2fs")) {
+ std::string opts(rec->fs_options);
+
+ opts += ",checkpoint=disable";
+ free(rec->fs_options);
+ rec->fs_options = strdup(opts.c_str());
+ } else {
+ LERROR << rec->fs_type << " does not implement checkpoints.";
+ }
+ } else if (fs_mgr_is_checkpoint_blk(rec)) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(rec->blk_device, O_RDONLY | O_CLOEXEC)));
+ if (!fd) {
+ PERROR << "Cannot open device " << rec->blk_device;
+ return false;
+ }
+
+ uint64_t size = get_block_device_size(fd) / 512;
+ if (!size) {
+ PERROR << "Cannot get device size";
+ return false;
+ }
+
+ android::dm::DmTable table;
+ if (!table.AddTarget(
+ std::make_unique<android::dm::DmTargetBow>(0, size, rec->blk_device))) {
+ LERROR << "Failed to add bow target";
+ return false;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice("bow", table)) {
+ PERROR << "Failed to create bow device";
+ return false;
+ }
+
+ std::string name;
+ if (!dm.GetDmDevicePathByName("bow", &name)) {
+ PERROR << "Failed to get bow device name";
+ return false;
+ }
+
+ device_map_[name] = rec->blk_device;
+ free(rec->blk_device);
+ rec->blk_device = strdup(name.c_str());
+ }
+ return true;
+ }
+
+ enum { UNKNOWN = -1, NO = 0, YES = 1 };
+ int needs_checkpoint_;
+ std::map<std::string, std::string> device_map_;
+};
/* When multiple fstab records share the same mount_point, it will
* try to mount each one in turn, and ignore any duplicates after a
@@ -869,7 +972,7 @@
int mret = -1;
int mount_errno = 0;
int attempted_idx = -1;
- int need_checkpoint = -1;
+ CheckpointManager checkpoint_manager;
FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
@@ -916,16 +1019,8 @@
}
}
- if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
- if (need_checkpoint == -1 &&
- !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
- LERROR << "Failed to find if checkpointing is needed. Assuming no.";
- need_checkpoint = 0;
- }
- if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
- LERROR << "Could not set up checkpoint partition, skipping!";
- continue;
- }
+ if (!checkpoint_manager.Update(&fstab->recs[i])) {
+ continue;
}
if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
@@ -1008,6 +1103,9 @@
<< " is wiped and " << fstab->recs[top_idx].mount_point
<< " " << fstab->recs[top_idx].fs_type
<< " is formattable. Format it.";
+
+ checkpoint_manager.Revert(&fstab->recs[top_idx]);
+
if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
@@ -1128,11 +1226,12 @@
* in turn, and stop on 1st success, or no more match.
*/
static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
- char* tmp_mount_point, int need_checkpoint) {
+ char* tmp_mount_point, int needs_checkpoint) {
int i = 0;
int mount_errors = 0;
int first_mount_errno = 0;
char* mount_point;
+ CheckpointManager checkpoint_manager(needs_checkpoint);
FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
@@ -1161,16 +1260,9 @@
}
}
- if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
- if (need_checkpoint == -1 &&
- !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
- LERROR << "Failed to find if checkpointing is needed. Assuming no.";
- need_checkpoint = 0;
- }
- if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
- LERROR << "Could not set up checkpoint partition, skipping!";
- continue;
- }
+ if (!checkpoint_manager.Update(&fstab->recs[i])) {
+ LERROR << "Could not set up checkpoint partition, skipping!";
+ continue;
}
/* First check the filesystem if requested */
@@ -1247,8 +1339,8 @@
}
int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
- bool needs_cp) {
- return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_cp);
+ bool needs_checkpoint) {
+ return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
}
/*
@@ -1300,29 +1392,25 @@
* on a system (all the memory comes from the same pool) so
* we can assume the device number is 0.
*/
- FILE *zram_fp;
- FILE *zram_mcs_fp;
-
if (fstab->recs[i].max_comp_streams >= 0) {
- zram_mcs_fp = fopen(ZRAM_CONF_MCS, "r+");
- if (zram_mcs_fp == NULL) {
- LERROR << "Unable to open zram conf comp device "
- << ZRAM_CONF_MCS;
- ret = -1;
- continue;
- }
- fprintf(zram_mcs_fp, "%d\n", fstab->recs[i].max_comp_streams);
- fclose(zram_mcs_fp);
+ auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{
+ fopen(ZRAM_CONF_MCS, "re"), fclose};
+ if (zram_mcs_fp == NULL) {
+ LERROR << "Unable to open zram conf comp device " << ZRAM_CONF_MCS;
+ ret = -1;
+ continue;
+ }
+ fprintf(zram_mcs_fp.get(), "%d\n", fstab->recs[i].max_comp_streams);
}
- zram_fp = fopen(ZRAM_CONF_DEV, "r+");
+ auto zram_fp =
+ std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, "re+"), fclose};
if (zram_fp == NULL) {
LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
ret = -1;
continue;
}
- fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
- fclose(zram_fp);
+ fprintf(zram_fp.get(), "%u\n", fstab->recs[i].zram_size);
}
if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
@@ -1457,19 +1545,18 @@
DeviceMapper& dm = DeviceMapper::Instance();
- bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";
-
for (int i = 0; i < fstab->num_entries; i++) {
- if (!fs_mgr_is_verified(&fstab->recs[i]) && !fs_mgr_is_avb(&fstab->recs[i])) {
+ auto fsrec = &fstab->recs[i];
+ if (!fs_mgr_is_verified(fsrec) && !fs_mgr_is_avb(fsrec)) {
continue;
}
std::string mount_point;
- if (system_root && !strcmp(fstab->recs[i].mount_point, "/")) {
+ if (!strcmp(fsrec->mount_point, "/")) {
// In AVB, the dm device name is vroot instead of system.
- mount_point = fs_mgr_is_avb(&fstab->recs[i]) ? "vroot" : "system";
+ mount_point = fs_mgr_is_avb(fsrec) ? "vroot" : "system";
} else {
- mount_point = basename(fstab->recs[i].mount_point);
+ mount_point = basename(fsrec->mount_point);
}
if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
@@ -1477,15 +1564,14 @@
continue;
}
- const char* status = nullptr;
+ const char* status;
std::vector<DeviceMapper::TargetInfo> table;
if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
- if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
- status = "V";
- } else {
- PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point.c_str();
+ if (!fs_mgr_is_verifyatboot(fsrec)) {
+ PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point;
continue;
}
+ status = "V";
} else {
status = table[0].data.c_str();
}
@@ -1495,9 +1581,13 @@
// instead of [partition.vroot.verified].
if (mount_point == "vroot") mount_point = "system";
if (*status == 'C' || *status == 'V') {
- callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
+ callback(fsrec, mount_point.c_str(), mode, *status);
}
}
return true;
}
+
+std::string fs_mgr_get_super_partition_name(int /* slot */) {
+ return LP_METADATA_DEFAULT_PARTITION_NAME;
+}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 804069a..4dacebf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -33,6 +33,7 @@
#include <sstream>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -50,8 +51,21 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
- const LpMetadataPartition& partition, DmTable* table) {
+bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device,
+ std::string* result) {
+ // Note: device-mapper will not accept symlinks, so we must use realpath
+ // here.
+ std::string name = GetBlockDevicePartitionName(block_device);
+ std::string path = "/dev/block/by-name/" + name;
+ if (!android::base::Realpath(path, result)) {
+ PERROR << "realpath: " << path;
+ return false;
+ }
+ return true;
+}
+
+static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
+ DmTable* table) {
uint64_t sector = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -60,10 +74,17 @@
case LP_TARGET_TYPE_ZERO:
target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
break;
- case LP_TARGET_TYPE_LINEAR:
- target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+ case LP_TARGET_TYPE_LINEAR: {
+ const auto& block_device = metadata.block_devices[extent.target_source];
+ std::string path;
+ if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
+ LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
+ return false;
+ }
+ target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
extent.target_data);
break;
+ }
default:
LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
return false;
@@ -79,13 +100,13 @@
return true;
}
-static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
- const LpMetadataPartition& partition, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
+static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
+ bool force_writable, const std::chrono::milliseconds& timeout_ms,
+ std::string* path) {
DeviceMapper& dm = DeviceMapper::Instance();
DmTable table;
- if (!CreateDmTable(block_device, metadata, partition, &table)) {
+ if (!CreateDmTable(metadata, partition, &table)) {
return false;
}
if (force_writable) {
@@ -122,7 +143,7 @@
continue;
}
std::string path;
- if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) {
+ if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
@@ -140,8 +161,8 @@
}
for (const auto& partition : metadata->partitions) {
if (GetPartitionName(partition) == partition_name) {
- return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
- timeout_ms, path);
+ return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms,
+ path);
}
}
LERROR << "Could not find any partition with name: " << partition_name;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 250793a..fc3a05c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -732,21 +732,19 @@
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{
- FILE *fstab_file;
struct fstab *fstab;
- fstab_file = fopen(fstab_path, "r");
+ auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fstab_path, "re"), fclose};
if (!fstab_file) {
PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
return nullptr;
}
- fstab = fs_mgr_read_fstab_file(fstab_file, !strcmp("/proc/mounts", fstab_path));
+ fstab = fs_mgr_read_fstab_file(fstab_file.get(), !strcmp("/proc/mounts", fstab_path));
if (!fstab) {
LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
}
- fclose(fstab_file);
return fstab;
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 07b2a7a..2ac638a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -29,6 +29,7 @@
#include <sys/vfs.h>
#include <unistd.h>
+#include <algorithm>
#include <map>
#include <memory>
#include <string>
@@ -40,19 +41,38 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
#include "fs_mgr_priv.h"
using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
-bool fs_mgr_overlayfs_mount_all(const fstab*) {
+bool fs_mgr_overlayfs_mount_all(fstab*) {
return false;
}
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>&) {
+ return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
+ return {};
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const std::vector<const fstab_rec*>&) {
+ return {};
+}
+
bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change) {
if (change) *change = false;
return false;
@@ -67,8 +87,10 @@
namespace {
-// acceptable overlayfs backing storage
-const auto kOverlayMountPoint = "/cache"s;
+// list of acceptable overlayfs backing storage
+const auto kScratchMountPoint = "/mnt/scratch"s;
+const auto kCacheMountPoint = "/cache"s;
+const std::vector<const std::string> kOverlayMountPoints = {kScratchMountPoint, kCacheMountPoint};
// Return true if everything is mounted, but before adb is started. Right
// after 'trigger load_persist_props_action' is done.
@@ -114,13 +136,63 @@
return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
}
-bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
// readonly filesystem, can not be mount -o remount,rw
// if squashfs or if free space is (near) zero making such a remount
// virtually useless, or if there are shared blocks that prevent remount,rw
- return ("squashfs"s == fsrec->fs_type) ||
- fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
- !fs_mgr_filesystem_has_space(fsrec->mount_point);
+ if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+ return true;
+ }
+ if (fs_mgr_is_logical(fsrec)) {
+ fs_mgr_update_logical_partition(fsrec);
+ }
+ return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
+}
+
+bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
+ auto save_errno = errno;
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+ if (!dir) {
+ if (errno == ENOENT) {
+ errno = save_errno;
+ return true;
+ }
+ PERROR << "opendir " << path << " depth=" << level;
+ if ((errno == EPERM) && (level != 0)) {
+ errno = save_errno;
+ return true;
+ }
+ return false;
+ }
+ dirent* entry;
+ auto ret = true;
+ while ((entry = readdir(dir.get()))) {
+ if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+ auto file = path + "/" + entry->d_name;
+ if (entry->d_type == DT_UNKNOWN) {
+ struct stat st;
+ save_errno = errno;
+ if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+ errno = save_errno;
+ }
+ if (entry->d_type == DT_DIR) {
+ ret &= fs_mgr_rm_all(file, change, level + 1);
+ if (!rmdir(file.c_str())) {
+ if (change) *change = true;
+ } else {
+ if (errno != ENOENT) ret = false;
+ PERROR << "rmdir " << file << " depth=" << level;
+ }
+ continue;
+ }
+ if (!unlink(file.c_str())) {
+ if (change) *change = true;
+ } else {
+ if (errno != ENOENT) ret = false;
+ PERROR << "rm " << file << " depth=" << level;
+ }
+ }
+ return ret;
}
const auto kUpperName = "upper"s;
@@ -129,14 +201,17 @@
std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
if (!fs_mgr_is_dir(mount_point)) return "";
- auto dir =
- kOverlayMountPoint + kOverlayTopDir + "/" + android::base::Basename(mount_point) + "/";
- auto upper = dir + kUpperName;
- if (!fs_mgr_is_dir(upper)) return "";
- auto work = dir + kWorkName;
- if (!fs_mgr_is_dir(work)) return "";
- if (!fs_mgr_dir_is_writable(work)) return "";
- return dir;
+ const auto base = android::base::Basename(mount_point) + "/";
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
+ auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
+ auto upper = dir + kUpperName;
+ if (!fs_mgr_is_dir(upper)) continue;
+ auto work = dir + kWorkName;
+ if (!fs_mgr_is_dir(work)) continue;
+ if (!fs_mgr_dir_is_writable(work)) continue;
+ return dir;
+ }
+ return "";
}
const auto kLowerdirOption = "lowerdir="s;
@@ -147,35 +222,34 @@
auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
if (candidate.empty()) return "";
- return "override_creds=off,"s + kLowerdirOption + mount_point + "," + kUpperdirOption +
+ return "override_creds=off," + kLowerdirOption + mount_point + "," + kUpperdirOption +
candidate + kUpperName + ",workdir=" + candidate + kWorkName;
}
-bool fs_mgr_system_root_image(const fstab* fstab) {
- if (!fstab) { // can not happen?
- // This will return empty on init first_stage_mount,
- // hence why we prefer checking the fstab instead.
- return android::base::GetBoolProperty("ro.build.system_root_image", false);
- }
- for (auto i = 0; i < fstab->num_entries; i++) {
- const auto fsrec = &fstab->recs[i];
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point) continue;
- if ("/system"s == fsrec_mount_point) return false;
- }
- return true;
-}
-
-const char* fs_mgr_mount_point(const fstab* fstab, const char* mount_point) {
+const char* fs_mgr_mount_point(const char* mount_point) {
if (!mount_point) return mount_point;
if ("/"s != mount_point) return mount_point;
- if (!fs_mgr_system_root_image(fstab)) return mount_point;
return "/system";
}
+bool fs_mgr_access(const std::string& path) {
+ auto save_errno = errno;
+ auto ret = access(path.c_str(), F_OK) == 0;
+ errno = save_errno;
+ return ret;
+}
+
+bool fs_mgr_rw_access(const std::string& path) {
+ if (path.empty()) return false;
+ auto save_errno = errno;
+ auto ret = access(path.c_str(), R_OK | W_OK) == 0;
+ errno = save_errno;
+ return ret;
+}
+
// return true if system supports overlayfs
bool fs_mgr_wants_overlayfs() {
- // This will return empty on init first_stage_mount, so speculative
+ // Properties will return empty on init first_stage_mount, so speculative
// determination, empty (unset) _or_ "1" is true which differs from the
// official ro.debuggable policy. ALLOW_ADBD_DISABLE_VERITY == 0 should
// protect us from false in any case, so this is insurance.
@@ -183,16 +257,10 @@
if (debuggable != "1") return false;
// Overlayfs available in the kernel, and patched for override_creds?
- static signed char overlayfs_in_kernel = -1; // cache for constant condition
- if (overlayfs_in_kernel == -1) {
- auto save_errno = errno;
- overlayfs_in_kernel = !access("/sys/module/overlay/parameters/override_creds", F_OK);
- errno = save_errno;
- }
- return overlayfs_in_kernel;
+ return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
}
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
fs_mgr_free_fstab);
if (!fstab) return false;
@@ -201,10 +269,11 @@
const auto fsrec = &fstab->recs[i];
const auto fs_type = fsrec->fs_type;
if (!fs_type) continue;
- if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+ if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
auto fsrec_mount_point = fsrec->mount_point;
if (!fsrec_mount_point) continue;
if (mount_point != fsrec_mount_point) continue;
+ if (!overlay_only) return true;
const auto fs_options = fsrec->fs_options;
if (!fs_options) continue;
const auto options = android::base::Split(fs_options, ",");
@@ -217,16 +286,15 @@
return false;
}
-bool fs_mgr_overlayfs_verity_enabled(const std::string& basename_mount_point) {
- auto found = false;
- fs_mgr_update_verity_state(
- [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
- if (mount_point && (basename_mount_point == mount_point)) found = true;
- });
- return found;
+std::vector<std::string> fs_mgr_overlayfs_verity_enabled_list() {
+ std::vector<std::string> ret;
+ fs_mgr_update_verity_state([&ret](fstab_rec*, const char* mount_point, int, int) {
+ ret.emplace_back(mount_point);
+ });
+ return ret;
}
-bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
+bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
if (!fsrec) return false;
auto fsrec_mount_point = fsrec->mount_point;
@@ -249,51 +317,32 @@
if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
- return !fs_mgr_overlayfs_verity_enabled(android::base::Basename(fsrec_mount_point));
+ return true;
}
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr) {
- auto save_errno = errno;
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
- if (!dir) {
- if (errno == ENOENT) {
- errno = save_errno;
- return true;
- }
- PERROR << "opendir " << path;
- return false;
- }
- dirent* entry;
+bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
auto ret = true;
- while ((entry = readdir(dir.get()))) {
- if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
- auto file = path + "/" + entry->d_name;
- if (entry->d_type == DT_UNKNOWN) {
- struct stat st;
- if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
- }
- if (entry->d_type == DT_DIR) {
- ret &= fs_mgr_rm_all(file, change);
- if (!rmdir(file.c_str())) {
- if (change) *change = true;
- } else {
- ret = false;
- PERROR << "rmdir " << file;
- }
- continue;
- }
- if (!unlink(file.c_str())) {
- if (change) *change = true;
- } else {
- ret = false;
- PERROR << "rm " << file;
- }
+ auto top = dir + kOverlayTopDir;
+ if (setfscreatecon(kOverlayfsFileContext)) {
+ ret = false;
+ PERROR << "setfscreatecon " << kOverlayfsFileContext;
}
+ auto save_errno = errno;
+ if (!mkdir(top.c_str(), 0755)) {
+ if (change) *change = true;
+ } else if (errno != EEXIST) {
+ ret = false;
+ PERROR << "mkdir " << top;
+ } else {
+ errno = save_errno;
+ }
+ setfscreatecon(nullptr);
+
+ if (overlay) *overlay = std::move(top);
return ret;
}
-constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-
bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
bool* change) {
auto ret = true;
@@ -344,6 +393,131 @@
return ret;
}
+uint32_t fs_mgr_overlayfs_slot_number() {
+ return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+}
+
+std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
+ return "/dev/block/by-name/" + fs_mgr_get_super_partition_name(slot_number);
+}
+
+bool fs_mgr_overlayfs_has_logical(const fstab* fstab) {
+ if (!fstab) return false;
+ for (auto i = 0; i < fstab->num_entries; i++) {
+ const auto fsrec = &fstab->recs[i];
+ if (fs_mgr_is_logical(fsrec)) return true;
+ }
+ return false;
+}
+
+// reduce 'DM_DEV_STATUS failed for scratch: No such device or address' noise
+std::string scratch_device_cache;
+
+bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change) {
+ // umount and delete kScratchMountPoint storage if we have logical partitions
+ if (overlay != kScratchMountPoint) return true;
+ scratch_device_cache.erase();
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+ if (!fs_mgr_rw_access(super_device)) return true;
+
+ auto save_errno = errno;
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+ // Lazy umount will allow us to move on and possibly later
+ // establish a new fresh mount without requiring a reboot should
+ // the developer wish to restart. Old references should melt
+ // away or have no data. Main goal is to shut the door on the
+ // current overrides with an expectation of a subsequent reboot,
+ // thus any errors here are ignored.
+ umount2(kScratchMountPoint.c_str(), MNT_DETACH);
+ }
+ auto builder = MetadataBuilder::New(super_device, slot_number);
+ if (!builder) {
+ errno = save_errno;
+ return true;
+ }
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+ if (builder->FindPartition(partition_name) == nullptr) {
+ errno = save_errno;
+ return true;
+ }
+ builder->RemovePartition(partition_name);
+ auto metadata = builder->Export();
+ if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+ if (change) *change = true;
+ if (!DestroyLogicalPartition(partition_name, 0s)) return false;
+ } else {
+ PERROR << "delete partition " << overlay;
+ return false;
+ }
+ errno = save_errno;
+ return true;
+}
+
+bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
+ bool* change) {
+ const auto top = overlay + kOverlayTopDir;
+
+ if (!fs_mgr_access(top)) return fs_mgr_overlayfs_teardown_scratch(overlay, change);
+
+ auto cleanup_all = mount_point.empty();
+ const auto partition_name = android::base::Basename(mount_point);
+ const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
+ const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
+ : top + "/." + partition_name + ".teardown";
+ auto ret = fs_mgr_rm_all(newpath);
+ auto save_errno = errno;
+ if (!rename(oldpath.c_str(), newpath.c_str())) {
+ if (change) *change = true;
+ } else if (errno != ENOENT) {
+ ret = false;
+ PERROR << "mv " << oldpath << " " << newpath;
+ } else {
+ errno = save_errno;
+ }
+ ret &= fs_mgr_rm_all(newpath, change);
+ save_errno = errno;
+ if (!rmdir(newpath.c_str())) {
+ if (change) *change = true;
+ } else if (errno != ENOENT) {
+ ret = false;
+ PERROR << "rmdir " << newpath;
+ } else {
+ errno = save_errno;
+ }
+ if (!cleanup_all) {
+ save_errno = errno;
+ if (!rmdir(top.c_str())) {
+ if (change) *change = true;
+ cleanup_all = true;
+ } else if (errno == ENOTEMPTY) {
+ cleanup_all = true;
+ // cleanup all if the content is all hidden (leading .)
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
+ if (!dir) {
+ PERROR << "opendir " << top;
+ } else {
+ dirent* entry;
+ while ((entry = readdir(dir.get()))) {
+ if (entry->d_name[0] != '.') {
+ cleanup_all = false;
+ break;
+ }
+ }
+ }
+ errno = save_errno;
+ } else if (errno == ENOENT) {
+ cleanup_all = true;
+ errno = save_errno;
+ } else {
+ ret = false;
+ PERROR << "rmdir " << top;
+ }
+ }
+ if (cleanup_all) ret &= fs_mgr_overlayfs_teardown_scratch(overlay, change);
+ return ret;
+}
+
bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
auto options = fs_mgr_get_overlayfs_options(mount_point);
if (options.empty()) return false;
@@ -370,16 +544,20 @@
}
}
-std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
- const char* mount_point = nullptr) {
+std::vector<std::string> fs_mgr_candidate_list(fstab* fstab, const char* mount_point = nullptr) {
std::vector<std::string> mounts;
if (!fstab) return mounts;
+ auto verity = fs_mgr_overlayfs_verity_enabled_list();
for (auto i = 0; i < fstab->num_entries; i++) {
const auto fsrec = &fstab->recs[i];
if (!fs_mgr_wants_overlayfs(fsrec)) continue;
- std::string new_mount_point(fs_mgr_mount_point(fstab, fsrec->mount_point));
+ std::string new_mount_point(fs_mgr_mount_point(fsrec->mount_point));
if (mount_point && (new_mount_point != mount_point)) continue;
+ if (std::find(verity.begin(), verity.end(), android::base::Basename(new_mount_point)) !=
+ verity.end()) {
+ continue;
+ }
auto duplicate_or_more_specific = false;
for (auto it = mounts.begin(); it != mounts.end();) {
if ((*it == new_mount_point) ||
@@ -395,43 +573,239 @@
}
if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
}
- // if not itemized /system or /, system as root, fake up
- // fs_mgr_wants_overlayfs evaluation of /system as candidate.
- if ((std::find(mounts.begin(), mounts.end(), "/system") == mounts.end()) &&
- !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/") &&
- !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system") &&
- !fs_mgr_overlayfs_verity_enabled("system")) {
- mounts.emplace_back("/system");
+ // if not itemized /system or /, system as root, fake one up?
+
+ // do we want or need to?
+ if (mount_point && ("/system"s != mount_point)) return mounts;
+ if (std::find(mounts.begin(), mounts.end(), "/system") != mounts.end()) return mounts;
+
+ // fs_mgr_overlayfs_verity_enabled_list says not to?
+ if (std::find(verity.begin(), verity.end(), "system") != verity.end()) return mounts;
+
+ // confirm that fstab is missing system
+ if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/")) {
+ return mounts;
}
+ if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system")) {
+ return mounts;
+ }
+
+ // We have a stunted fstab (w/o system or / ) passed in by the caller,
+ // verity claims are assumed accurate because they are collected internally
+ // from fs_mgr_fstab_default() from within fs_mgr_update_verity_state(),
+ // Can (re)evaluate /system with impunity since we know it is ever-present.
+ mounts.emplace_back("/system");
return mounts;
}
+// Mount kScratchMountPoint
+bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type) {
+ if (!fs_mgr_rw_access(device_path)) return false;
+ if (setfscreatecon(kOverlayfsFileContext)) {
+ PERROR << "setfscreatecon " << kOverlayfsFileContext;
+ }
+ if (mkdir(kScratchMountPoint.c_str(), 0755) && (errno != EEXIST)) {
+ PERROR << "create " << kScratchMountPoint;
+ }
+
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> local_fstab(
+ static_cast<fstab*>(calloc(1, sizeof(fstab))), fs_mgr_free_fstab);
+ auto fsrec = static_cast<fstab_rec*>(calloc(1, sizeof(fstab_rec)));
+ local_fstab->num_entries = 1;
+ local_fstab->recs = fsrec;
+ fsrec->blk_device = strdup(device_path.c_str());
+ fsrec->mount_point = strdup(kScratchMountPoint.c_str());
+ fsrec->fs_type = strdup(mnt_type.c_str());
+ fsrec->flags = MS_RELATIME;
+ fsrec->fs_options = strdup("");
+ auto save_errno = errno;
+ auto mounted = fs_mgr_do_mount_one(fsrec) == 0;
+ if (!mounted) {
+ free(fsrec->fs_type);
+ if (mnt_type == "f2fs") {
+ fsrec->fs_type = strdup("ext4");
+ } else {
+ fsrec->fs_type = strdup("f2fs");
+ }
+ mounted = fs_mgr_do_mount_one(fsrec) == 0;
+ if (!mounted) save_errno = errno;
+ }
+ setfscreatecon(nullptr);
+ if (!mounted) rmdir(kScratchMountPoint.c_str());
+ errno = save_errno;
+ return mounted;
+}
+
+const std::string kMkF2fs("/system/bin/make_f2fs");
+const std::string kMkExt4("/system/bin/mke2fs");
+
+// Only a suggestion for _first_ try during mounting
+std::string fs_mgr_overlayfs_scratch_mount_type() {
+ if (!access(kMkF2fs.c_str(), X_OK)) return "f2fs";
+ if (!access(kMkExt4.c_str(), X_OK)) return "ext4";
+ return "auto";
+}
+
+std::string fs_mgr_overlayfs_scratch_device() {
+ if (!scratch_device_cache.empty()) return scratch_device_cache;
+
+ auto& dm = DeviceMapper::Instance();
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+ std::string path;
+ if (!dm.GetDmDevicePathByName(partition_name, &path)) return "";
+ return scratch_device_cache = path;
+}
+
+// Create and mount kScratchMountPoint storage if we have logical partitions
+bool fs_mgr_overlayfs_setup_scratch(const fstab* fstab, bool* change) {
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+ auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
+ auto scratch_device = fs_mgr_overlayfs_scratch_device();
+ auto partition_exists = fs_mgr_rw_access(scratch_device);
+ if (!partition_exists) {
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+ if (!fs_mgr_rw_access(super_device)) return false;
+ if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
+ auto builder = MetadataBuilder::New(super_device, slot_number);
+ if (!builder) {
+ PERROR << "open " << super_device << " metadata";
+ return false;
+ }
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+ partition_exists = builder->FindPartition(partition_name) != nullptr;
+ if (!partition_exists) {
+ auto partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+ if (!partition) {
+ PERROR << "create " << partition_name;
+ return false;
+ }
+ auto partition_size = builder->AllocatableSpace() - builder->UsedSpace();
+ // 512MB or half the remaining available space, whichever is greater.
+ partition_size = std::max(uint64_t(512 * 1024 * 1024), partition_size / 2);
+ if (!builder->ResizePartition(partition, partition_size)) {
+ PERROR << "resize " << partition_name;
+ return false;
+ }
+
+ auto metadata = builder->Export();
+ if (!metadata) {
+ LERROR << "generate new metadata " << partition_name;
+ return false;
+ }
+ if (!UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+ LERROR << "update " << partition_name;
+ return false;
+ }
+
+ if (change) *change = true;
+ }
+
+ if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+ &scratch_device))
+ return false;
+ }
+
+ if (partition_exists) {
+ if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
+ if (change) *change = true;
+ return true;
+ }
+ // partition existed, but was not initialized;
+ errno = 0;
+ }
+
+ auto ret = system((mnt_type == "f2fs")
+ ? ((kMkF2fs + " -d1 " + scratch_device).c_str())
+ : ((kMkExt4 + " -b 4096 -t ext4 -m 0 -M " + kScratchMountPoint +
+ " -O has_journal " + scratch_device)
+ .c_str()));
+ if (ret) {
+ LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " error=" << ret;
+ return false;
+ }
+
+ if (change) *change = true;
+
+ return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
+}
+
+bool fs_mgr_overlayfs_scratch_can_be_mounted(const std::string& scratch_device) {
+ if (scratch_device.empty()) return false;
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return false;
+ if (fs_mgr_rw_access(scratch_device)) return true;
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+ if (!fs_mgr_rw_access(super_device)) return false;
+ auto builder = MetadataBuilder::New(super_device, slot_number);
+ if (!builder) return false;
+ return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
+}
+
} // namespace
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
auto ret = false;
if (!fs_mgr_wants_overlayfs()) return ret;
if (!fstab) return ret;
+ auto scratch_can_be_mounted = true;
for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+ if (scratch_can_be_mounted) {
+ 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) &&
+ fs_mgr_overlayfs_mount_scratch(scratch_device,
+ fs_mgr_overlayfs_scratch_mount_type()) &&
+ !fs_mgr_access(kScratchMountPoint + kOverlayTopDir)) {
+ umount2(kScratchMountPoint.c_str(), MNT_DETACH);
+ rmdir(kScratchMountPoint.c_str());
+ }
+ }
if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
}
return ret;
}
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fsrecs) {
+ std::vector<fstab_rec> recs;
+ for (const auto& rec : fsrecs) recs.push_back(*rec);
+ fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+ return fs_mgr_overlayfs_mount_all(&fstab);
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
+ if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
+ return {};
+ }
+
+ for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+ auto device = fs_mgr_overlayfs_scratch_device();
+ if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
+ return {device};
+ }
+ return {};
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+ const std::vector<const fstab_rec*>& fsrecs) {
+ std::vector<fstab_rec> recs;
+ for (const auto& rec : fsrecs) recs.push_back(*rec);
+ fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+ return fs_mgr_overlayfs_required_devices(&fstab);
+}
+
// Returns false if setup not permitted, errno set to last error.
// If something is altered, set *change.
bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
if (change) *change = false;
auto ret = false;
- if (backing && (kOverlayMountPoint != backing)) {
- errno = EINVAL;
- return ret;
- }
if (!fs_mgr_wants_overlayfs()) return ret;
if (!fs_mgr_boot_completed()) {
errno = EBUSY;
@@ -441,26 +815,32 @@
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
fs_mgr_free_fstab);
- if (fstab && !fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
- auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(fstab.get(), mount_point));
- if (fstab && mounts.empty()) return ret;
+ if (!fstab) return ret;
+ auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(mount_point));
+ if (mounts.empty()) return ret;
- if (setfscreatecon(kOverlayfsFileContext)) {
- PERROR << "setfscreatecon " << kOverlayfsFileContext;
+ std::string dir;
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
+ if (backing && backing[0] && (overlay_mount_point != backing)) continue;
+ if (overlay_mount_point == kScratchMountPoint) {
+ if (!fs_mgr_rw_access(fs_mgr_overlayfs_super_device(fs_mgr_overlayfs_slot_number())) ||
+ !fs_mgr_overlayfs_has_logical(fstab.get())) {
+ continue;
+ }
+ if (!fs_mgr_overlayfs_setup_scratch(fstab.get(), change)) continue;
+ } else {
+ if (!fs_mgr_get_entry_for_mount_point(fstab.get(), overlay_mount_point)) continue;
+ }
+ dir = overlay_mount_point;
+ break;
}
- auto overlay = kOverlayMountPoint + kOverlayTopDir;
- auto save_errno = errno;
- if (!mkdir(overlay.c_str(), 0755)) {
- if (change) *change = true;
- } else if (errno != EEXIST) {
- PERROR << "mkdir " << overlay;
- } else {
- errno = save_errno;
+ if (dir.empty()) {
+ errno = ESRCH;
+ return ret;
}
- setfscreatecon(nullptr);
- if (!fstab && mount_point && fs_mgr_overlayfs_setup_one(overlay, mount_point, change)) {
- ret = true;
- }
+
+ std::string overlay;
+ ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
for (const auto& fsrec_mount_point : mounts) {
ret |= fs_mgr_overlayfs_setup_one(overlay, fsrec_mount_point, change);
}
@@ -471,44 +851,23 @@
// If something is altered, set *change.
bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
if (change) *change = false;
- mount_point = fs_mgr_mount_point(std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>(
- fs_mgr_read_fstab_default(), fs_mgr_free_fstab)
- .get(),
- mount_point);
+ mount_point = fs_mgr_mount_point(mount_point);
auto ret = true;
- const auto overlay = kOverlayMountPoint + kOverlayTopDir;
- const auto oldpath = overlay + (mount_point ? "/"s + mount_point : ""s);
- const auto newpath = oldpath + ".teardown";
- ret &= fs_mgr_rm_all(newpath);
- auto save_errno = errno;
- if (!rename(oldpath.c_str(), newpath.c_str())) {
- if (change) *change = true;
- } else if (errno != ENOENT) {
- ret = false;
- PERROR << "mv " << oldpath << " " << newpath;
- } else {
- errno = save_errno;
- }
- ret &= fs_mgr_rm_all(newpath, change);
- save_errno = errno;
- if (!rmdir(newpath.c_str())) {
- if (change) *change = true;
- } else if (errno != ENOENT) {
- ret = false;
- PERROR << "rmdir " << newpath;
- } else {
- errno = save_errno;
- }
- if (mount_point) {
- save_errno = errno;
- if (!rmdir(overlay.c_str())) {
- if (change) *change = true;
- } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
- ret = false;
- PERROR << "rmdir " << overlay;
- } else {
- errno = save_errno;
+ // If scratch exists, but is not mounted, lets gain access to clean
+ // specific override entries.
+ if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+ auto scratch_device = fs_mgr_overlayfs_scratch_device();
+ if (scratch_device.empty()) {
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+ CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+ &scratch_device);
}
+ fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type());
+ }
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
+ ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
}
if (!fs_mgr_wants_overlayfs()) {
// After obligatory teardown to make sure everything is clean, but if
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 506e81d..5e83cfb 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -128,10 +128,10 @@
FileWaitMode wait_mode = FileWaitMode::Exists);
int fs_mgr_set_blk_ro(const char* blockdev);
-bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_update_for_slotselect(fstab* fstab);
bool fs_mgr_is_device_unlocked();
const std::string& get_android_dt_dir();
bool is_dt_compatible();
-int load_verity_state(struct fstab_rec* fstab, int* mode);
+int load_verity_state(fstab_rec* fstab, int* mode);
#endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fb4ebb..2727a6d 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -89,24 +89,21 @@
{
uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
- FILE* f = fopen(path, "r");
+ auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
if (!f) {
LERROR << "Can't open " << path;
- return NULL;
+ return nullptr;
}
- if (!fread(key_data, sizeof(key_data), 1, f)) {
+ if (!fread(key_data, sizeof(key_data), 1, f.get())) {
LERROR << "Could not read key!";
- fclose(f);
- return NULL;
+ return nullptr;
}
- fclose(f);
-
- RSA* key = NULL;
+ RSA* key = nullptr;
if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
LERROR << "Could not parse key!";
- return NULL;
+ return nullptr;
}
return key;
@@ -368,7 +365,6 @@
static int metadata_find(const char *fname, const char *stag,
unsigned int slength, off64_t *offset)
{
- FILE *fp = NULL;
char tag[METADATA_TAG_MAX_LENGTH + 1];
int rc = -1;
int n;
@@ -380,75 +376,64 @@
return -1;
}
- fp = fopen(fname, "r+");
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
if (!fp) {
PERROR << "Failed to open " << fname;
- goto out;
+ return -1;
}
/* check magic */
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fread(&magic, sizeof(magic), 1, fp) != 1) {
+ if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
PERROR << "Failed to read magic from " << fname;
- goto out;
+ return -1;
}
if (magic != METADATA_MAGIC) {
magic = METADATA_MAGIC;
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fwrite(&magic, sizeof(magic), 1, fp) != 1) {
+ if (fseek(fp.get(), start, SEEK_SET) < 0 ||
+ fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
PERROR << "Failed to write magic to " << fname;
- goto out;
+ return -1;
}
- rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
+ rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
if (rc < 0) {
PERROR << "Failed to add metadata to " << fname;
}
- goto out;
+ return rc;
}
start += sizeof(magic);
while (1) {
- n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
- tag, &length);
+ 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);
+ start = ftell(fp.get());
if (!strcmp(tag, stag) && length == slength) {
*offset = start;
- rc = 0;
- goto out;
+ return 0;
}
start += length;
- if (fseek(fp, length, SEEK_CUR) < 0) {
+ if (fseek(fp.get(), length, SEEK_CUR) < 0) {
PERROR << "Failed to seek " << fname;
- goto out;
+ return -1;
}
} else {
- rc = metadata_add(fp, start, stag, slength, offset);
+ rc = metadata_add(fp.get(), start, stag, slength, offset);
if (rc < 0) {
PERROR << "Failed to write metadata to " << fname;
}
- goto out;
+ return rc;
}
- }
-
-out:
- if (fp) {
- fflush(fp);
- fclose(fp);
}
-
- return rc;
}
static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index cee069b..5dabe76 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -23,6 +23,7 @@
#include <linux/dm-ioctl.h>
#include <functional>
+#include <string>
#include <fstab/fstab.h>
@@ -50,8 +51,8 @@
};
// Callback function for verity status
-typedef void fs_mgr_verity_state_callback(struct fstab_rec* fstab, const char* mount_point,
- int mode, int status);
+typedef void fs_mgr_verity_state_callback(fstab_rec* fstab, const char* mount_point, int mode,
+ int status);
#define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
#define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
@@ -62,31 +63,35 @@
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
#define FS_MGR_MNTALL_FAIL (-1)
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode);
+int fs_mgr_mount_all(fstab* fstab, int mount_mode);
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
#define FS_MGR_DOMNT_SUCCESS 0
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
- char *tmp_mount_point);
-int fs_mgr_do_mount(struct fstab* fstab, const char* n_name, char* n_blk_device,
- char* tmp_mount_point, bool need_cp);
-int fs_mgr_do_mount_one(struct fstab_rec *rec);
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+ bool need_cp);
+int fs_mgr_do_mount_one(fstab_rec* rec);
int fs_mgr_do_tmpfs_mount(const char *n_name);
-struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
-void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
+fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab);
+void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
bool fs_mgr_load_verity_state(int* mode);
bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback);
int fs_mgr_swapon_all(struct fstab *fstab);
bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
-int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
+int fs_mgr_do_format(fstab_rec* fstab, bool reserve_footer);
#define FS_MGR_SETUP_VERITY_SKIPPED (-3)
#define FS_MGR_SETUP_VERITY_DISABLED (-2)
#define FS_MGR_SETUP_VERITY_FAIL (-1)
#define FS_MGR_SETUP_VERITY_SUCCESS 0
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
+int fs_mgr_setup_verity(fstab_rec* fstab, bool wait_for_verity_dev);
+
+// Return the name of the super partition if it exists. If a slot number is
+// specified, the super partition for the corresponding metadata slot will be
+// returned. Otherwise, it will use the current slot.
+std::string fs_mgr_get_super_partition_name(int slot = -1);
#endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 251dd9b..9182532 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -19,8 +19,13 @@
#include <fstab/fstab.h>
#include <string>
+#include <vector>
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+ const std::vector<const fstab_rec*>& fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
bool* change = nullptr);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index cc61917..70823c6 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -59,7 +59,8 @@
: dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
valid_ = dm_.CreateDevice(name, table);
}
- TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+ TempDevice(TempDevice&& other) noexcept
+ : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
other.valid_ = false;
}
~TempDevice() {
@@ -103,7 +104,7 @@
TempDevice(const TempDevice&) = delete;
TempDevice& operator=(const TempDevice&) = delete;
- TempDevice& operator=(TempDevice&& other) {
+ TempDevice& operator=(TempDevice&& other) noexcept {
name_ = other.name_;
valid_ = other.valid_;
other.valid_ = false;
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index aab89e5..175b0f0 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -156,6 +156,20 @@
std::string target_string_;
};
+// dm-bow is the backup on write target that can provide checkpoint capability
+// for file systems that do not support checkpoints natively
+class DmTargetBow final : public DmTarget {
+ public:
+ DmTargetBow(uint64_t start, uint64_t length, const std::string& target_string)
+ : DmTarget(start, length), target_string_(target_string) {}
+
+ std::string name() const override { return "bow"; }
+ std::string GetParameterString() const override { return target_string_; }
+
+ private:
+ std::string target_string_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 89282db..5689bdf 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -25,6 +25,7 @@
srcs: [
"builder.cpp",
"images.cpp",
+ "partition_opener.cpp",
"reader.cpp",
"utility.cpp",
"writer.cpp",
@@ -35,7 +36,6 @@
"libcrypto",
"libcrypto_utils",
"libsparse",
- "libext2_uuid",
"libext4_utils",
"libz",
],
@@ -48,14 +48,19 @@
cppflags: [
"-Wno-unused-parameter",
],
+ static_libs: [
+ "libgmock",
+ ],
shared_libs: [
"liblp",
"libbase",
"libfs_mgr",
+ "libsparse",
],
srcs: [
"builder_test.cpp",
"io_test.cpp",
+ "test_partition_opener.cpp",
"utility_test.cpp",
],
}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 352647b..3cd33b1 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -16,16 +16,11 @@
#include "liblp/builder.h"
-#if defined(__linux__)
-#include <linux/fs.h>
-#endif
#include <string.h>
-#include <sys/ioctl.h>
#include <algorithm>
#include <android-base/unique_fd.h>
-#include <uuid/uuid.h>
#include "liblp/liblp.h"
#include "reader.h"
@@ -34,67 +29,39 @@
namespace android {
namespace fs_mgr {
-bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
-#if defined(__linux__)
- android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+bool LinearExtent::AddTo(LpMetadata* out) const {
+ if (device_index_ >= out->block_devices.size()) {
+ LERROR << "Extent references unknown block device.";
return false;
}
- if (!GetDescriptorSize(fd, &device_info->size)) {
- return false;
- }
- if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
- return false;
- }
-
- int alignment_offset;
- if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
- return false;
- }
- int logical_block_size;
- if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
- return false;
- }
-
- device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
- device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+ out->extents.emplace_back(
+ LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
return true;
-#else
- (void)block_device;
- (void)device_info;
- LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
- return false;
-#endif
}
-void LinearExtent::AddTo(LpMetadata* out) const {
- out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+bool ZeroExtent::AddTo(LpMetadata* out) const {
+ out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
+ return true;
}
-void ZeroExtent::AddTo(LpMetadata* out) const {
- out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
-}
-
-Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
- : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+ : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
size_ += extent->num_sectors() * LP_SECTOR_SIZE;
if (LinearExtent* new_extent = extent->AsLinearExtent()) {
- if (!extents_.empty() && extents_.back()->AsLinearExtent() &&
- extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) {
- // If the previous extent can be merged into this new one, do so
- // to avoid creating unnecessary extents.
+ if (!extents_.empty() && extents_.back()->AsLinearExtent()) {
LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
- extent = std::make_unique<LinearExtent>(
- prev_extent->num_sectors() + new_extent->num_sectors(),
- prev_extent->physical_sector());
- extents_.pop_back();
+ if (prev_extent->end_sector() == new_extent->physical_sector() &&
+ prev_extent->device_index() == new_extent->device_index()) {
+ // If the previous extent can be merged into this new one, do so
+ // to avoid creating unnecessary extents.
+ extent = std::make_unique<LinearExtent>(
+ prev_extent->num_sectors() + new_extent->num_sectors(),
+ prev_extent->device_index(), prev_extent->physical_sector());
+ extents_.pop_back();
+ }
}
}
extents_.push_back(std::move(extent));
@@ -128,9 +95,21 @@
DCHECK(size_ == aligned_size);
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+uint64_t Partition::BytesOnDisk() const {
+ uint64_t sectors = 0;
+ for (const auto& extent : extents_) {
+ if (!extent->AsLinearExtent()) {
+ continue;
+ }
+ sectors += extent->num_sectors();
+ }
+ return sectors * LP_SECTOR_SIZE;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& opener,
+ const std::string& super_partition,
uint32_t slot_number) {
- std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+ std::unique_ptr<LpMetadata> metadata = ReadMetadata(opener, super_partition, slot_number);
if (!metadata) {
return nullptr;
}
@@ -138,18 +117,26 @@
if (!builder) {
return nullptr;
}
- BlockDeviceInfo device_info;
- if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
- builder->set_block_device_info(device_info);
+ for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+ std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+ BlockDeviceInfo device_info;
+ if (opener.GetInfo(partition_name, &device_info)) {
+ builder->UpdateBlockDeviceInfo(i, device_info);
+ }
}
return builder;
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
- uint32_t metadata_max_size,
- uint32_t metadata_slot_count) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
+ uint32_t slot_number) {
+ return New(PartitionOpener(), super_partition, slot_number);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
+ const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+ uint32_t metadata_max_size, uint32_t metadata_slot_count) {
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
- if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
+ if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {
return nullptr;
}
return builder;
@@ -175,14 +162,25 @@
header_.header_size = sizeof(header_);
header_.partitions.entry_size = sizeof(LpMetadataPartition);
header_.extents.entry_size = sizeof(LpMetadataExtent);
+ header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
+ header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);
}
bool MetadataBuilder::Init(const LpMetadata& metadata) {
geometry_ = metadata.geometry;
+ block_devices_ = metadata.block_devices;
+
+ for (const auto& group : metadata.groups) {
+ std::string group_name = GetPartitionGroupName(group);
+ if (!AddGroup(group_name, group.maximum_size)) {
+ return false;
+ }
+ }
for (const auto& partition : metadata.partitions) {
- Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
- partition.attributes);
+ std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
+ Partition* builder =
+ AddPartition(GetPartitionName(partition), group_name, partition.attributes);
if (!builder) {
return false;
}
@@ -190,7 +188,8 @@
for (size_t i = 0; i < partition.num_extents; i++) {
const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
- auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+ auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+ extent.target_data);
builder->AddExtent(std::move(copy));
} else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
@@ -198,14 +197,40 @@
}
}
}
-
- device_info_.alignment = geometry_.alignment;
- device_info_.alignment_offset = geometry_.alignment_offset;
- device_info_.logical_block_size = geometry_.logical_block_size;
return true;
}
-bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
+static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+ if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " logical block size must be a multiple of 512.";
+ return false;
+ }
+ if (device_info.size % device_info.logical_block_size != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " size must be a multiple of its block size.";
+ return false;
+ }
+ if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " alignment offset is not sector-aligned.";
+ return false;
+ }
+ if (device_info.alignment % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " partition alignment is not sector-aligned.";
+ return false;
+ }
+ if (device_info.alignment_offset > device_info.alignment) {
+ LERROR << "Block device " << device_info.partition_name
+ << " partition alignment offset is greater than its alignment.";
+ return false;
+ }
+ return true;
+}
+
+bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
+ const std::string& super_partition, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
if (metadata_max_size < sizeof(LpMetadataHeader)) {
LERROR << "Invalid metadata maximum size.";
@@ -215,87 +240,123 @@
LERROR << "Invalid metadata slot count.";
return false;
}
+ if (block_devices.empty()) {
+ LERROR << "No block devices were specified.";
+ return false;
+ }
// Align the metadata size up to the nearest sector.
metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
- // Check that device properties are sane.
- device_info_ = device_info;
- if (device_info_.size % LP_SECTOR_SIZE != 0) {
- LERROR << "Block device size must be a multiple of 512.";
+ // Validate and build the block device list.
+ uint32_t logical_block_size = 0;
+ for (const auto& device_info : block_devices) {
+ if (!VerifyDeviceProperties(device_info)) {
+ return false;
+ }
+
+ if (!logical_block_size) {
+ logical_block_size = device_info.logical_block_size;
+ }
+ if (logical_block_size != device_info.logical_block_size) {
+ LERROR << "All partitions must have the same logical block size.";
+ return false;
+ }
+
+ LpMetadataBlockDevice out = {};
+ out.alignment = device_info.alignment;
+ out.alignment_offset = device_info.alignment_offset;
+ out.size = device_info.size;
+ if (device_info.partition_name.size() >= sizeof(out.partition_name)) {
+ LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
+ return false;
+ }
+ strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));
+
+ // In the case of the super partition, this field will be adjusted
+ // later. For all partitions, the first 512 bytes are considered
+ // untouched to be compatible code that looks for an MBR. Thus we
+ // start counting free sectors at sector 1, not 0.
+ uint64_t free_area_start = LP_SECTOR_SIZE;
+ if (out.alignment || out.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+ } else {
+ free_area_start = AlignTo(free_area_start, logical_block_size);
+ }
+ out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+ // There must be one logical block of space available.
+ uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;
+ if (device_info.size < minimum_size) {
+ LERROR << "Block device " << device_info.partition_name
+ << " is too small to hold any logical partitions.";
+ return false;
+ }
+
+ // The "root" of the super partition is always listed first.
+ if (device_info.partition_name == super_partition) {
+ block_devices_.emplace(block_devices_.begin(), out);
+ } else {
+ block_devices_.emplace_back(out);
+ }
+ }
+ if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+ LERROR << "No super partition was specified.";
return false;
}
- if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) {
- LERROR << "Logical block size must be a multiple of 512.";
- return false;
- }
- if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
- LERROR << "Alignment offset is not sector-aligned.";
- return false;
- }
- if (device_info_.alignment % LP_SECTOR_SIZE != 0) {
- LERROR << "Partition alignment is not sector-aligned.";
- return false;
- }
- if (device_info_.alignment_offset > device_info_.alignment) {
- LERROR << "Partition alignment offset is greater than its alignment.";
- return false;
- }
+
+ LpMetadataBlockDevice& super = block_devices_[0];
// We reserve a geometry block (4KB) plus space for each copy of the
// maximum size of a metadata blob. Then, we double that space since
// we store a backup copy of everything.
- uint64_t reserved =
- LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
- uint64_t total_reserved = reserved * 2;
- if (device_info_.size < total_reserved) {
+ uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
+ if (super.size < total_reserved) {
LERROR << "Attempting to create metadata on a block device that is too small.";
return false;
}
// Compute the first free sector, factoring in alignment.
- uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
- uint64_t first_sector = free_area / LP_SECTOR_SIZE;
+ uint64_t free_area_start = total_reserved;
+ if (super.alignment || super.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+ } else {
+ free_area_start = AlignTo(free_area_start, logical_block_size);
+ }
+ super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
- // Compute the last free sector, which is inclusive. We subtract 1 to make
- // sure that logical partitions won't overlap with the same sector as the
- // backup metadata, which could happen if the block device was not aligned
- // to LP_SECTOR_SIZE.
- uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
-
- // If this check fails, it means either (1) we did not have free space to
- // allocate a single sector, or (2) we did, but the alignment was high
- // enough to bump the first sector out of range. Either way, we cannot
- // continue.
- if (first_sector > last_sector) {
- LERROR << "Not enough space to allocate any partition tables.";
+ // There must be one logical block of free space remaining (enough for one partition).
+ uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;
+ if (super.size < minimum_disk_size) {
+ LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has "
+ << super.size;
return false;
}
- // Finally, the size of the allocatable space must be a multiple of the
- // logical block size. If we have no more free space after this
- // computation, then we abort. Note that the last sector is inclusive,
- // so we have to account for that.
- uint64_t num_free_sectors = last_sector - first_sector + 1;
- uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
- if (num_free_sectors < sectors_per_block) {
- LERROR << "Not enough space to allocate any partition tables.";
- return false;
- }
- last_sector = first_sector + (num_free_sectors / sectors_per_block) * sectors_per_block - 1;
-
- geometry_.first_logical_sector = first_sector;
- geometry_.last_logical_sector = last_sector;
geometry_.metadata_max_size = metadata_max_size;
geometry_.metadata_slot_count = metadata_slot_count;
- geometry_.alignment = device_info_.alignment;
- geometry_.alignment_offset = device_info_.alignment_offset;
- geometry_.block_device_size = device_info_.size;
- geometry_.logical_block_size = device_info.logical_block_size;
+ geometry_.logical_block_size = logical_block_size;
+
+ if (!AddGroup("default", 0)) {
+ return false;
+ }
return true;
}
-Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
+ if (FindGroup(group_name)) {
+ LERROR << "Group already exists: " << group_name;
+ return false;
+ }
+ groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));
+ return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
+ return AddPartition(name, "default", attributes);
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
uint32_t attributes) {
if (name.empty()) {
LERROR << "Partition must have a non-empty name.";
@@ -305,7 +366,11 @@
LERROR << "Attempting to create duplication partition with name: " << name;
return nullptr;
}
- partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+ if (!FindGroup(group_name)) {
+ LERROR << "Could not find partition group: " << group_name;
+ return nullptr;
+ }
+ partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
return partitions_.back().get();
}
@@ -318,6 +383,26 @@
return nullptr;
}
+PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
+ for (const auto& group : groups_) {
+ if (group->name() == group_name) {
+ return group.get();
+ }
+ }
+ return nullptr;
+}
+
+uint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {
+ uint64_t total = 0;
+ for (const auto& partition : partitions_) {
+ if (partition->group_name() != group->name()) {
+ continue;
+ }
+ total += partition->BytesOnDisk();
+ }
+ return total;
+}
+
void MetadataBuilder::RemovePartition(const std::string& name) {
for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
if ((*iter)->name() == name) {
@@ -327,44 +412,16 @@
}
}
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
- // Figure out how much we need to allocate.
- uint64_t space_needed = aligned_size - partition->size();
- uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
- DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
-
- struct Interval {
- uint64_t start;
- uint64_t end;
-
- Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
- uint64_t length() const { return end - start; }
- bool operator<(const Interval& other) const { return start < other.start; }
- };
-
- // Collect all extents in the partition table, then sort them by starting
- // sector.
- std::vector<Interval> extents;
- for (const auto& partition : partitions_) {
- for (const auto& extent : partition->extents()) {
- LinearExtent* linear = extent->AsLinearExtent();
- if (!linear) {
- continue;
- }
- extents.emplace_back(linear->physical_sector(),
- linear->physical_sector() + extent->num_sectors());
- }
- }
- std::sort(extents.begin(), extents.end());
-
+void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents,
+ std::vector<Interval>* free_regions) const {
// Convert the extent list into a list of gaps between the extents; i.e.,
// the list of ranges that are free on the disk.
- std::vector<Interval> free_regions;
for (size_t i = 1; i < extents.size(); i++) {
const Interval& previous = extents[i - 1];
const Interval& current = extents[i];
+ DCHECK(previous.device_index == current.device_index);
- uint64_t aligned = AlignSector(previous.end);
+ uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
if (aligned >= current.start) {
// There is no gap between these two extents, try the next one.
// Note that we check with >= instead of >, since alignment may
@@ -374,18 +431,70 @@
// The new interval represents the free space starting at the end of
// the previous interval, and ending at the start of the next interval.
- free_regions.emplace_back(aligned, current.start);
+ free_regions->emplace_back(current.device_index, aligned, current.start);
+ }
+}
+
+auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
+ std::vector<Interval> free_regions;
+
+ // Collect all extents in the partition table, per-device, then sort them
+ // by starting sector.
+ std::vector<std::vector<Interval>> device_extents(block_devices_.size());
+ for (const auto& partition : partitions_) {
+ for (const auto& extent : partition->extents()) {
+ LinearExtent* linear = extent->AsLinearExtent();
+ if (!linear) {
+ continue;
+ }
+ CHECK(linear->device_index() < device_extents.size());
+ auto& extents = device_extents[linear->device_index()];
+ extents.emplace_back(linear->device_index(), linear->physical_sector(),
+ linear->physical_sector() + extent->num_sectors());
+ }
}
- // Add a final interval representing the remainder of the free space.
- uint64_t last_free_extent_start =
- extents.empty() ? geometry_.first_logical_sector : extents.back().end;
- last_free_extent_start = AlignSector(last_free_extent_start);
- if (last_free_extent_start <= geometry_.last_logical_sector) {
- free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1);
+ // Add 0-length intervals for the first and last sectors. This will cause
+ // ExtentToFreeList() to treat the space in between as available.
+ for (size_t i = 0; i < device_extents.size(); i++) {
+ auto& extents = device_extents[i];
+ const auto& block_device = block_devices_[i];
+
+ uint64_t first_sector = block_device.first_logical_sector;
+ uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;
+ extents.emplace_back(i, first_sector, first_sector);
+ extents.emplace_back(i, last_sector, last_sector);
+
+ std::sort(extents.begin(), extents.end());
+ ExtentsToFreeList(extents, &free_regions);
+ }
+ return free_regions;
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+ PartitionGroup* group = FindGroup(partition->group_name());
+ CHECK(group);
+
+ // Figure out how much we need to allocate, and whether our group has
+ // enough space remaining.
+ uint64_t space_needed = aligned_size - partition->size();
+ if (group->maximum_size() > 0) {
+ uint64_t group_size = TotalSizeOfGroup(group);
+ if (group_size >= group->maximum_size() ||
+ group->maximum_size() - group_size < space_needed) {
+ LERROR << "Partition " << partition->name() << " is part of group " << group->name()
+ << " which does not have enough space free (" << space_needed << "requested, "
+ << group_size << " used out of " << group->maximum_size();
+ return false;
+ }
}
- const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+ uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+ DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+ std::vector<Interval> free_regions = GetFreeRegions();
+
+ const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
CHECK_NE(sectors_per_block, 0);
CHECK(sectors_needed % sectors_per_block == 0);
@@ -413,7 +522,7 @@
uint64_t sectors = std::min(sectors_needed, region.length());
CHECK(sectors % sectors_per_block == 0);
- auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+ auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
new_extents.push_back(std::move(extent));
sectors_needed -= sectors;
if (!sectors_needed) {
@@ -441,6 +550,24 @@
metadata->header = header_;
metadata->geometry = geometry_;
+ // Assign this early so the extent table can read it.
+ metadata->block_devices = block_devices_;
+
+ std::map<std::string, size_t> group_indices;
+ for (const auto& group : groups_) {
+ LpMetadataPartitionGroup out = {};
+
+ if (group->name().size() > sizeof(out.name)) {
+ LERROR << "Partition group name is too long: " << group->name();
+ return nullptr;
+ }
+ strncpy(out.name, group->name().c_str(), sizeof(out.name));
+ out.maximum_size = group->maximum_size();
+
+ group_indices[group->name()] = metadata->groups.size();
+ metadata->groups.push_back(out);
+ }
+
// Flatten the partition and extent structures into an LpMetadata, which
// makes it very easy to validate, serialize, or pass on to device-mapper.
for (const auto& partition : partitions_) {
@@ -457,29 +584,40 @@
}
strncpy(part.name, partition->name().c_str(), sizeof(part.name));
- if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
- LERROR << "Could not parse guid " << partition->guid() << " for partition "
- << partition->name();
- return nullptr;
- }
-
part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
part.num_extents = static_cast<uint32_t>(partition->extents().size());
part.attributes = partition->attributes();
+ auto iter = group_indices.find(partition->group_name());
+ if (iter == group_indices.end()) {
+ LERROR << "Partition " << partition->name() << " is a member of unknown group "
+ << partition->group_name();
+ return nullptr;
+ }
+ part.group_index = iter->second;
+
for (const auto& extent : partition->extents()) {
- extent->AddTo(metadata.get());
+ if (!extent->AddTo(metadata.get())) {
+ return nullptr;
+ }
}
metadata->partitions.push_back(part);
}
metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+ metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
+ metadata->header.block_devices.num_entries =
+ static_cast<uint32_t>(metadata->block_devices.size());
return metadata;
}
uint64_t MetadataBuilder::AllocatableSpace() const {
- return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
+ uint64_t total_size = 0;
+ for (const auto& block_device : block_devices_) {
+ total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE);
+ }
+ return total_size;
}
uint64_t MetadataBuilder::UsedSpace() const {
@@ -490,36 +628,80 @@
return size;
}
-uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
+uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
+ uint64_t sector) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- uint64_t aligned = AlignTo(lba, device_info_.alignment, device_info_.alignment_offset);
+ uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
}
-void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
- device_info_.size = device_info.size;
+bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
+ uint32_t* index) const {
+ for (size_t i = 0; i < block_devices_.size(); i++) {
+ if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
- // Note that if the logical block size changes, we're probably in trouble:
- // we could have already built extents that will only work on the previous
- // size.
- DCHECK(partitions_.empty() ||
- device_info_.logical_block_size == device_info.logical_block_size);
+bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,
+ BlockDeviceInfo* info) const {
+ uint32_t index;
+ if (!FindBlockDeviceByName(partition_name, &index)) {
+ LERROR << "No device named " << partition_name;
+ return false;
+ }
+ info->size = block_devices_[index].size;
+ info->alignment = block_devices_[index].alignment;
+ info->alignment_offset = block_devices_[index].alignment_offset;
+ info->logical_block_size = geometry_.logical_block_size;
+ info->partition_name = partition_name;
+ return true;
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name,
+ const BlockDeviceInfo& device_info) {
+ uint32_t index;
+ if (!FindBlockDeviceByName(partition_name, &index)) {
+ LERROR << "No device named " << partition_name;
+ return false;
+ }
+ return UpdateBlockDeviceInfo(index, device_info);
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {
+ CHECK(index < block_devices_.size());
+
+ LpMetadataBlockDevice& block_device = block_devices_[index];
+ if (device_info.size != block_device.size) {
+ LERROR << "Device size does not match (got " << device_info.size << ", expected "
+ << block_device.size << ")";
+ return false;
+ }
+ if (device_info.logical_block_size != geometry_.logical_block_size) {
+ LERROR << "Device logical block size does not match (got " << device_info.logical_block_size
+ << ", expected " << geometry_.logical_block_size << ")";
+ return false;
+ }
// The kernel does not guarantee these values are present, so we only
// replace existing values if the new values are non-zero.
if (device_info.alignment) {
- device_info_.alignment = device_info.alignment;
+ block_device.alignment = device_info.alignment;
}
if (device_info.alignment_offset) {
- device_info_.alignment_offset = device_info.alignment_offset;
+ block_device.alignment_offset = device_info.alignment_offset;
}
+ return true;
}
bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
// Align the space needed up to the nearest sector.
- uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size);
+ uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
uint64_t old_size = partition->size();
if (aligned_size > old_size) {
@@ -530,10 +712,43 @@
ShrinkPartition(partition, aligned_size);
}
- LINFO << "Partition " << partition->name() << " will resize from " << old_size << " bytes to "
- << aligned_size << " bytes";
+ if (partition->size() != old_size) {
+ LINFO << "Partition " << partition->name() << " will resize from " << old_size
+ << " bytes to " << aligned_size << " bytes";
+ }
return true;
}
+std::vector<std::string> MetadataBuilder::ListGroups() const {
+ std::vector<std::string> names;
+ for (const auto& group : groups_) {
+ names.emplace_back(group->name());
+ }
+ return names;
+}
+
+void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
+ if (group_name == "default") {
+ // Cannot remove the default group.
+ return;
+ }
+ std::vector<std::string> partition_names;
+ for (const auto& partition : partitions_) {
+ if (partition->group_name() == group_name) {
+ partition_names.emplace_back(partition->name());
+ }
+ }
+
+ for (const auto& partition_name : partition_names) {
+ RemovePartition(partition_name);
+ }
+ for (auto iter = groups_.begin(); iter != groups_.end(); iter++) {
+ if ((*iter)->name() == group_name) {
+ groups_.erase(iter);
+ break;
+ }
+ }
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 0c7e43d..c27e300 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,24 +14,24 @@
* limitations under the License.
*/
+#include <fs_mgr.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
-#include "fs_mgr.h"
+
#include "utility.h"
using namespace std;
using namespace android::fs_mgr;
-
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
-static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
+using ::testing::ElementsAre;
TEST(liblp, BuildBasic) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
- Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(partition, nullptr);
EXPECT_EQ(partition->name(), "system");
- EXPECT_EQ(partition->guid(), TEST_GUID);
EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(partition->size(), 0);
EXPECT_EQ(builder->FindPartition("system"), partition);
@@ -42,8 +42,9 @@
TEST(liblp, ResizePartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
EXPECT_EQ(system->size(), 65536);
@@ -52,8 +53,11 @@
LinearExtent* extent = system->extents()[0]->AsLinearExtent();
ASSERT_NE(extent, nullptr);
EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
- // The first logical sector will be (4096+1024*2)/512 = 12.
- EXPECT_EQ(extent->physical_sector(), 12);
+ // The first logical sector will be:
+ // (LP_PARTITION_RESERVED_BYTES + 4096*2 + 1024*4) / 512
+ // Or, in terms of sectors (reserved + geometry + metadata):
+ // (8 + 16 + 8) = 32
+ EXPECT_EQ(extent->physical_sector(), 32);
// Test resizing to the same size.
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -82,7 +86,7 @@
extent = system->extents()[0]->AsLinearExtent();
ASSERT_NE(extent, nullptr);
EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
- EXPECT_EQ(extent->physical_sector(), 12);
+ EXPECT_EQ(extent->physical_sector(), 32);
// Test shrinking to 0.
builder->ResizePartition(system, 0);
@@ -92,9 +96,10 @@
TEST(liblp, PartitionAlignment) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
// Test that we align up to one sector.
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 10000), true);
EXPECT_EQ(system->size(), 12288);
@@ -118,6 +123,7 @@
TEST(liblp, MetadataAlignment) {
// Make sure metadata sizes get aligned up.
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+ ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
@@ -125,13 +131,14 @@
TEST(liblp, InternalAlignment) {
// Test the metadata fitting within alignment.
- BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
- EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
+ auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 1536);
// Test a large alignment offset thrown in.
device_info.alignment_offset = 753664;
@@ -139,8 +146,9 @@
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
- EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
+ super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 1472);
// Alignment offset without alignment doesn't mean anything.
device_info.alignment = 0;
@@ -154,8 +162,9 @@
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 78);
- EXPECT_EQ(exported->geometry.last_logical_sector, 1973);
+ super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 174);
// Test a small alignment with no alignment offset.
device_info.alignment = 11 * 1024;
@@ -163,17 +172,18 @@
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
- EXPECT_EQ(exported->geometry.first_logical_sector, 72);
- EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+ super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+ EXPECT_EQ(super_device->first_logical_sector, 160);
}
TEST(liblp, InternalPartitionAlignment) {
- BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
+ BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
- Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+ Partition* a = builder->AddPartition("a", 0);
ASSERT_NE(a, nullptr);
- Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+ Partition* b = builder->AddPartition("b", 0);
ASSERT_NE(b, nullptr);
// Add a bunch of small extents to each, interleaving.
@@ -210,11 +220,12 @@
// maximum size of a metadata blob. Then, we double that space since
// we store a backup copy of everything.
static constexpr uint64_t geometry = 4 * 1024;
- static constexpr uint64_t allocatable = total - (metadata * slots + geometry) * 2;
+ static constexpr uint64_t allocatable =
+ total - (metadata * slots + geometry) * 2 - LP_PARTITION_RESERVED_BYTES;
EXPECT_EQ(builder->AllocatableSpace(), allocatable);
EXPECT_EQ(builder->UsedSpace(), 0);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
EXPECT_EQ(builder->ResizePartition(system, allocatable), true);
EXPECT_EQ(system->size(), allocatable);
@@ -229,8 +240,8 @@
TEST(liblp, BuildComplex) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
- Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -251,11 +262,11 @@
ASSERT_NE(system2, nullptr);
ASSERT_NE(vendor1, nullptr);
EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
- EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system1->physical_sector(), 32);
EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
- EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(system2->physical_sector(), 224);
EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
- EXPECT_EQ(vendor1->physical_sector(), 140);
+ EXPECT_EQ(vendor1->physical_sector(), 160);
EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
}
@@ -263,15 +274,15 @@
TEST(liblp, AddInvalidPartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(partition, nullptr);
// Duplicate name.
- partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(partition, nullptr);
// Empty name.
- partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ partition = builder->AddPartition("", LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(partition, nullptr);
}
@@ -282,8 +293,8 @@
unique_ptr<MetadataBuilder> builder =
MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
- Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -293,6 +304,9 @@
unique_ptr<LpMetadata> exported = builder->Export();
EXPECT_NE(exported, nullptr);
+ auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
+
// Verify geometry. Some details of this may change if we change the
// metadata structures. So in addition to checking the exact values, we
// also check that they are internally consistent after.
@@ -301,14 +315,11 @@
EXPECT_EQ(geometry.struct_size, sizeof(geometry));
EXPECT_EQ(geometry.metadata_max_size, 1024);
EXPECT_EQ(geometry.metadata_slot_count, 2);
- EXPECT_EQ(geometry.first_logical_sector, 12);
- EXPECT_EQ(geometry.last_logical_sector, 2035);
+ EXPECT_EQ(super_device->first_logical_sector, 32);
static const size_t kMetadataSpace =
- (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
- uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
- EXPECT_GE(space_at_end, kMetadataSpace);
- EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+ ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
+ EXPECT_GE(super_device->first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
// Verify header.
const LpMetadataHeader& header = exported->header;
@@ -322,7 +333,6 @@
for (const auto& partition : exported->partitions) {
Partition* original = builder->FindPartition(GetPartitionName(partition));
ASSERT_NE(original, nullptr);
- EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = exported->extents[partition.first_extent_index + i];
LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
@@ -337,8 +347,8 @@
TEST(liblp, BuilderImport) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
- Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -357,20 +367,18 @@
EXPECT_EQ(system->size(), 98304);
ASSERT_EQ(system->extents().size(), 2);
- EXPECT_EQ(system->guid(), TEST_GUID);
EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(vendor->size(), 32768);
ASSERT_EQ(vendor->extents().size(), 1);
- EXPECT_EQ(vendor->guid(), TEST_GUID2);
EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
- EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system1->physical_sector(), 32);
EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
- EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(system2->physical_sector(), 224);
EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
}
@@ -378,17 +386,7 @@
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
- Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
- EXPECT_NE(system, nullptr);
-
- unique_ptr<LpMetadata> exported = builder->Export();
- EXPECT_EQ(exported, nullptr);
-}
-
-TEST(liblp, ExportInvalidGuid) {
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-
- Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition(name + name, LP_PARTITION_ATTR_READONLY);
EXPECT_NE(system, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
@@ -400,12 +398,12 @@
static const size_t kMetadataSize = 64 * 1024;
// No space to store metadata + geometry.
- BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
// No space to store metadata + geometry + one free sector.
- device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
+ device_info.size += LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2);
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
@@ -430,13 +428,10 @@
fs_mgr_free_fstab);
ASSERT_NE(fstab, nullptr);
- // This should read from the "super" partition once we have a well-defined
- // way to access it.
- struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data");
- ASSERT_NE(rec, nullptr);
+ PartitionOpener opener;
BlockDeviceInfo device_info;
- ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+ ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
// Sanity check that the device doesn't give us some weird inefficient
// alignment.
@@ -450,40 +445,55 @@
}
TEST(liblp, UpdateBlockDeviceInfo) {
- BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
- EXPECT_EQ(builder->block_device_info().size, device_info.size);
- EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
- EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
- EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size);
+ BlockDeviceInfo new_info;
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+
+ EXPECT_EQ(new_info.size, device_info.size);
+ EXPECT_EQ(new_info.alignment, device_info.alignment);
+ EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
+ EXPECT_EQ(new_info.logical_block_size, device_info.logical_block_size);
device_info.alignment = 0;
device_info.alignment_offset = 2048;
- builder->set_block_device_info(device_info);
- EXPECT_EQ(builder->block_device_info().alignment, 4096);
- EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+ ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+ EXPECT_EQ(new_info.alignment, 4096);
+ EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
device_info.alignment = 8192;
device_info.alignment_offset = 0;
- builder->set_block_device_info(device_info);
- EXPECT_EQ(builder->block_device_info().alignment, 8192);
- EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
+ ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+ EXPECT_EQ(new_info.alignment, 8192);
+ EXPECT_EQ(new_info.alignment_offset, 2048);
+
+ new_info.size += 4096;
+ ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+ EXPECT_EQ(new_info.size, 1024 * 1024);
+
+ new_info.logical_block_size = 512;
+ ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+ EXPECT_EQ(new_info.logical_block_size, 4096);
}
TEST(liblp, InvalidBlockSize) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
EXPECT_EQ(builder, nullptr);
}
TEST(liblp, AlignedExtentSize) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
- Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
+ Partition* partition = builder->AddPartition("system", 0);
ASSERT_NE(partition, nullptr);
ASSERT_TRUE(builder->ResizePartition(partition, 512));
EXPECT_EQ(partition->size(), 4096);
@@ -491,7 +501,134 @@
TEST(liblp, AlignedFreeSpace) {
// Only one sector free - at least one block is required.
- BlockDeviceInfo device_info(10240, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
ASSERT_EQ(builder, nullptr);
}
+
+TEST(liblp, HasDefaultGroup) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ EXPECT_FALSE(builder->AddGroup("default", 0));
+}
+
+TEST(liblp, GroupSizeLimits) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google", 16384));
+
+ Partition* partition = builder->AddPartition("system", "google", 0);
+ ASSERT_NE(partition, nullptr);
+ EXPECT_TRUE(builder->ResizePartition(partition, 8192));
+ EXPECT_EQ(partition->size(), 8192);
+ EXPECT_TRUE(builder->ResizePartition(partition, 16384));
+ EXPECT_EQ(partition->size(), 16384);
+ EXPECT_FALSE(builder->ResizePartition(partition, 32768));
+ EXPECT_EQ(partition->size(), 16384);
+}
+
+constexpr unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT
+ return x << 30;
+}
+constexpr unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT
+ return x << 20;
+}
+
+TEST(liblp, RemoveAndAddFirstPartition) {
+ auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
+ ASSERT_NE(nullptr, builder);
+ ASSERT_TRUE(builder->AddGroup("foo_a", 5_GiB));
+ ASSERT_TRUE(builder->AddGroup("foo_b", 5_GiB));
+ android::fs_mgr::Partition* p;
+ p = builder->AddPartition("system_a", "foo_a", 0);
+ ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+ p = builder->AddPartition("vendor_a", "foo_a", 0);
+ ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+ p = builder->AddPartition("system_b", "foo_b", 0);
+ ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+ p = builder->AddPartition("vendor_b", "foo_b", 0);
+ ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+
+ builder->RemovePartition("system_a");
+ builder->RemovePartition("vendor_a");
+ p = builder->AddPartition("system_a", "foo_a", 0);
+ ASSERT_TRUE(p && builder->ResizePartition(p, 3_GiB));
+ p = builder->AddPartition("vendor_a", "foo_a", 0);
+ ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+}
+
+TEST(liblp, ListGroups) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(builder->AddGroup("example", 0));
+
+ std::vector<std::string> groups = builder->ListGroups();
+ ASSERT_THAT(groups, ElementsAre("default", "example"));
+}
+
+TEST(liblp, RemoveGroupAndPartitions) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(builder->AddGroup("example", 0));
+ ASSERT_NE(builder->AddPartition("system", "default", 0), nullptr);
+ ASSERT_NE(builder->AddPartition("vendor", "example", 0), nullptr);
+
+ builder->RemoveGroupAndPartitions("example");
+ ASSERT_NE(builder->FindPartition("system"), nullptr);
+ ASSERT_EQ(builder->FindPartition("vendor"), nullptr);
+ ASSERT_THAT(builder->ListGroups(), ElementsAre("default"));
+
+ builder->RemoveGroupAndPartitions("default");
+ ASSERT_NE(builder->FindPartition("system"), nullptr);
+}
+
+TEST(liblp, MultipleBlockDevices) {
+ std::vector<BlockDeviceInfo> partitions = {
+ BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
+ BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
+ BlockDeviceInfo("product_a", 64_MiB, 786432, 753664, 4096),
+ };
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+ EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+
+ // Create a partition that spans 3 devices.
+ Partition* p = builder->AddPartition("system_a", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 466976768));
+
+ unique_ptr<LpMetadata> metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->block_devices.size(), 3);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "system_a");
+ EXPECT_EQ(metadata->block_devices[0].size, 256_MiB);
+ EXPECT_EQ(metadata->block_devices[0].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), "vendor_a");
+ EXPECT_EQ(metadata->block_devices[1].size, 128_MiB);
+ EXPECT_EQ(metadata->block_devices[1].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), "product_a");
+ EXPECT_EQ(metadata->block_devices[2].size, 64_MiB);
+ EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
+ ASSERT_EQ(metadata->extents.size(), 3);
+ EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+ EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[0].target_data, 1984);
+ EXPECT_EQ(metadata->extents[0].target_source, 0);
+ EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+ EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[1].target_data, 1472);
+ EXPECT_EQ(metadata->extents[1].target_source, 1);
+ EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+ EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[2].target_data, 1472);
+ EXPECT_EQ(metadata->extents[2].target_source, 2);
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 742b1d0..46bdfa4 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -19,8 +19,6 @@
#include <limits.h>
#include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
#include "reader.h"
#include "utility.h"
@@ -30,12 +28,17 @@
namespace fs_mgr {
std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
- LpMetadataGeometry geometry;
- if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+ if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed";
return nullptr;
}
- if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << " read failed";
+ return nullptr;
+ }
+ LpMetadataGeometry geometry;
+ if (!ParseGeometry(buffer.get(), &geometry)) {
return nullptr;
}
return ParseMetadata(geometry, fd);
@@ -61,7 +64,7 @@
std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
android::base::unique_fd fd(open(file, O_RDONLY));
if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
return nullptr;
}
return ReadFromImageFile(fd);
@@ -74,7 +77,7 @@
std::string everything = geometry + metadata;
if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
- PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+ PERROR << __PRETTY_FUNCTION__ << " write " << everything.size() << " bytes failed";
return false;
}
return true;
@@ -83,47 +86,54 @@
bool WriteToImageFile(const char* file, const LpMetadata& input) {
android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
return false;
}
return WriteToImageFile(fd, input);
}
-// We use an object to build the sparse file since it requires that data
-// pointers be held alive until the sparse file is destroyed. It's easier
-// to do this when the data pointers are all in one place.
-class SparseBuilder {
- public:
- SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images);
-
- bool Build();
- bool Export(const char* file);
- bool IsValid() const { return file_ != nullptr; }
-
- private:
- bool AddData(const std::string& blob, uint64_t sector);
- bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
- int OpenImageFile(const std::string& file);
- bool SectorToBlock(uint64_t sector, uint32_t* block);
-
- const LpMetadata& metadata_;
- const LpMetadataGeometry& geometry_;
- uint32_t block_size_;
- std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
- std::string primary_blob_;
- std::string backup_blob_;
- std::map<std::string, std::string> images_;
- std::vector<android::base::unique_fd> temp_fds_;
-};
-
SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images)
: metadata_(metadata),
geometry_(metadata.geometry),
block_size_(block_size),
- file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy),
- images_(images) {}
+ file_(nullptr, sparse_file_destroy),
+ images_(images) {
+ uint64_t total_size = GetTotalSuperPartitionSize(metadata);
+ if (block_size % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+ return;
+ }
+ if (total_size % block_size != 0) {
+ LERROR << "Device size must be a multiple of the block size, " << block_size;
+ return;
+ }
+ if (metadata.geometry.metadata_max_size % block_size != 0) {
+ LERROR << "Metadata max size must be a multiple of the block size, " << block_size;
+ return;
+ }
+ if (LP_METADATA_GEOMETRY_SIZE % block_size != 0) {
+ LERROR << "Geometry size is not a multiple of the block size, " << block_size;
+ return;
+ }
+ if (LP_PARTITION_RESERVED_BYTES % block_size != 0) {
+ LERROR << "Reserved size is not a multiple of the block size, " << block_size;
+ return;
+ }
+
+ uint64_t num_blocks = total_size % block_size;
+ if (num_blocks >= UINT_MAX) {
+ // libsparse counts blocks in unsigned 32-bit integers, so we check to
+ // make sure we're not going to overflow.
+ LERROR << "Block device is too large to encode with libsparse.";
+ return;
+ }
+
+ file_.reset(sparse_file_new(block_size_, total_size));
+ if (!file_) {
+ LERROR << "Could not allocate sparse file of size " << total_size;
+ }
+}
bool SparseBuilder::Export(const char* file) {
android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
@@ -170,20 +180,21 @@
}
bool SparseBuilder::Build() {
+ if (sparse_file_add_fill(file_.get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
+ LERROR << "Could not add initial sparse block for reserved zeroes";
+ return false;
+ }
+
std::string geometry_blob = SerializeGeometry(geometry_);
std::string metadata_blob = SerializeMetadata(metadata_);
metadata_blob.resize(geometry_.metadata_max_size);
- std::string all_metadata;
- for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
- all_metadata += metadata_blob;
+ // Two copies of geometry, then two copies of each metadata slot.
+ all_metadata_ += geometry_blob + geometry_blob;
+ for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
+ all_metadata_ += metadata_blob;
}
-
- // Metadata immediately follows geometry, and we write the same metadata
- // to all slots. Note that we don't bother trying to write skip chunks
- // here since it's a small amount of data.
- primary_blob_ = geometry_blob + all_metadata;
- if (!AddData(primary_blob_, 0)) {
+ if (!AddData(all_metadata_, 0)) {
return false;
}
@@ -202,17 +213,6 @@
LERROR << "Partition image was specified but no partition was found.";
return false;
}
-
- // The backup area contains all metadata slots, and then geometry. Similar
- // to before we write the metadata to every slot.
- int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0);
- uint64_t backups_start = geometry_.block_device_size + backup_offset;
- uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
-
- backup_blob_ = all_metadata + geometry_blob;
- if (!AddData(backup_blob_, backup_sector)) {
- return false;
- }
return true;
}
@@ -336,31 +336,8 @@
bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images) {
- if (block_size % LP_SECTOR_SIZE != 0) {
- LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
- return false;
- }
- if (metadata.geometry.block_device_size % block_size != 0) {
- LERROR << "Device size must be a multiple of the block size, " << block_size;
- return false;
- }
- uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
- if (num_blocks >= UINT_MAX) {
- // libsparse counts blocks in unsigned 32-bit integers, so we check to
- // make sure we're not going to overflow.
- LERROR << "Block device is too large to encode with libsparse.";
- return false;
- }
-
SparseBuilder builder(metadata, block_size, images);
- if (!builder.IsValid()) {
- LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
- return false;
- }
- if (!builder.Build()) {
- return false;
- }
- return builder.Export(file);
+ return builder.IsValid() && builder.Build() && builder.Export(file);
}
} // namespace fs_mgr
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 3a999b8..a9ef8ce 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -14,7 +14,14 @@
* limitations under the License.
*/
+#include <stdint.h>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
#include <liblp/liblp.h>
+#include <sparse/sparse.h>
namespace android {
namespace fs_mgr {
@@ -25,5 +32,34 @@
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
bool WriteToImageFile(int fd, const LpMetadata& metadata);
+// We use an object to build the sparse file since it requires that data
+// pointers be held alive until the sparse file is destroyed. It's easier
+// to do this when the data pointers are all in one place.
+class SparseBuilder {
+ public:
+ SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images);
+
+ bool Build();
+ bool Export(const char* file);
+ bool IsValid() const { return file_ != nullptr; }
+
+ sparse_file* file() const { return file_.get(); }
+
+ private:
+ bool AddData(const std::string& blob, uint64_t sector);
+ bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
+ int OpenImageFile(const std::string& file);
+ bool SectorToBlock(uint64_t sector, uint32_t* block);
+
+ const LpMetadata& metadata_;
+ const LpMetadataGeometry& geometry_;
+ uint32_t block_size_;
+ std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+ std::string all_metadata_;
+ std::map<std::string, std::string> images_;
+ std::vector<android::base::unique_fd> temp_fds_;
+};
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 2780825..f9de106 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,6 +24,7 @@
#include <memory>
#include "liblp.h"
+#include "partition_opener.h"
namespace android {
namespace fs_mgr {
@@ -34,34 +35,13 @@
static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
static const uint32_t kDefaultBlockSize = 4096;
-struct BlockDeviceInfo {
- BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
- BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
- uint32_t logical_block_size)
- : size(size),
- alignment(alignment),
- alignment_offset(alignment_offset),
- logical_block_size(logical_block_size) {}
- // Size of the block device, in bytes.
- uint64_t size;
- // Optimal target alignment, in bytes. Partition extents will be aligned to
- // this value by default. This value must be 0 or a multiple of 512.
- uint32_t alignment;
- // Alignment offset to parent device (if any), in bytes. The sector at
- // |alignment_offset| on the target device is correctly aligned on its
- // parent device. This value must be 0 or a multiple of 512.
- uint32_t alignment_offset;
- // Block size, for aligning extent sizes and partition sizes.
- uint32_t logical_block_size;
-};
-
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
virtual ~Extent() {}
- virtual void AddTo(LpMetadata* out) const = 0;
+ virtual bool AddTo(LpMetadata* out) const = 0;
virtual LinearExtent* AsLinearExtent() { return nullptr; }
uint64_t num_sectors() const { return num_sectors_; }
@@ -74,16 +54,18 @@
// This corresponds to a dm-linear target.
class LinearExtent final : public Extent {
public:
- LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
- : Extent(num_sectors), physical_sector_(physical_sector) {}
+ LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector)
+ : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}
- void AddTo(LpMetadata* metadata) const override;
+ bool AddTo(LpMetadata* metadata) const override;
LinearExtent* AsLinearExtent() override { return this; }
uint64_t physical_sector() const { return physical_sector_; }
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
+ uint32_t device_index() const { return device_index_; }
private:
+ uint32_t device_index_;
uint64_t physical_sector_;
};
@@ -92,14 +74,27 @@
public:
explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
- void AddTo(LpMetadata* out) const override;
+ bool AddTo(LpMetadata* out) const override;
+};
+
+class PartitionGroup final {
+ public:
+ explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
+ : name_(name), maximum_size_(maximum_size) {}
+
+ const std::string& name() const { return name_; }
+ uint64_t maximum_size() const { return maximum_size_; }
+
+ private:
+ std::string name_;
+ uint64_t maximum_size_;
};
class Partition final {
friend class MetadataBuilder;
public:
- Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+ Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
// Add a raw extent.
void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -107,9 +102,13 @@
// Remove all extents from this partition.
void RemoveExtents();
+ // Compute the size used by linear extents. This is the same as size(),
+ // but does not factor in extents which do not take up space.
+ uint64_t BytesOnDisk() const;
+
const std::string& name() const { return name_; }
+ const std::string& group_name() const { return group_name_; }
uint32_t attributes() const { return attributes_; }
- const std::string& guid() const { return guid_; }
const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
uint64_t size() const { return size_; }
@@ -117,7 +116,7 @@
void ShrinkTo(uint64_t aligned_size);
std::string name_;
- std::string guid_;
+ std::string group_name_;
std::vector<std::unique_ptr<Extent>> extents_;
uint32_t attributes_;
uint64_t size_;
@@ -125,22 +124,29 @@
class MetadataBuilder {
public:
- // Construct an empty logical partition table builder. The block device size
- // and maximum metadata size must be specified, as this will determine which
- // areas of the physical partition can be flashed for metadata vs for logical
- // partitions.
+ // Construct an empty logical partition table builder given the specified
+ // map of partitions that are available for storing logical partitions.
+ //
+ // At least one partition in the list must be the "super" device, where
+ // metadata will be stored.
//
// If the parameters would yield invalid metadata, nullptr is returned. This
- // could happen if the block device size is too small to store the metadata
- // and backup copies.
- static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+ // could happen if the super device is too small to store all required
+ // metadata.
+ static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,
+ const std::string& super_partition,
uint32_t metadata_max_size,
uint32_t metadata_slot_count);
// Import an existing table for modification. This reads metadata off the
// given block device and imports it. It also adjusts alignment information
// based on run-time values in the operating system.
- static std::unique_ptr<MetadataBuilder> New(const std::string& block_device,
+ static std::unique_ptr<MetadataBuilder> New(const IPartitionOpener& opener,
+ const std::string& super_partition,
+ uint32_t slot_number);
+
+ // Same as above, but use the default PartitionOpener.
+ static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
uint32_t slot_number);
// Import an existing table for modification. If the table is not valid, for
@@ -148,21 +154,42 @@
// This method is for testing or changing off-line tables.
static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+ // Helper function for a single super partition, for tests.
+ static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ return New({device_info}, device_info.partition_name, metadata_max_size,
+ metadata_slot_count);
+ }
+
// Wrapper around New() with a BlockDeviceInfo that only specifies a device
// size. This is a convenience method for tests.
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
- BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
+ BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0,
+ kDefaultBlockSize);
return New(device_info, metadata_max_size, metadata_slot_count);
}
+ // Define a new partition group. By default there is one group called
+ // "default", with an unrestricted size. A non-zero size will restrict the
+ // total space used by all partitions in the group.
+ //
+ // This can fail and return false if the group already exists.
+ bool AddGroup(const std::string& group_name, uint64_t maximum_size);
+
// Export metadata so it can be serialized to an image, to disk, or mounted
// via device-mapper.
std::unique_ptr<LpMetadata> Export();
// Add a partition, returning a handle so it can be sized as needed. If a
// partition with the given name already exists, nullptr is returned.
- Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+ Partition* AddPartition(const std::string& name, const std::string& group_name,
+ uint32_t attributes);
+
+ // Same as AddPartition above, but uses the default partition group which
+ // has no size restrictions.
+ Partition* AddPartition(const std::string& name, uint32_t attributes);
// Delete a partition by name if it exists.
void RemovePartition(const std::string& name);
@@ -170,6 +197,9 @@
// Find a partition by name. If no partition is found, nullptr is returned.
Partition* FindPartition(const std::string& name);
+ // Find a group by name. If no group is found, nullptr is returned.
+ PartitionGroup* FindGroup(const std::string& name);
+
// Grow or shrink a partition to the requested size. This size will be
// rounded UP to the nearest block (512 bytes).
//
@@ -186,10 +216,14 @@
uint64_t AllocatableSpace() const;
uint64_t UsedSpace() const;
- // Merge new block device information into previous values. Alignment values
- // are only overwritten if the new values are non-zero.
- void set_block_device_info(const BlockDeviceInfo& device_info);
- const BlockDeviceInfo& block_device_info() const { return device_info_; }
+ // Return a list of all group names.
+ std::vector<std::string> ListGroups() const;
+
+ // Remove all partitions belonging to a group, then remove the group.
+ void RemoveGroupAndPartitions(const std::string& group_name);
+
+ bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
+ bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
private:
MetadataBuilder();
@@ -197,16 +231,40 @@
MetadataBuilder(MetadataBuilder&&) = delete;
MetadataBuilder& operator=(const MetadataBuilder&) = delete;
MetadataBuilder& operator=(MetadataBuilder&&) = delete;
- bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+ bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+ uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
bool GrowPartition(Partition* partition, uint64_t aligned_size);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- uint64_t AlignSector(uint64_t sector);
+ uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+ uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
+ bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
+ bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
+
+ struct Interval {
+ uint32_t device_index;
+ uint64_t start;
+ uint64_t end;
+
+ Interval(uint32_t device_index, uint64_t start, uint64_t end)
+ : device_index(device_index), start(start), end(end) {}
+ uint64_t length() const { return end - start; }
+
+ // Note: the device index is not included in sorting (intervals are
+ // sorted in per-device lists).
+ bool operator<(const Interval& other) const {
+ return (start == other.start) ? end < other.end : start < other.start;
+ }
+ };
+ std::vector<Interval> GetFreeRegions() const;
+ void ExtentsToFreeList(const std::vector<Interval>& extents,
+ std::vector<Interval>* free_regions) const;
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
- BlockDeviceInfo device_info_;
+ std::vector<std::unique_ptr<PartitionGroup>> groups_;
+ std::vector<LpMetadataBlockDevice> block_devices_;
};
// 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 6da24f6..4669cea 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -24,7 +24,10 @@
#include <memory>
#include <string>
+#include <android-base/unique_fd.h>
+
#include "metadata_format.h"
+#include "partition_opener.h"
namespace android {
namespace fs_mgr {
@@ -36,13 +39,16 @@
LpMetadataHeader header;
std::vector<LpMetadataPartition> partitions;
std::vector<LpMetadataExtent> extents;
+ std::vector<LpMetadataPartitionGroup> groups;
+ std::vector<LpMetadataBlockDevice> block_devices;
};
// Place an initial partition table on the device. This will overwrite the
// existing geometry, and should not be used for normal partition table
// updates. False can be returned if the geometry is incompatible with the
// block device or an I/O error occurs.
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata);
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata);
// Update the partition table for a given metadata slot number. False is
// returned if an error occurs, which can include:
@@ -50,12 +56,19 @@
// - I/O error.
// - Corrupt or missing metadata geometry on disk.
// - Incompatible geometry.
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
- uint32_t slot_number);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number);
// Read logical partition metadata from its predetermined location on a block
// device. If readback fails, we also attempt to load from a backup copy.
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+ const std::string& super_partition, uint32_t slot_number);
+
+// Helper functions that use the default PartitionOpener.
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata);
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+ uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
// Read/Write logical partition metadata to an image file, for diagnostics or
// flashing.
@@ -67,7 +80,15 @@
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
-std::string GetPartitionGuid(const LpMetadataPartition& partition);
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
+
+// Return the block device that houses the super partition metadata; returns
+// null on failure.
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
+
+// Return the total size of all partitions comprising the super partition.
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
// Helper to return a slot number for a slot suffix.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 52c80f7..1e40df3 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MAJOR_VERSION 8
#define LP_METADATA_MINOR_VERSION 0
/* Attributes for the LpMetadataPartition::attributes field.
@@ -58,22 +58,27 @@
* +--------------------+
* | Disk Geometry |
* +--------------------+
- * | Metadata |
+ * | Geometry Backup |
* +--------------------+
- * | Logical Partitions |
+ * | Metadata |
* +--------------------+
* | Backup Metadata |
* +--------------------+
- * | Geometry Backup |
+ * | Logical Partitions |
* +--------------------+
*/
-#define LP_METADATA_PARTITION_NAME "super"
+#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
#define LP_SECTOR_SIZE 512
-/* This structure is stored at sector 0 in the first 4096 bytes of the
- * partition, and again in the very last 4096 bytes. It is never modified and
+/* Amount of space reserved at the start of every super partition to avoid
+ * creating an accidental boot sector.
+ */
+#define LP_PARTITION_RESERVED_BYTES 4096
+
+/* This structure is stored at block 0 in the first 4096 bytes of the
+ * partition, and again in the following block. It is never modified and
* describes how logical partition information can be located.
*/
typedef struct LpMetadataGeometry {
@@ -98,48 +103,10 @@
*/
uint32_t metadata_slot_count;
- /* 48: First usable sector for allocating logical partitions. this will be
- * the first sector after the initial 4096 geometry block, followed by the
- * space consumed by metadata_max_size*metadata_slot_count.
- */
- uint64_t first_logical_sector;
-
- /* 56: Last usable sector, inclusive, for allocating logical partitions.
- * At the end of this sector will follow backup metadata slots and the
- * backup geometry block at the very end.
- */
- uint64_t last_logical_sector;
-
- /* 64: Alignment for defining partitions or partition extents. For example,
- * an alignment of 1MiB will require that all partitions have a size evenly
- * divisible by 1MiB, and that the smallest unit the partition can grow by
- * is 1MiB.
- *
- * Alignment is normally determined at runtime when growing or adding
- * partitions. If for some reason the alignment cannot be determined, then
- * this predefined alignment in the geometry is used instead. By default
- * it is set to 1MiB.
- */
- uint32_t alignment;
-
- /* 68: Alignment offset for "stacked" devices. For example, if the "super"
- * partition itself is not aligned within the parent block device's
- * partition table, then we adjust for this in deciding where to place
- * |first_logical_sector|.
- *
- * Similar to |alignment|, this will be derived from the operating system.
- * If it cannot be determined, it is assumed to be 0.
- */
- uint32_t alignment_offset;
-
- /* 72: Block device size, as specified when the metadata was created. This
- * can be used to verify the geometry against a target device.
- */
- uint64_t block_device_size;
-
- /* 76: Logical block size of the super partition block device. This is the
- * minimal alignment for partition and extent sizes, and it must be a
- * multiple of LP_SECTOR_SIZE.
+ /* 48: Logical block size. This is the minimal alignment for partition and
+ * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
+ * this must be equal across all LUNs that comprise the super partition,
+ * and thus this field is stored in the geometry, not per-device.
*/
uint32_t logical_block_size;
} __attribute__((packed)) LpMetadataGeometry;
@@ -216,6 +183,10 @@
LpMetadataTableDescriptor partitions;
/* 92: Extent table descriptor. */
LpMetadataTableDescriptor extents;
+ /* 104: Updateable group descriptor. */
+ LpMetadataTableDescriptor groups;
+ /* 116: Block device table. */
+ LpMetadataTableDescriptor block_devices;
} __attribute__((packed)) LpMetadataHeader;
/* This struct defines a logical partition entry, similar to what would be
@@ -230,21 +201,21 @@
*/
char name[36];
- /* 36: Globally unique identifier (GUID) of this partition. */
- uint8_t guid[16];
-
- /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+ /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
uint32_t attributes;
- /* 56: Index of the first extent owned by this partition. The extent will
+ /* 40: Index of the first extent owned by this partition. The extent will
* start at logical sector 0. Gaps between extents are not allowed.
*/
uint32_t first_extent_index;
- /* 60: Number of extents in the partition. Every partition must have at
+ /* 44: Number of extents in the partition. Every partition must have at
* least one extent.
*/
uint32_t num_extents;
+
+ /* 48: Group this partition belongs to. */
+ uint32_t group_index;
} __attribute__((packed)) LpMetadataPartition;
/* This extent is a dm-linear target, and the index is an index into the
@@ -269,8 +240,70 @@
* ZERO: This field must be 0.
*/
uint64_t target_data;
+
+ /* 20: Contents depends on target_type.
+ *
+ * LINEAR: Must be an index into the block devices table.
+ * ZERO: This field must be 0.
+ */
+ uint32_t target_source;
} __attribute__((packed)) LpMetadataExtent;
+/* This struct defines an entry in the groups table. Each group has a maximum
+ * size, and partitions in a group must not exceed that size. There is always
+ * a "default" group of unlimited size, which is used when not using update
+ * groups or when using overlayfs or fastbootd.
+ */
+typedef struct LpMetadataPartitionGroup {
+ /* 0: Name of this group. Any unused characters must be 0. */
+ char name[36];
+
+ /* 36: Maximum size in bytes. If 0, the group has no maximum size. */
+ uint64_t maximum_size;
+} LpMetadataPartitionGroup;
+
+/* This struct defines an entry in the block_devices table. There must be at
+ * least one device, and the first device must represent the partition holding
+ * the super metadata.
+ */
+typedef struct LpMetadataBlockDevice {
+ /* 0: First usable sector for allocating logical partitions. this will be
+ * the first sector after the initial geometry blocks, followed by the
+ * space consumed by metadata_max_size*metadata_slot_count*2.
+ */
+ uint64_t first_logical_sector;
+
+ /* 8: Alignment for defining partitions or partition extents. For example,
+ * an alignment of 1MiB will require that all partitions have a size evenly
+ * divisible by 1MiB, and that the smallest unit the partition can grow by
+ * is 1MiB.
+ *
+ * Alignment is normally determined at runtime when growing or adding
+ * partitions. If for some reason the alignment cannot be determined, then
+ * this predefined alignment in the geometry is used instead. By default
+ * it is set to 1MiB.
+ */
+ uint32_t alignment;
+
+ /* 12: Alignment offset for "stacked" devices. For example, if the "super"
+ * partition itself is not aligned within the parent block device's
+ * partition table, then we adjust for this in deciding where to place
+ * |first_logical_sector|.
+ *
+ * Similar to |alignment|, this will be derived from the operating system.
+ * If it cannot be determined, it is assumed to be 0.
+ */
+ uint32_t alignment_offset;
+
+ /* 16: Block device size, as specified when the metadata was created. This
+ * can be used to verify the geometry against a target device.
+ */
+ uint64_t size;
+
+ /* 24: Partition name in the GPT. Any unused characters must be 0. */
+ char partition_name[36];
+} LpMetadataBlockDevice;
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
new file mode 100644
index 0000000..e506bd5
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -0,0 +1,77 @@
+//
+// 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 <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct BlockDeviceInfo {
+ BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+ BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,
+ uint32_t alignment_offset, uint32_t logical_block_size)
+ : size(size),
+ alignment(alignment),
+ alignment_offset(alignment_offset),
+ logical_block_size(logical_block_size),
+ partition_name(partition_name) {}
+ // Size of the block device, in bytes.
+ uint64_t size;
+ // Optimal target alignment, in bytes. Partition extents will be aligned to
+ // this value by default. This value must be 0 or a multiple of 512.
+ uint32_t alignment;
+ // Alignment offset to parent device (if any), in bytes. The sector at
+ // |alignment_offset| on the target device is correctly aligned on its
+ // parent device. This value must be 0 or a multiple of 512.
+ uint32_t alignment_offset;
+ // Block size, for aligning extent sizes and partition sizes.
+ uint32_t logical_block_size;
+ // The physical partition name for this block device, as it would appear in
+ // the GPT or under /dev/block/by-name.
+ std::string partition_name;
+};
+
+// Test-friendly interface for interacting with partitions.
+class IPartitionOpener {
+ public:
+ virtual ~IPartitionOpener() = default;
+
+ // Open the given named physical partition with the provided open() flags.
+ // The name can be an absolute path if the full path is already known.
+ virtual android::base::unique_fd Open(const std::string& partition_name, int flags) const = 0;
+
+ // Return block device information about the given named physical partition.
+ // The name can be an absolute path if the full path is already known.
+ virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+};
+
+// Helper class to implement IPartitionOpener. If |partition_name| is not an
+// absolute path, /dev/block/by-name/ will be prepended.
+class PartitionOpener : public IPartitionOpener {
+ public:
+ virtual android::base::unique_fd Open(const std::string& partition_name,
+ int flags) const override;
+ virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index eda68fd..603e5c0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -26,6 +26,7 @@
#include "images.h"
#include "reader.h"
+#include "test_partition_opener.h"
#include "utility.h"
#include "writer.h"
@@ -37,8 +38,6 @@
static const size_t kDiskSize = 131072;
static const size_t kMetadataSize = 512;
static const size_t kMetadataSlots = 2;
-static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
// Helper function for creating an in-memory file descriptor. This lets us
// simulate read/writing logical partition metadata as if we had a block device
@@ -81,7 +80,7 @@
}
static bool AddDefaultPartitions(MetadataBuilder* builder) {
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
if (!system) {
return false;
}
@@ -103,7 +102,9 @@
if (!exported) {
return {};
}
- if (!FlashPartitionTable(fd, *exported.get())) {
+
+ TestPartitionOpener opener({{"super", fd}});
+ if (!FlashPartitionTable(opener, "super", *exported.get())) {
return {};
}
return fd;
@@ -118,14 +119,16 @@
ASSERT_TRUE(GetDescriptorSize(fd, &size));
ASSERT_EQ(size, kDiskSize);
+ TestPartitionOpener opener({{"super", fd}});
+
// Verify that we can't read unwritten metadata.
- ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+ ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
}
// Flashing metadata should not work if the metadata was created for a larger
// disk than the destination disk.
TEST(liblp, ExportDiskTooSmall) {
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
@@ -135,7 +138,9 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- EXPECT_FALSE(FlashPartitionTable(fd, *exported.get()));
+ TestPartitionOpener opener({{"super", fd}});
+
+ EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
}
// Test the basics of flashing a partition and reading it back.
@@ -147,23 +152,23 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Export and flash.
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
- ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+ ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
// Read back. Note that some fields are only filled in during
// serialization, so exported and imported will not be identical. For
// example, table sizes and checksums are computed in WritePartitionTable.
// Therefore we check on a field-by-field basis.
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
// Check geometry and header.
EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
- EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
- EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
EXPECT_EQ(exported->header.major_version, imported->header.major_version);
EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
EXPECT_EQ(exported->header.header_size, imported->header.header_size);
@@ -171,7 +176,6 @@
// Check partition tables.
ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
- EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
EXPECT_EQ(exported->partitions[0].first_extent_index,
imported->partitions[0].first_extent_index);
@@ -182,6 +186,11 @@
EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+
+ // Check block devices table.
+ ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size());
+ EXPECT_EQ(exported->block_devices[0].first_logical_sector,
+ imported->block_devices[0].first_logical_sector);
}
// Test that we can update metadata slots without disturbing others.
@@ -189,34 +198,40 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
// Change the name before writing to the next slot.
strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
- ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
// Read back the original slot, make sure it hasn't changed.
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
// Now read back the new slot, and verify that it has a different name.
- imported = ReadMetadata(fd, 1);
+ imported = ReadMetadata(opener, "super", 1);
ASSERT_NE(imported, nullptr);
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
+ auto super_device = GetMetadataSuperBlockDevice(*imported.get());
+ ASSERT_NE(super_device, nullptr);
+
+ uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
+
// Verify that we didn't overwrite anything in the logical paritition area.
// We expect the disk to be filled with 0xcc on creation so we can read
// this back and compare it.
char expected[LP_SECTOR_SIZE];
memset(expected, 0xcc, sizeof(expected));
- for (uint64_t i = imported->geometry.first_logical_sector;
- i <= imported->geometry.last_logical_sector; i++) {
+ for (uint64_t i = super_device->first_logical_sector; i < last_sector; i++) {
char buffer[LP_SECTOR_SIZE];
ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
@@ -228,15 +243,17 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Make sure all slots are filled.
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
ASSERT_NE(metadata, nullptr);
for (uint32_t i = 1; i < kMetadataSlots; i++) {
- ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *metadata.get(), i));
}
// Verify that we can't read unavailable slots.
- EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+ EXPECT_EQ(ReadMetadata(opener, "super", kMetadataSlots), nullptr);
}
// Test that updating a metadata slot does not allow it to be computed based
@@ -245,27 +262,28 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
imported->geometry.metadata_slot_count++;
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- imported->geometry.first_logical_sector++;
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+ ASSERT_EQ(imported->block_devices.size(), 1);
+ imported->block_devices[0].first_logical_sector++;
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- imported->geometry.last_logical_sector--;
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
}
// Test that changing one bit of metadata is enough to break the checksum.
@@ -273,6 +291,8 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
LpMetadataGeometry geometry;
ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
@@ -281,7 +301,7 @@
bad_geometry.metadata_slot_count++;
ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
ASSERT_NE(metadata, nullptr);
EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
}
@@ -290,39 +310,45 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
char corruption[LP_METADATA_GEOMETRY_SIZE];
memset(corruption, 0xff, sizeof(corruption));
- // Corrupt the first 4096 bytes of the disk.
- ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ // Corrupt the primary geometry.
+ ASSERT_GE(lseek(fd, GetPrimaryGeometryOffset(), SEEK_SET), 0);
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+ EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
- // Corrupt the last 4096 bytes too.
- ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+ // Corrupt the backup geometry.
+ ASSERT_GE(lseek(fd, GetBackupGeometryOffset(), SEEK_SET), 0);
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+ EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
}
TEST(liblp, ReadBackupMetadata) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
char corruption[kMetadataSize];
memset(corruption, 0xff, sizeof(corruption));
- ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
- ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+ off_t offset = GetPrimaryMetadataOffset(metadata->geometry, 0);
- off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+ ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
+
+ offset = GetBackupMetadataOffset(metadata->geometry, 0);
// Corrupt the backup metadata.
- ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+ ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
- EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+ EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
}
// Test that we don't attempt to write metadata if it would overflow its
@@ -331,21 +357,20 @@
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
- // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
- size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
- EXPECT_LT(max_partitions, 10);
+ // Compute the maximum number of partitions we can fit in 512 bytes of
+ // metadata. By default there is the header, one partition group, and a
+ // block device entry.
+ static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) -
+ sizeof(LpMetadataPartitionGroup) -
+ sizeof(LpMetadataBlockDevice);
+ size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
// Add this number of partitions.
Partition* partition = nullptr;
for (size_t i = 0; i < max_partitions; i++) {
- std::string guid = std::string(TEST_GUID) + to_string(i);
- partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+ partition = builder->AddPartition(to_string(i), LP_PARTITION_ATTR_NONE);
ASSERT_NE(partition, nullptr);
}
- ASSERT_NE(partition, nullptr);
- // Add one extent to any partition to fill up more space - we're at 508
- // bytes after this, out of 512.
- ASSERT_TRUE(builder->ResizePartition(partition, 1024));
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
@@ -353,28 +378,30 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Check that we are able to write our table.
- ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+ ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
// Check that adding one more partition overflows the metadata allotment.
- partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ partition = builder->AddPartition("final", LP_PARTITION_ATTR_NONE);
EXPECT_NE(partition, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
// The new table should be too large to be written.
- ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *exported.get(), 1));
+
+ auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+ ASSERT_NE(super_device, nullptr);
// Check that the first and last logical sectors weren't touched when we
// wrote this almost-full metadata.
char expected[LP_SECTOR_SIZE];
memset(expected, 0xcc, sizeof(expected));
char buffer[LP_SECTOR_SIZE];
- ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
- ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
- EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
- ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_GE(lseek(fd, super_device->first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
}
@@ -385,6 +412,7 @@
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
ASSERT_GE(fd, 0);
@@ -459,23 +487,25 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Read and write it back.
writer.FailOnWrite(1);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
// We should still be able to read the backup copy.
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
// Flash again, this time fail the backup copy. We should still be able
// to read the primary.
writer.FailOnWrite(3);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
- imported = ReadMetadata(fd, 0);
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
}
@@ -485,23 +515,25 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Read and write it back.
writer.FailOnWrite(2);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
// We should still be able to read the primary copy.
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
// Flash again, this time fail the primary copy. We should still be able
// to read the primary.
writer.FailOnWrite(2);
- ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
- imported = ReadMetadata(fd, 0);
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
}
@@ -512,20 +544,22 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Change the name of the existing partition.
- unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> new_table = ReadMetadata(opener, "super", 0);
ASSERT_NE(new_table, nullptr);
ASSERT_GE(new_table->partitions.size(), 1);
new_table->partitions[0].name[0]++;
// Flash it, but fail to write the backup copy.
writer.FailAfterWrite(2);
- ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+ ASSERT_FALSE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
// When we read back, we should get the updated primary copy.
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_GE(new_table->partitions.size(), 1);
ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
@@ -534,10 +568,43 @@
// Note that the sync step should have used the primary to sync, not
// the backup.
writer.Reset();
- ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
- imported = ReadMetadata(fd, 0);
+ imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
ASSERT_GE(new_table->partitions.size(), 1);
ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
}
+
+// Test that writing a sparse image can be read back.
+TEST(liblp, FlashSparseImage) {
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 512);
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // Build the sparse file.
+ SparseBuilder sparse(*exported.get(), 512, {});
+ ASSERT_TRUE(sparse.IsValid());
+ sparse_file_verbose(sparse.file());
+ ASSERT_TRUE(sparse.Build());
+
+ // Write it to the fake disk.
+ ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);
+ int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false);
+ ASSERT_EQ(ret, 0);
+
+ // Verify that we can read both sets of metadata.
+ LpMetadataGeometry geometry;
+ ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry));
+ ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry));
+ ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);
+ ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
+}
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
new file mode 100644
index 0000000..77b0e62
--- /dev/null
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "liblp/partition_opener.h"
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string GetPartitionAbsolutePath(const std::string& path) {
+ if (path[0] == '/') {
+ return path;
+ }
+ return "/dev/block/by-name/" + path;
+}
+
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+ unique_fd fd(open(block_device.c_str(), O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+ return false;
+ }
+ if (!GetDescriptorSize(fd, &device_info->size)) {
+ return false;
+ }
+ if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ return false;
+ }
+
+ int alignment_offset;
+ if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ return false;
+ }
+ int logical_block_size;
+ if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+ return false;
+ }
+
+ device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
+ device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+ device_info->partition_name = android::base::Basename(block_device);
+ return true;
+#else
+ (void)block_device;
+ (void)device_info;
+ LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+ return false;
+#endif
+}
+
+} // namespace
+
+unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
+ std::string path = GetPartitionAbsolutePath(partition_name);
+ return unique_fd{open(path.c_str(), flags)};
+}
+
+bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+ std::string path = GetPartitionAbsolutePath(partition_name);
+ return GetBlockDeviceInfo(path, info);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 190c650..a02e746 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -108,47 +108,44 @@
LERROR << "Metadata max size is not sector-aligned.";
return false;
}
+ return true;
+}
- // Check that the metadata area and logical partition areas don't overlap.
- int64_t end_of_metadata =
- GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
- geometry->metadata_max_size;
- if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
- LERROR << "Logical partition metadata overlaps with logical partition contents.";
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+ if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed";
return false;
}
- return true;
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << " read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+ return false;
+ }
+ return ParseGeometry(buffer.get(), geometry);
+}
+
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+ if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << " backup read " << LP_METADATA_GEOMETRY_SIZE
+ << " bytes failed";
+ return false;
+ }
+ return ParseGeometry(buffer.get(), geometry);
}
// Read and validate geometry information from a block device that holds
// logical partitions. If the information is corrupted, this will attempt
// to read it from a secondary backup location.
bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
- // Read the first 4096 bytes.
- std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
- if (SeekFile64(fd, 0, SEEK_SET) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed";
- return false;
- }
- if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
- PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
- return false;
- }
- if (ParseGeometry(buffer.get(), geometry)) {
+ if (ReadPrimaryGeometry(fd, geometry)) {
return true;
}
-
- // Try the backup copy in the last 4096 bytes.
- if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
- return false;
- }
- if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
- PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
- << " bytes failed";
- return false;
- }
- return ParseGeometry(buffer.get(), geometry);
+ return ReadBackupGeometry(fd, geometry);
}
static bool ValidateTableBounds(const LpMetadataHeader& header,
@@ -188,7 +185,9 @@
return false;
}
if (!ValidateTableBounds(header, header.partitions) ||
- !ValidateTableBounds(header, header.extents)) {
+ !ValidateTableBounds(header, header.extents) ||
+ !ValidateTableBounds(header, header.groups) ||
+ !ValidateTableBounds(header, header.block_devices)) {
LERROR << "Logical partition metadata has invalid table bounds.";
return false;
}
@@ -202,6 +201,10 @@
LERROR << "Logical partition metadata has invalid extent table entry size.";
return false;
}
+ if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
+ LERROR << "Logical partition metadata has invalid group table entry size.";
+ return false;
+ }
return true;
}
@@ -212,7 +215,7 @@
// First read and validate the header.
std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) {
- PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+ PERROR << __PRETTY_FUNCTION__ << " read " << sizeof(metadata->header) << "bytes failed";
return nullptr;
}
if (!ValidateMetadataHeader(metadata->header)) {
@@ -230,7 +233,7 @@
return nullptr;
}
if (!reader->ReadFully(buffer.get(), header.tables_size)) {
- PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+ PERROR << __PRETTY_FUNCTION__ << " read " << header.tables_size << "bytes failed";
return nullptr;
}
@@ -257,6 +260,10 @@
LERROR << "Logical partition has invalid extent list.";
return nullptr;
}
+ if (partition.group_index >= header.groups.num_entries) {
+ LERROR << "Logical partition has invalid group index.";
+ return nullptr;
+ }
metadata->partitions.push_back(partition);
}
@@ -267,8 +274,46 @@
memcpy(&extent, cursor, sizeof(extent));
cursor += header.extents.entry_size;
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
+ extent.target_source >= header.block_devices.num_entries) {
+ LERROR << "Logical partition extent has invalid block device.";
+ return nullptr;
+ }
+
metadata->extents.push_back(extent);
}
+
+ cursor = buffer.get() + header.groups.offset;
+ for (size_t i = 0; i < header.groups.num_entries; i++) {
+ LpMetadataPartitionGroup group = {};
+ memcpy(&group, cursor, sizeof(group));
+ cursor += header.groups.entry_size;
+
+ metadata->groups.push_back(group);
+ }
+
+ cursor = buffer.get() + header.block_devices.offset;
+ for (size_t i = 0; i < header.block_devices.num_entries; i++) {
+ LpMetadataBlockDevice device = {};
+ memcpy(&device, cursor, sizeof(device));
+ cursor += header.block_devices.entry_size;
+
+ metadata->block_devices.push_back(device);
+ }
+
+ const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
+ if (!super_device) {
+ LERROR << "Metadata does not specify a super device.";
+ return nullptr;
+ }
+
+ // Check that the metadata area and logical partition areas don't overlap.
+ uint64_t metadata_region =
+ GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
+ if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Logical partition metadata overlaps with logical partition contents.";
+ return nullptr;
+ }
return metadata;
}
@@ -287,7 +332,7 @@
uint32_t slot_number) {
int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
if (SeekFile64(fd, offset, SEEK_SET) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
return nullptr;
}
return ParseMetadata(geometry, fd);
@@ -296,21 +341,28 @@
std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
uint32_t slot_number) {
int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
- if (SeekFile64(fd, offset, SEEK_END) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
return nullptr;
}
return ParseMetadata(geometry, fd);
}
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+ const std::string& super_partition, uint32_t slot_number) {
+ android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+ return nullptr;
+ }
+
LpMetadataGeometry geometry;
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
return nullptr;
}
if (slot_number >= geometry.metadata_slot_count) {
- LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+ LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
return nullptr;
}
@@ -322,13 +374,8 @@
return ReadBackupMetadata(fd, geometry, slot_number);
}
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
- android::base::unique_fd fd(open(block_device, O_RDONLY));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
- return nullptr;
- }
- return ReadMetadata(fd, slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
+ return ReadMetadata(PartitionOpener(), super_partition, slot_number);
}
static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
@@ -345,5 +392,13 @@
return NameFromFixedArray(partition.name, sizeof(partition.name));
}
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
+ return NameFromFixedArray(group.name, sizeof(group.name));
+}
+
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
+ return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 9f6ca6e..d5d5188 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -31,11 +31,12 @@
bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
// Helper functions for manually reading geometry and metadata.
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
size_t size);
bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
// These functions assume a valid geometry and slot number.
std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
diff --git a/fs_mgr/liblp/test_partition_opener.cpp b/fs_mgr/liblp/test_partition_opener.cpp
new file mode 100644
index 0000000..c796f6c
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "test_partition_opener.h"
+
+#include <errno.h>
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+TestPartitionOpener::TestPartitionOpener(
+ const std::map<std::string, int>& partition_map,
+ const std::map<std::string, BlockDeviceInfo>& partition_info)
+ : partition_map_(partition_map), partition_info_(partition_info) {}
+
+unique_fd TestPartitionOpener::Open(const std::string& partition_name, int flags) const {
+ auto iter = partition_map_.find(partition_name);
+ if (iter == partition_map_.end()) {
+ errno = ENOENT;
+ return {};
+ }
+ return unique_fd{dup(iter->second)};
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+ auto iter = partition_info_.find(partition_name);
+ if (iter == partition_info_.end()) {
+ errno = ENOENT;
+ return false;
+ }
+ *info = iter->second;
+ return true;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/test_partition_opener.h b/fs_mgr/liblp/test_partition_opener.h
new file mode 100644
index 0000000..b90fee7
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <liblp/partition_opener.h>
+
+namespace android {
+namespace fs_mgr {
+
+class TestPartitionOpener : public PartitionOpener {
+ public:
+ explicit TestPartitionOpener(const std::map<std::string, int>& partition_map,
+ const std::map<std::string, BlockDeviceInfo>& partition_info = {});
+
+ android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+ bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+
+ private:
+ std::map<std::string, int> partition_map_;
+ std::map<std::string, BlockDeviceInfo> partition_info_;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index a590037..742ad82 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -16,14 +16,12 @@
#include <fcntl.h>
#include <stdint.h>
-#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
-#include <uuid/uuid.h>
#include "utility.h"
@@ -57,22 +55,40 @@
return lseek(fd, offset, whence);
}
+int64_t GetPrimaryGeometryOffset() {
+ return LP_PARTITION_RESERVED_BYTES;
+}
+
+int64_t GetBackupGeometryOffset() {
+ return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
+}
+
int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
-
- int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
- CHECK(offset + geometry.metadata_max_size <=
- int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+ int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
+ geometry.metadata_max_size * slot_number;
return offset;
}
int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
- int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+ int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
return start + int64_t(geometry.metadata_max_size * slot_number);
}
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
+ return LP_PARTITION_RESERVED_BYTES +
+ (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
+}
+
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
+ if (metadata.block_devices.empty()) {
+ return nullptr;
+ }
+ return &metadata.block_devices[0];
+}
+
void SHA256(const void* data, size_t length, uint8_t out[32]) {
SHA256_CTX c;
SHA256_Init(&c);
@@ -80,15 +96,6 @@
SHA256_Final(out, &c);
}
-std::string GetPartitionGuid(const LpMetadataPartition& partition) {
- // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
- // macro to assist with buffer sizing.
- static const size_t kGuidLen = 36;
- char buffer[kGuidLen + 1];
- uuid_unparse_upper(partition.guid, buffer);
- return buffer;
-}
-
uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
if (suffix.empty()) {
return 0;
@@ -101,5 +108,13 @@
return suffix[1] - 'a';
}
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
+ uint64_t size = 0;
+ for (const auto& block_device : metadata.block_devices) {
+ size += block_device.size;
+ }
+ return size;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 6ef5124..65e643b 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -23,7 +23,7 @@
#include <android-base/logging.h>
-#include "liblp/metadata_format.h"
+#include "liblp/liblp.h"
#define LP_TAG "[liblp]"
#define LWARN LOG(WARNING) << LP_TAG
@@ -38,6 +38,10 @@
// error. After calling this, the position of |fd| may have changed.
bool GetDescriptorSize(int fd, uint64_t* size);
+// Return the offset of the primary or backup geometry.
+int64_t GetPrimaryGeometryOffset();
+int64_t GetBackupGeometryOffset();
+
// Return the offset of a primary metadata slot, relative to the start of the
// device.
int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
@@ -46,6 +50,10 @@
// device.
int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+// Return the total space at the start of the super partition that must be set
+// aside from headers/metadata and backups.
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
+
// Cross-platform helper for lseek64().
int64_t SeekFile64(int fd, int64_t offset, int whence);
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 7bf42ae..bdf6dfd 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -36,21 +36,18 @@
{0},
16384,
4,
- 10000,
- 80000,
- 0,
- 0,
- 1024 * 1024,
4096};
- EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
- EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
- EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
- EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+ static const uint64_t start = LP_PARTITION_RESERVED_BYTES;
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), start + 8192 + 16384);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), start + 8192 + 16384 * 2);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), start + 8192 + 16384 * 3);
- EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
- EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
- EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
- EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+ static const uint64_t backup_start = start + 8192 + 16384 * 4;
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), backup_start + 16384 * 3);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), backup_start + 16384 * 2);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), backup_start + 16384 * 1);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
}
TEST(liblp, AlignTo) {
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 9dd2745..c740bd4 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -43,8 +43,7 @@
static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
return g1.metadata_max_size == g2.metadata_max_size &&
g1.metadata_slot_count == g2.metadata_slot_count &&
- g1.first_logical_sector == g2.first_logical_sector &&
- g1.last_logical_sector == g2.last_logical_sector;
+ g1.logical_block_size == g2.logical_block_size;
}
std::string SerializeMetadata(const LpMetadata& input) {
@@ -56,14 +55,20 @@
metadata.partitions.size() * sizeof(LpMetadataPartition));
std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
metadata.extents.size() * sizeof(LpMetadataExtent));
+ std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
+ metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
+ std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
+ metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
// Compute positions of tables.
header.partitions.offset = 0;
header.extents.offset = header.partitions.offset + partitions.size();
- header.tables_size = header.extents.offset + extents.size();
+ header.groups.offset = header.extents.offset + extents.size();
+ header.block_devices.offset = header.groups.offset + groups.size();
+ header.tables_size = header.block_devices.offset + block_devices.size();
// Compute payload checksum.
- std::string tables = partitions + extents;
+ std::string tables = partitions + extents + groups + block_devices;
SHA256(tables.data(), tables.size(), header.tables_checksum);
// Compute header checksum.
@@ -83,15 +88,11 @@
return false;
}
- *blob = SerializeMetadata(metadata);
-
const LpMetadataHeader& header = metadata.header;
const LpMetadataGeometry& geometry = metadata.geometry;
- // Validate the usable sector range.
- if (geometry.first_logical_sector > geometry.last_logical_sector) {
- LERROR << "Logical partition metadata has invalid sector range.";
- return false;
- }
+
+ *blob = SerializeMetadata(metadata);
+
// Make sure we're writing within the space reserved.
if (blob->size() > geometry.metadata_max_size) {
LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
@@ -103,18 +104,22 @@
// metadata.
uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
- if (reserved_size > blockdevice_size ||
- reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+ uint64_t total_reserved = reserved_size * 2;
+
+ const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+ if (!super_device) {
+ LERROR << "Logical partition metadata does not have a super block device.";
+ return false;
+ }
+
+ if (total_reserved > blockdevice_size ||
+ total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Not enough space to store all logical partition metadata slots.";
return false;
}
- if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
- LERROR << "Not enough space to backup all logical partition metadata slots.";
- return false;
- }
- if (blockdevice_size != metadata.geometry.block_device_size) {
+ if (blockdevice_size != super_device->size) {
LERROR << "Block device size " << blockdevice_size
- << " does not match metadata requested size " << metadata.geometry.block_device_size;
+ << " does not match metadata requested size " << super_device->size;
return false;
}
@@ -127,11 +132,12 @@
}
// Make sure all linear extents have a valid range.
+ uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
for (const auto& extent : metadata.extents) {
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
uint64_t physical_sector = extent.target_data;
- if (physical_sector < geometry.first_logical_sector ||
- physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+ if (physical_sector < super_device->first_logical_sector ||
+ physical_sector + extent.num_sectors > last_sector) {
LERROR << "Extent table entry is out of bounds.";
return false;
}
@@ -140,54 +146,69 @@
return true;
}
-static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+// Check that the given region is within metadata bounds.
+static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
+ const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+ if (!super_device) {
+ LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
+ return false;
+ }
+ if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
+ << " overlaps with logical partition contents";
+ return false;
+ }
+ return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
- int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
+ if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
+ return false;
+ }
if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
return false;
}
if (!writer(fd, blob)) {
- PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
return false;
}
return true;
}
-static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
- int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
- int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
- if (abs_offset == (int64_t)-1) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+ int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
+ if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
return false;
}
- if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
- PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
- << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+ if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
return false;
}
if (!writer(fd, blob)) {
- PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
return false;
}
return true;
}
-static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
// Make sure we're writing to a valid metadata slot.
- if (slot_number >= geometry.metadata_slot_count) {
+ if (slot_number >= metadata.geometry.metadata_slot_count) {
LERROR << "Invalid logical partition metadata slot number.";
return false;
}
- if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+ if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
return false;
}
- if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
+ if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
return false;
}
return true;
@@ -197,7 +218,14 @@
return android::base::WriteFully(fd, blob.data(), blob.size());
}
-bool FlashPartitionTable(int fd, const LpMetadata& metadata) {
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata) {
+ android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+ return false;
+ }
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
@@ -206,39 +234,63 @@
return false;
}
- // Write geometry to the first and last 4096 bytes of the device.
- std::string blob = SerializeGeometry(metadata.geometry);
+ // Write zeroes to the first block.
+ std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset 0";
+ return false;
+ }
+ if (!android::base::WriteFully(fd, zeroes.data(), zeroes.size())) {
+ PERROR << __PRETTY_FUNCTION__ << " write " << zeroes.size() << " bytes failed";
+ return false;
+ }
+
+ LWARN << "Flashing new logical partition geometry to " << super_partition;
+
+ // Write geometry to the primary and backup locations.
+ std::string blob = SerializeGeometry(metadata.geometry);
+ if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: primary geometry";
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
- PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
return false;
}
- if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+ if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed: backup geometry";
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
- PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
return false;
}
bool ok = true;
for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
- ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter);
+ ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
}
return ok;
}
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {
+ return FlashPartitionTable(PartitionOpener(), super_partition, metadata);
+}
+
static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
return !memcmp(a.header.header_checksum, b.header.header_checksum,
sizeof(a.header.header_checksum));
}
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number,
const std::function<bool(int, const std::string&)>& writer) {
+ android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+ return false;
+ }
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
@@ -279,7 +331,7 @@
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
- if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+ if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {
LERROR << "Error writing primary metadata to repair corrupted backup";
return false;
}
@@ -291,46 +343,31 @@
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
- if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+ if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
LERROR << "Error writing primary metadata to repair corrupted backup";
return false;
}
}
// Both copies should now be in sync, so we can continue the update.
- return WriteMetadata(fd, geometry, slot_number, blob, writer);
-}
+ if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {
+ return false;
+ }
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {
- android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
- return false;
- }
- if (!FlashPartitionTable(fd, metadata)) {
- return false;
- }
- LWARN << "Flashed new logical partition geometry to " << block_device;
- return true;
-}
-
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
- uint32_t slot_number) {
- android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
- if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
- return false;
- }
- if (!UpdatePartitionTable(fd, metadata, slot_number)) {
- return false;
- }
LINFO << "Updated logical partition table at slot " << slot_number << " on device "
- << block_device;
+ << super_partition;
return true;
}
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
- return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number) {
+ return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
+}
+
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+ uint32_t slot_number) {
+ PartitionOpener opener;
+ return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
}
} // namespace fs_mgr
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
index ab18d45..6f1da0f 100644
--- a/fs_mgr/liblp/writer.h
+++ b/fs_mgr/liblp/writer.h
@@ -30,10 +30,8 @@
// These variants are for testing only. The path-based functions should be used
// for actual operation, so that open() is called with the correct flags.
-bool FlashPartitionTable(int fd, const LpMetadata& metadata);
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
-
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+ const LpMetadata& metadata, uint32_t slot_number,
const std::function<bool(int, const std::string&)>& writer);
} // namespace fs_mgr
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
new file mode 100755
index 0000000..b6a8eef
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -0,0 +1,305 @@
+#! /bin/bash
+
+USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+
+adb remount tests (overlayfs focus)
+
+Conditions:
+ - Must be a userdebug build.
+ - Must be in adb mode.
+ - Kernel must have overlayfs enabled and patched to support override_creds.
+ - Must have either squashfs, ext4-dedupe or right-sized partitions.
+ - Minimum expectation system and vender are overlayfs covered partitions.
+"
+
+if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
+ echo "${USAGE}" >&2
+ exit 0
+fi
+
+# Helper Variables
+
+SPACE=" "
+# A _real_ embedded tab character
+TAB="`echo | tr '\n' '\t'`"
+# A _real_ embedded escape character
+ESCAPE="`echo | tr '\n' '\033'`"
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+ORANGE="${ESCAPE}[38;5;255:165:0m"
+NORMAL="${ESCAPE}[0m"
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+ fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+ adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: adb_sh <commands>
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+ adb shell "${@}"
+}
+
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+ adb_sh getprop ${1} 2>&1 </dev/null
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+ if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+ false
+ fi
+}
+
+[ "USAGE: adb_su <commands>
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+ adb_sh su root "${@}"
+}
+
+[ "USAGE: adb_cat <file> >stdout
+
+Returns: content of file to stdout with carriage returns skipped,
+ true of the file exists" ]
+adb_cat() {
+ OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
+ retval=${?}
+ echo "${OUTPUT}" | tr -d '\r'
+ return ${retval}
+}
+
+[ "USAGE: adb_reboot
+
+Returns: true if the reboot command succeeded" ]
+adb_reboot() {
+ adb reboot remount-test
+}
+
+[ "USAGE: adb_wait [timeout]
+
+Returns: waits until the device has returned or the optional timeout" ]
+adb_wait() {
+ if [ -n "${1}" ]; then
+ timeout --preserve-status --signal=KILL ${1} adb wait-for-device
+ else
+ adb wait-for-device
+ fi
+}
+
+[ "USAGE: adb_root
+
+Returns: true if device in root state" ]
+adb_root() {
+ adb root >/dev/null </dev/null 2>&1 &&
+ sleep 1 &&
+ adb_wait &&
+ sleep 1
+}
+
+die() {
+ echo "${RED}[ FAILED ]${NORMAL} ${@}" >&2
+ exit 1
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+ lval="${1}"
+ rval="${2}"
+ shift 2
+ if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+ if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
+*}" ]; then
+ echo "ERROR: expected \"${lval}\"" >&2
+ echo " got \"${rval}\"" |
+ sed ': again
+ N
+ s/\(\n\)\([^ ]\)/\1 \2/
+ t again' >&2
+ if [ -n "${*}" ] ; then
+ echo " ${*}" >&2
+ fi
+ else
+ echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
+ fi
+ return 1
+ fi
+ if [ -n "${*}" ] ; then
+ if [ X"${lval}" != X"${rval}" ]; then
+ if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
+ echo "INFO: ok \"${lval}\"" >&2
+ echo " = \"${rval}\"" |
+ sed ': again
+ N
+ s/\(\n\)\([^ ]\)/\1 \2/
+ t again' >&2
+ if [ -n "${*}" ] ; then
+ echo " ${*}" >&2
+ fi
+ else
+ echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+ fi
+ else
+ echo "INFO: ok \"${lval}\" ${*}" >&2
+ fi
+ fi
+ return 0
+}
+
+[ "USAGE: check_eq <lval> <rval> [message]
+
+Exits if (regex) lval mismatches rval" ]
+check_eq() {
+ left="${1}"
+ right="${2}"
+ shift 2
+ EXPECT_EQ "${left}" "${right}" ||
+ die "${@}"
+}
+
+[ "USAGE: skip_administrative_mounts
+
+Filters out all administrative (eg: sysfs) mounts" ]
+skip_administrative_mounts() {
+ grep -v -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\|/data/media\) " -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\|data\) "
+}
+
+if [ X"-s" = X"${1}" -a -n "${2}" ]; then
+ export ANDROID_SERIAL="${2}"
+ shift 2
+fi
+
+inFastboot && die "device in fastboot mode"
+if ! inAdb; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode ... waiting 2 minutes"
+ adb_wait 2m
+fi
+inAdb || die "device not in adb mode"
+isDebuggable || die "device not a debug build"
+
+# Do something
+adb_wait || die "wait for device failed"
+adb_sh ls -d /sys/module/overlay </dev/null || die "overlay module not present"
+adb_su ls /sys/module/overlay/parameters/override_creds </dev/null ||
+ die "overlay module can not be used on ANDROID"
+adb_root &&
+ adb_wait &&
+ D=`adb disable-verity 2>&1` ||
+ die "setup for overlay"
+echo "${D}"
+if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+ echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
+fi
+if adb_sh ls -d /data/overlay </dev/null >/dev/null 2>&1; then
+ echo "/data/overlay setup, clearing out" >&2
+ adb_sh rm -rf /data/overlay </dev/null ||
+ die "/data/overlay removal"
+fi
+adb_sh ls -d /cache/overlay </dev/null >/dev/null 2>&1 ||
+ adb_sh ls -d /mnt/scratch/overlay </dev/null >/dev/null 2>&1 ||
+ die "overlay directory setup"
+adb_reboot &&
+ adb_wait &&
+ adb_sh df -k </dev/null | head -1 &&
+ adb_sh df -k </dev/null | grep "^overlay " ||
+ die "overlay takeover failed"
+adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
+
+adb_root &&
+ adb_wait &&
+ adb remount &&
+ adb_sh df -k </dev/null | head -1 &&
+ adb_sh df -k </dev/null | grep "^overlay " &&
+ adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ die "overlay takeover after remount"
+!(adb_sh grep "^overlay " /proc/mounts </dev/null | grep " overlay ro,") &&
+ !(adb_sh grep " rw," /proc/mounts </dev/null |
+ skip_administrative_mounts) ||
+ die "remount overlayfs missed a spot"
+
+adb_su "sed -n '1,/overlay \\/system/p' /proc/mounts" </dev/null |
+ skip_administrative_mounts |
+ grep -v ' \(squashfs\|ext4\|f2fs\) ' &&
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
+ echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
+
+# Check something
+A="Hello World! $(date)"
+echo "${A}" | adb_sh "cat - > /system/hello"
+echo "${A}" | adb_sh "cat - > /vendor/hello"
+B="`adb_cat /system/hello`" ||
+ die "sytem hello"
+check_eq "${A}" "${B}" system before reboot
+B="`adb_cat /vendor/hello`" ||
+ die "vendor hello"
+check_eq "${A}" "${B}" vendor before reboot
+adb_reboot &&
+ adb_wait &&
+ B="`adb_cat /system/hello`" ||
+ die "re-read system hello after reboot"
+check_eq "${A}" "${B}" system after reboot
+# Only root can read vendor if sepolicy permissions are as expected
+B="`adb_cat /vendor/hello`" &&
+ die "re-read vendor hello after reboot w/o root"
+check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
+adb_root &&
+ adb_wait &&
+ B="`adb_cat /vendor/hello`" ||
+ die "re-read vendor hello after reboot"
+check_eq "${A}" "${B}" vendor after reboot
+
+adb reboot-fastboot &&
+ fastboot flash vendor &&
+ fastboot reboot ||
+ die "fastbootd flash vendor"
+adb_wait &&
+ adb_root &&
+ adb_wait &&
+ adb_sh df -k </dev/null | head -1 &&
+ adb_sh df -k </dev/null | grep "^overlay " &&
+ adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ die "overlay system takeover after flash vendor"
+adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
+ die "overlay minus vendor takeover after flash vendor"
+B="`adb_cat /system/hello`" ||
+ die "re-read system hello after flash vendor"
+check_eq "${A}" "${B}" system after flash vendor
+adb_root &&
+ adb_wait ||
+ die "adb root"
+B="`adb_cat /vendor/hello`" &&
+ die "re-read vendor hello after flash vendor"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+
+adb remount &&
+ ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
+ adb_sh rm /system/hello </dev/null ||
+ die "cleanup hello"
+B="`adb_cat /system/hello`" &&
+ die "re-read system hello after rm"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
+B="`adb_cat /vendor/hello`" &&
+ die "re-read vendor hello after rm"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
+
+echo "${GREEN}[ PASSED ]${NORMAL} adb remount" >&2
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 8b1c55a..db01c1e 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -139,7 +139,7 @@
auto fstab = fs_mgr_read_fstab("/proc/mounts");
ASSERT_NE(fstab, nullptr);
- std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "r"),
+ std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "re"),
endmntent);
ASSERT_NE(mounts, nullptr);
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 879ba21..f78093b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -42,6 +42,7 @@
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 DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
@@ -108,6 +109,13 @@
std::string block_device = NextArg();
return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
block_device);
+ } else if (target_type == "bow") {
+ if (!HasArgs(1)) {
+ std::cerr << "Expected \"bow\" <block_device>" << std::endl;
+ return nullptr;
+ }
+ std::string block_device = NextArg();
+ return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
index 1c339f4..43d5708 100644
--- a/gatekeeperd/IGateKeeperService.cpp
+++ b/gatekeeperd/IGateKeeperService.cpp
@@ -70,7 +70,7 @@
} else {
reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
}
- return NO_ERROR;
+ return OK;
}
case VERIFY: {
CHECK_INTERFACE(IGateKeeperService, data, reply);
@@ -102,7 +102,7 @@
} else {
reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
}
- return NO_ERROR;
+ return OK;
}
case VERIFY_CHALLENGE: {
CHECK_INTERFACE(IGateKeeperService, data, reply);
@@ -141,7 +141,7 @@
} else {
reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
}
- return NO_ERROR;
+ return OK;
}
case GET_SECURE_USER_ID: {
CHECK_INTERFACE(IGateKeeperService, data, reply);
@@ -149,20 +149,20 @@
uint64_t sid = getSecureUserId(uid);
reply->writeNoException();
reply->writeInt64(sid);
- return NO_ERROR;
+ return OK;
}
case CLEAR_SECURE_USER_ID: {
CHECK_INTERFACE(IGateKeeperService, data, reply);
uint32_t uid = data.readInt32();
clearSecureUserId(uid);
reply->writeNoException();
- return NO_ERROR;
+ return OK;
}
case REPORT_DEVICE_SETUP_COMPLETE: {
CHECK_INTERFACE(IGateKeeperService, data, reply);
reportDeviceSetupComplete();
reply->writeNoException();
- return NO_ERROR;
+ return OK;
}
default:
return BBinder::onTransact(code, data, reply, flags);
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 5f3ce36..f2818f3 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -386,7 +386,7 @@
write(fd, result, strlen(result) + 1);
}
- return NO_ERROR;
+ return OK;
}
private:
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 80c5afe..2a5667c 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -357,7 +357,7 @@
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryChargeCounterPath);
- ret = NO_ERROR;
+ ret = OK;
} else {
ret = NAME_NOT_FOUND;
}
@@ -367,7 +367,7 @@
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentNowPath);
- ret = NO_ERROR;
+ ret = OK;
} else {
ret = NAME_NOT_FOUND;
}
@@ -377,7 +377,7 @@
if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
- ret = NO_ERROR;
+ ret = OK;
} else {
ret = NAME_NOT_FOUND;
}
@@ -387,7 +387,7 @@
if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCapacityPath);
- ret = NO_ERROR;
+ ret = OK;
} else {
ret = NAME_NOT_FOUND;
}
@@ -403,7 +403,7 @@
case BATTERY_PROP_BATTERY_STATUS:
val->valueInt64 = getChargeStatus();
- ret = NO_ERROR;
+ ret = OK;
break;
default:
diff --git a/healthd/animation.h b/healthd/animation.h
index 562b689..f59fb38 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -20,7 +20,7 @@
#include <inttypes.h>
#include <string>
-struct GRSurface;
+class GRSurface;
struct GRFont;
namespace android {
diff --git a/init/Android.bp b/init/Android.bp
index c793971..ff3b61f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -74,6 +74,7 @@
"libdl",
"libext4_utils",
"libfs_mgr",
+ "libfscrypt",
"libhidl-gen-utils",
"libkeyutils",
"liblog",
diff --git a/init/Android.mk b/init/Android.mk
index 5554995..c85727c 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -51,7 +51,8 @@
uevent_listener.cpp \
util.cpp \
-LOCAL_MODULE := init
+LOCAL_MODULE := init_first_stage
+LOCAL_MODULE_STEM := init
LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -72,6 +73,7 @@
libsquashfs_utils \
liblogwrap \
libext4_utils \
+ libfscrypt \
libseccomp_policy \
libcrypto_utils \
libsparse \
@@ -87,8 +89,32 @@
libselinux \
libcap \
-LOCAL_REQUIRED_MODULES := \
- init_second_stage \
-
LOCAL_SANITIZE := signed-integer-overflow
include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init_system
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+LOCAL_REQUIRED_MODULES := \
+ init_first_stage \
+ init_second_stage \
+
+else
+LOCAL_REQUIRED_MODULES := \
+ init_second_stage \
+
+endif
+include $(BUILD_PHONY_PACKAGE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init_vendor
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+LOCAL_REQUIRED_MODULES := \
+ init_first_stage \
+
+endif
+include $(BUILD_PHONY_PACKAGE)
+
+
diff --git a/init/README.md b/init/README.md
index b45da21..6c51b37 100644
--- a/init/README.md
+++ b/init/README.md
@@ -189,7 +189,7 @@
`critical`
> This is a device-critical service. If it exits more than four times in
- four minutes, the device will reboot into recovery mode.
+ four minutes, the device will reboot into bootloader.
`disabled`
> This service will not automatically start with its class.
@@ -213,7 +213,8 @@
`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.
+ 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>`
@@ -225,6 +226,15 @@
keycodes are pressed at once, the service will start. This is typically used to start the
bugreport service.
+> This option may take a property instead of a list of keycodes. In this case, only one option is
+ provided: the property name in the typical property expansion format. The property must contain
+ a comma separated list of keycode values or the text 'none' to indicate that
+ this service does not respond to keycodes.
+
+> For example, `keycodes ${some.property.name:-none}` where some.property.name expands
+ to "123,124,125". Since keycodes are handled very early in init,
+ only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
+
`memcg.limit_in_bytes <value>`
> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
which must be equal or greater than 0.
@@ -262,6 +272,13 @@
> Scheduling priority of the service process. This value has to be in range
-20 to 19. Default priority is 0. Priority is set via setpriority().
+`restart_period <seconds>`
+> If a non-oneshot service exits, it will be restarted at its start time plus
+ this period. It defaults to 5s to rate limit crashing services.
+ This can be increased for services that are meant to run periodically. For
+ example, it may be set to 3600 to indicate that the service should run every hour
+ or 86400 to indicate that the service should run every day.
+
`rlimit <resource> <cur> <max>`
> This applies the given rlimit to the service. rlimits are inherited by child
processes, so this effectively applies the given rlimit to the process tree
@@ -298,6 +315,12 @@
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
+`timeout_period <seconds>`
+> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
+ here, so oneshot services do not automatically restart, however all other services will.
+ This is particularly useful for creating a periodic service combined with the restart_period
+ option described above.
+
`user <username>`
> Change to 'username' before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)
diff --git a/init/action.cpp b/init/action.cpp
index 11335ca..94ccef2 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -44,9 +44,12 @@
return function(builtin_arguments);
}
-Command::Command(BuiltinFunction f, bool execute_in_subcontext,
- const std::vector<std::string>& args, int line)
- : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
+Command::Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
+ int line)
+ : func_(std::move(f)),
+ execute_in_subcontext_(execute_in_subcontext),
+ args_(std::move(args)),
+ line_(line) {}
Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
if (subcontext) {
@@ -80,7 +83,7 @@
const KeywordFunctionMap* Action::function_map_ = nullptr;
-Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
+Result<Success> Action::AddCommand(std::vector<std::string>&& args, int line) {
if (!function_map_) {
return Error() << "no function map available";
}
@@ -88,12 +91,12 @@
auto function = function_map_->FindFunction(args);
if (!function) return Error() << function.error();
- commands_.emplace_back(function->second, function->first, args, line);
+ commands_.emplace_back(function->second, function->first, std::move(args), line);
return Success();
}
-void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
- commands_.emplace_back(f, false, args, line);
+void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
+ commands_.emplace_back(f, false, std::move(args), line);
}
std::size_t Action::NumCommands() const {
diff --git a/init/action.h b/init/action.h
index 4f063cc..967c682 100644
--- a/init/action.h
+++ b/init/action.h
@@ -36,7 +36,7 @@
class Command {
public:
- Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+ Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
int line);
Result<Success> InvokeFunc(Subcontext* subcontext) const;
@@ -61,8 +61,8 @@
const std::string& event_trigger,
const std::map<std::string, std::string>& property_triggers);
- Result<Success> AddCommand(const std::vector<std::string>& args, int line);
- void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
+ Result<Success> 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;
void ExecuteAllCommands() const;
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 22977bb..9de4085 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -47,9 +47,7 @@
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
- std::vector<std::string> name_vector{name};
-
- action->AddCommand(func, name_vector, 0);
+ action->AddCommand(func, {name}, 0);
event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 1481162..4f8bd16 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -19,7 +19,11 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include "stable_properties.h"
+#if defined(__ANDROID__)
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
using android::base::GetBoolProperty;
using android::base::StartsWith;
@@ -36,15 +40,19 @@
return true;
}
- if (kExportedActionableProperties.count(prop_name) == 1) {
- return true;
- }
+ static constexpr const char* kPartnerPrefixes[] = {
+ "init.svc.vendor.", "ro.vendor.", "persist.vendor.",
+ "vendor.", "init.svc.odm.", "ro.odm.",
+ "persist.odm.", "odm.", "ro.boot.",
+ };
+
for (const auto& prefix : kPartnerPrefixes) {
if (android::base::StartsWith(prop_name, prefix)) {
return true;
}
}
- return false;
+
+ return CanReadProperty(subcontext->context(), prop_name);
}
Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 17d34e1..5d62c0b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -50,9 +50,9 @@
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_crypt.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <fs_mgr.h>
+#include <fscrypt/fscrypt.h>
+#include <fscrypt/fscrypt_init_extensions.h>
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
@@ -307,8 +307,8 @@
}
}
- if (e4crypt_is_native()) {
- if (e4crypt_set_directory_policy(args[1].c_str())) {
+ if (fscrypt_is_native()) {
+ if (fscrypt_set_directory_policy(args[1].c_str())) {
return reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
}
@@ -517,8 +517,8 @@
return reboot_into_recovery(options);
/* If reboot worked, there is no return. */
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -528,8 +528,8 @@
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
return Success();
} else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -539,8 +539,8 @@
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
return Success();
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -1016,9 +1016,13 @@
}
service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
- if (e4crypt_is_native()) {
+ if (fscrypt_is_native()) {
LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
- reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+ if (auto result = reboot_into_recovery(
+ {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+ !result) {
+ LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+ }
} else {
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
}
@@ -1034,7 +1038,7 @@
static Result<Success> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return Success();
- auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+ auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
return ErrnoError() << "Failed to create " << unencrypted_dir;
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 71a8e0d..eb86eb0 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -68,6 +68,7 @@
bool CreateLogicalPartitions();
bool MountPartition(fstab_rec* fstab_rec);
bool MountPartitions();
+ bool IsDmLinearEnabled();
bool GetBackingDmLinearDevices();
virtual ListenerAction UeventCallback(const Uevent& uevent);
@@ -82,6 +83,7 @@
std::string lp_metadata_partition_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
+ std::string super_partition_name_;
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
@@ -134,22 +136,6 @@
return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
}
-static inline bool IsDmLinearEnabled() {
- static bool checked = false;
- static bool enabled = false;
- if (checked) {
- return enabled;
- }
- import_kernel_cmdline(false,
- [](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.logical_partitions" && value == "1") {
- enabled = true;
- }
- });
- checked = true;
- return enabled;
-}
-
// Class Definitions
// -----------------
FirstStageMount::FirstStageMount()
@@ -168,6 +154,8 @@
device_handler_ = std::make_unique<DeviceHandler>(
std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
std::move(boot_devices), false);
+
+ super_partition_name_ = fs_mgr_get_super_partition_name();
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -198,13 +186,20 @@
return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
}
+bool FirstStageMount::IsDmLinearEnabled() {
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (fs_mgr_is_logical(fstab_rec)) return true;
+ }
+ return false;
+}
+
bool FirstStageMount::GetBackingDmLinearDevices() {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return true;
}
- required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+ required_devices_partition_names_.emplace(super_partition_name_);
return true;
}
@@ -270,7 +265,7 @@
if (lp_metadata_partition_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
- << LP_METADATA_PARTITION_NAME;
+ << super_partition_name_;
return false;
}
return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
@@ -283,7 +278,7 @@
auto iter = required_devices_partition_names_.find(name);
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
- if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+ if (IsDmLinearEnabled() && name == super_partition_name_) {
std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
lp_metadata_partition_ = links[0];
}
@@ -396,6 +391,12 @@
}
}
+ // heads up for instantiating required device(s) for overlayfs logic
+ const auto devices = fs_mgr_overlayfs_required_devices(device_tree_fstab_.get());
+ for (auto const& device : devices) {
+ InitMappedDevice(device);
+ }
+
fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
return true;
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 8866bdc..b85e54a 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -30,6 +30,9 @@
std::string default_console = "/dev/console";
// property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+ return true;
+}
uint32_t SetProperty(const std::string& key, const std::string& value) {
android::base::SetProperty(key, value);
return 0;
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 0af11f6..63ceead 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -39,6 +39,7 @@
extern std::string default_console;
// property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name);
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error);
diff --git a/init/init.cpp b/init/init.cpp
index 47cfe32..b12ba8c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -61,6 +61,10 @@
#include "ueventd.h"
#include "util.h"
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -74,6 +78,25 @@
namespace android {
namespace init {
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+ return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+ LOG(ERROR) << "Main stage (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+ LOG(ERROR) << "Main stage: " << str;
+}
+#endif
+
static int property_triggers_enabled = 0;
static char qemu[32];
@@ -187,23 +210,34 @@
}
}
-static std::optional<boot_clock::time_point> RestartProcesses() {
- std::optional<boot_clock::time_point> next_process_restart_time;
+static std::optional<boot_clock::time_point> HandleProcessActions() {
+ std::optional<boot_clock::time_point> next_process_action_time;
for (const auto& s : ServiceList::GetInstance()) {
+ if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
+ auto timeout_time = s->time_started() + *s->timeout_period();
+ if (boot_clock::now() > timeout_time) {
+ s->Timeout();
+ } else {
+ if (!next_process_action_time || timeout_time < *next_process_action_time) {
+ next_process_action_time = timeout_time;
+ }
+ }
+ }
+
if (!(s->flags() & SVC_RESTARTING)) continue;
- auto restart_time = s->time_started() + 5s;
+ auto restart_time = s->time_started() + s->restart_period();
if (boot_clock::now() > restart_time) {
if (auto result = s->Start(); !result) {
LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
}
} else {
- if (!next_process_restart_time || restart_time < *next_process_restart_time) {
- next_process_restart_time = restart_time;
+ if (!next_process_action_time || restart_time < *next_process_action_time) {
+ next_process_action_time = restart_time;
}
}
}
- return next_process_restart_time;
+ return next_process_action_time;
}
static Result<Success> DoControlStart(Service* service) {
@@ -570,23 +604,6 @@
RebootSystem(ANDROID_RB_RESTART2, "bootloader");
}
-static void InitKernelLogging(char* argv[]) {
- // Make stdin/stdout/stderr all point to /dev/null.
- int fd = open("/sys/fs/selinux/null", O_RDWR);
- if (fd == -1) {
- int saved_errno = errno;
- android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
- errno = saved_errno;
- PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
- }
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) close(fd);
-
- android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
-}
-
static void GlobalSeccomp() {
import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
bool in_qemu) {
@@ -625,6 +642,10 @@
}
int main(int argc, char** argv) {
+#if __has_feature(address_sanitizer)
+ __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
@@ -643,7 +664,8 @@
SetupSelinux(argv);
}
- InitKernelLogging(argv);
+ // We need to set up stdin/stdout/stderr again now that we're running in init's context.
+ InitKernelLogging(argv, InitAborter);
LOG(INFO) << "init second stage started!";
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
@@ -770,12 +792,12 @@
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
- auto next_process_restart_time = RestartProcesses();
+ auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
- if (next_process_restart_time) {
+ if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
- *next_process_restart_time - boot_clock::now());
+ *next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 40706a1..d81ca5c 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -102,9 +102,11 @@
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
- android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
- });
+ // We need to set up stdin/stdout/stderr for child processes forked from first
+ // stage init as part of the mount process. This closes /dev/console if the
+ // kernel had previously opened it.
+ auto reboot_bootloader = [](const char*) { RebootSystem(ANDROID_RB_RESTART2, "bootloader"); };
+ InitKernelLogging(argv, reboot_bootloader);
if (!errors.empty()) {
for (const auto& [error_string, error_errno] : errors) {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index b8c1cfd..f5ac44f 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -41,7 +41,7 @@
Keychords::~Keychords() noexcept {
if (inotify_fd_ >= 0) {
- epoll_->UnregisterHandler(inotify_fd_);
+ epoll_->UnregisterHandler(inotify_fd_).IgnoreError();
::close(inotify_fd_);
}
while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
@@ -186,7 +186,11 @@
current_ |= mask & available & set;
LambdaCheck();
}
- epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
+ if (auto result = epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
+ !result) {
+ LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
+ return false;
+ }
return true;
}
@@ -208,7 +212,7 @@
auto it = registration_.find(device);
if (it == registration_.end()) return;
auto fd = (*it).second;
- epoll_->UnregisterHandler(fd);
+ epoll_->UnregisterHandler(fd).IgnoreError();
registration_.erase(it);
::close(fd);
}
@@ -266,7 +270,11 @@
}
if (inotify_fd_ >= 0) {
- epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+ if (auto result =
+ epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+ !result) {
+ LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
+ }
}
}
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index c8c47a8..e5a6fd3 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -47,9 +47,9 @@
public:
EventHandler();
EventHandler(const EventHandler&) = delete;
- EventHandler(EventHandler&&);
+ EventHandler(EventHandler&&) noexcept;
EventHandler& operator=(const EventHandler&) = delete;
- EventHandler& operator=(EventHandler&&);
+ EventHandler& operator=(EventHandler&&) noexcept;
~EventHandler() noexcept;
bool init();
@@ -64,11 +64,11 @@
EventHandler::EventHandler() : fd_(-1) {}
-EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+EventHandler::EventHandler(EventHandler&& rval) noexcept : fd_(rval.fd_) {
rval.fd_ = -1;
}
-EventHandler& EventHandler::operator=(EventHandler&& rval) {
+EventHandler& EventHandler::operator=(EventHandler&& rval) noexcept {
fd_ = rval.fd_;
rval.fd_ = -1;
return *this;
@@ -213,7 +213,7 @@
}
void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
- epoll_.Wait(wait);
+ epoll_.Wait(wait).IgnoreError();
}
void TestFrame::SetChord(int key, bool value) {
diff --git a/init/parser.cpp b/init/parser.cpp
index fa0fd11..bbfbdc6 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -53,7 +53,12 @@
int section_start_line = -1;
std::vector<std::string> args;
+ // If we encounter a bad section start, there is no valid parser object to parse the subsequent
+ // sections, so we must suppress errors until the next valid section is found.
+ bool bad_section_found = false;
+
auto end_section = [&] {
+ bad_section_found = false;
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
@@ -101,6 +106,7 @@
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
+ bad_section_found = true;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
@@ -108,7 +114,7 @@
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
- } else {
+ } else if (!bad_section_found) {
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line
<< ": Invalid section keyword found";
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5c8b92a..5328869 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -111,6 +111,22 @@
LOG(FATAL) << "Failed to load serialized property info file";
}
}
+
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+ const char* target_context = nullptr;
+ property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
+
+ PropertyAuditData audit_data;
+
+ audit_data.name = name.c_str();
+
+ ucred cr = {.pid = 0, .uid = 0, .gid = 0};
+ audit_data.cr = &cr;
+
+ return selinux_check_access(source_context.c_str(), target_context, "file", "read",
+ &audit_data) == 0;
+}
+
static bool CheckMacPerms(const std::string& name, const char* target_context,
const char* source_context, const ucred& cr) {
if (!target_context || !source_context) {
@@ -746,7 +762,7 @@
return;
}
- int fd = open(rec->blk_device, O_RDONLY);
+ int fd = open(rec->blk_device, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
PLOG(ERROR) << "error opening block device " << rec->blk_device;
return;
diff --git a/init/property_service.h b/init/property_service.h
index cacd987..9022f5a 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,6 +26,8 @@
namespace android {
namespace init {
+bool CanReadProperty(const std::string& source_context, const std::string& name);
+
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/reboot.cpp b/init/reboot.cpp
index b84bfd3..a145797 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -142,7 +142,9 @@
LOG(WARNING) << "cannot find blank_screen in TurnOffBacklight";
return;
}
- service->Start();
+ if (auto result = service->Start(); !result) {
+ LOG(WARNING) << "Could not start blank_screen service: " << result.error();
+ }
}
static void ShutdownVold() {
@@ -162,7 +164,7 @@
*/
static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
std::vector<MountEntry>* emulatedPartitions, bool dump) {
- std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+ std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
if (fp == nullptr) {
PLOG(ERROR) << "Failed to open /proc/mounts";
return false;
@@ -307,15 +309,14 @@
auto shutdown_timeout = 0ms;
if (!SHUTDOWN_ZERO_TIMEOUT) {
- if (is_thermal_shutdown) {
- constexpr unsigned int thermal_shutdown_timeout = 1;
- shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
- } else {
- constexpr unsigned int shutdown_timeout_default = 6;
- auto shutdown_timeout_property = android::base::GetUintProperty(
- "ro.build.shutdown_timeout", shutdown_timeout_default);
- shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+ constexpr unsigned int shutdown_timeout_default = 6;
+ constexpr unsigned int max_thermal_shutdown_timeout = 3;
+ auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout",
+ shutdown_timeout_default);
+ if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {
+ shutdown_timeout_final = max_thermal_shutdown_timeout;
}
+ shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);
}
LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
diff --git a/init/result.h b/init/result.h
index fc03962..0e3fd3d 100644
--- a/init/result.h
+++ b/init/result.h
@@ -151,7 +151,7 @@
}
template <typename T>
-class Result {
+class [[nodiscard]] Result {
public:
Result() {}
@@ -170,6 +170,8 @@
: 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_); }
diff --git a/init/service.cpp b/init/service.cpp
index d20e90a..7f49423 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -60,6 +60,7 @@
using android::base::GetProperty;
using android::base::Join;
using android::base::ParseInt;
+using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -369,7 +370,7 @@
return;
}
- // If we crash > 4 times in 4 minutes, reboot into recovery.
+ // If we crash > 4 times in 4 minutes, reboot into bootloader.
boot_clock::time_point now = boot_clock::now();
if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
if (now < time_crashed_ + 4min) {
@@ -400,7 +401,7 @@
[] (const auto& info) { LOG(INFO) << *info; });
}
-Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
+Result<Success> Service::ParseCapabilities(std::vector<std::string>&& args) {
capabilities_ = 0;
if (!CapAmbientSupported()) {
@@ -429,29 +430,29 @@
return Success();
}
-Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
+Result<Success> Service::ParseClass(std::vector<std::string>&& args) {
classnames_ = std::set<std::string>(args.begin() + 1, args.end());
return Success();
}
-Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
+Result<Success> Service::ParseConsole(std::vector<std::string>&& args) {
flags_ |= SVC_CONSOLE;
console_ = args.size() > 1 ? "/dev/" + args[1] : "";
return Success();
}
-Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
+Result<Success> Service::ParseCritical(std::vector<std::string>&& args) {
flags_ |= SVC_CRITICAL;
return Success();
}
-Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
+Result<Success> Service::ParseDisabled(std::vector<std::string>&& args) {
flags_ |= SVC_DISABLED;
flags_ |= SVC_RC_DISABLED;
return Success();
}
-Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+Result<Success> Service::ParseEnterNamespace(std::vector<std::string>&& args) {
if (args[1] != "net") {
return Error() << "Init only supports entering network namespaces";
}
@@ -461,11 +462,11 @@
// 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, args[2]);
+ namespaces_to_enter_.emplace_back(CLONE_NEWNET, std::move(args[2]));
return Success();
}
-Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+Result<Success> 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();
@@ -482,7 +483,7 @@
return Success();
}
-Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
+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
@@ -493,7 +494,7 @@
return Success();
}
-Result<Success> Service::ParseInterface(const std::vector<std::string>& args) {
+Result<Success> Service::ParseInterface(std::vector<std::string>&& args) {
const std::string& interface_name = args[1];
const std::string& instance_name = args[2];
@@ -524,7 +525,7 @@
return Success();
}
-Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
+Result<Success> Service::ParseIoprio(std::vector<std::string>&& args) {
if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
return Error() << "priority value must be range 0 - 7";
}
@@ -542,36 +543,53 @@
return Success();
}
-Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
- for (std::size_t i = 1; i < args.size(); i++) {
+Result<Success> Service::ParseKeycodes(std::vector<std::string>&& args) {
+ auto it = args.begin() + 1;
+ if (args.size() == 2 && StartsWith(args[1], "$")) {
+ std::string expanded;
+ if (!expand_props(args[1], &expanded)) {
+ return Error() << "Could not expand property '" << args[1] << "'";
+ }
+
+ // 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();
+ }
+
+ args = Split(expanded, ",");
+ it = args.begin();
+ }
+
+ for (; it != args.end(); ++it) {
int code;
- if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+ if (ParseInt(*it, &code, 0, KEY_MAX)) {
for (auto& key : keycodes_) {
- if (key == code) return Error() << "duplicate keycode: " << args[i];
+ if (key == code) return Error() << "duplicate keycode: " << *it;
}
keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
} else {
- return Error() << "invalid keycode: " << args[i];
+ return Error() << "invalid keycode: " << *it;
}
}
return Success();
}
-Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOneshot(std::vector<std::string>&& args) {
flags_ |= SVC_ONESHOT;
return Success();
}
-Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
- std::vector<std::string> str_args(args.begin() + 1, args.end());
+Result<Success> Service::ParseOnrestart(std::vector<std::string>&& args) {
+ args.erase(args.begin());
int line = onrestart_.NumCommands() + 1;
- if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+ if (auto result = onrestart_.AddCommand(std::move(args), line); !result) {
return Error() << "cannot add Onrestart command: " << result.error();
}
return Success();
}
-Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
+Result<Success> Service::ParseNamespace(std::vector<std::string>&& args) {
for (size_t i = 1; i < args.size(); i++) {
if (args[i] == "pid") {
namespace_flags_ |= CLONE_NEWPID;
@@ -586,40 +604,40 @@
return Success();
}
-Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
+Result<Success> 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();
}
-Result<Success> Service::ParseOverride(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOverride(std::vector<std::string>&& args) {
override_ = true;
return Success();
}
-Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
+Result<Success> 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();
}
-Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
+Result<Success> 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();
}
-Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
+Result<Success> 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();
}
-Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args) {
+Result<Success> Service::ParseProcessRlimit(std::vector<std::string>&& args) {
auto rlimit = ParseRlimit(args);
if (!rlimit) return rlimit.error();
@@ -627,22 +645,31 @@
return Success();
}
-Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
- seclabel_ = args[1];
+Result<Success> 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();
}
-Result<Success> Service::ParseSigstop(const std::vector<std::string>& args) {
+Result<Success> Service::ParseSeclabel(std::vector<std::string>&& args) {
+ seclabel_ = std::move(args[1]);
+ return Success();
+}
+
+Result<Success> Service::ParseSigstop(std::vector<std::string>&& args) {
sigstop_ = true;
return Success();
}
-Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
- environment_vars_.emplace_back(args[1], args[2]);
+Result<Success> Service::ParseSetenv(std::vector<std::string>&& args) {
+ environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
return Success();
}
-Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
+Result<Success> Service::ParseShutdown(std::vector<std::string>&& args) {
if (args[1] == "critical") {
flags_ |= SVC_SHUTDOWN_CRITICAL;
return Success();
@@ -650,8 +677,17 @@
return Error() << "Invalid shutdown option";
}
+Result<Success> 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();
+}
+
template <typename T>
-Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
+Result<Success> 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;
@@ -686,26 +722,26 @@
}
// name type perm [ uid gid context ]
-Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
+Result<Success> 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'";
}
- return AddDescriptor<SocketInfo>(args);
+ return AddDescriptor<SocketInfo>(std::move(args));
}
// name type perm [ uid gid context ]
-Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
+Result<Success> 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'";
}
if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
return Error() << "file name must not be relative";
}
- return AddDescriptor<FileInfo>(args);
+ return AddDescriptor<FileInfo>(std::move(args));
}
-Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+Result<Success> 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();
@@ -714,8 +750,9 @@
return Success();
}
-Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
- writepid_files_.assign(args.begin() + 1, args.end());
+Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ writepid_files_ = std::move(args);
return Success();
}
@@ -757,12 +794,16 @@
{1, 1, &Service::ParseOomScoreAdjust}},
{"override", {0, 0, &Service::ParseOverride}},
{"priority", {1, 1, &Service::ParsePriority}},
+ {"restart_period",
+ {1, 1, &Service::ParseRestartPeriod}},
{"rlimit", {3, 3, &Service::ParseProcessRlimit}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"shutdown", {1, 1, &Service::ParseShutdown}},
{"sigstop", {0, 0, &Service::ParseSigstop}},
{"socket", {3, 6, &Service::ParseSocket}},
+ {"timeout_period",
+ {1, 1, &Service::ParseTimeoutPeriod}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
@@ -770,13 +811,13 @@
return option_parsers;
}
-Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
+Result<Success> Service::ParseLine(std::vector<std::string>&& args) {
static const OptionParserMap parser_map;
auto parser = parser_map.FindFunction(args);
if (!parser) return parser.error();
- return std::invoke(*parser, this, args);
+ return std::invoke(*parser, this, std::move(args));
}
Result<Success> Service::ExecStart() {
@@ -1022,6 +1063,18 @@
}
}
+void Service::Timeout() {
+ // All process state flags will be taken care of in Reap(), we really just want to kill the
+ // process here when it times out. Oneshot processes will transition to be disabled, and
+ // all other processes will transition to be restarting.
+ LOG(INFO) << "Service '" << name_ << "' expired its timeout of " << timeout_period_->count()
+ << " seconds and will now be killed";
+ if (pid_) {
+ KillProcessGroup(SIGKILL);
+ NotifyStateChange("stopping");
+ }
+}
+
void Service::Restart() {
if (flags_ & SVC_RUNNING) {
/* Stop, wait, then start the service. */
diff --git a/init/service.h b/init/service.h
index ea79a07..c7beee9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -21,7 +21,9 @@
#include <sys/resource.h>
#include <sys/types.h>
+#include <chrono>
#include <memory>
+#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -41,7 +43,7 @@
#define SVC_RUNNING 0x004 // currently active
#define SVC_RESTARTING 0x008 // waiting to restart
#define SVC_CONSOLE 0x010 // requires console
-#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
+#define SVC_CRITICAL 0x020 // will reboot into bootloader if keeps crashing
#define SVC_RESET 0x040 // Use when stopping a process,
// but not disabling so it can be restarted with its class.
#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
@@ -73,7 +75,7 @@
static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
- Result<Success> ParseLine(const std::vector<std::string>& args);
+ Result<Success> ParseLine(std::vector<std::string>&& args);
Result<Success> ExecStart();
Result<Success> Start();
Result<Success> StartIfNotDisabled();
@@ -81,6 +83,7 @@
void Reset();
void Stop();
void Terminate();
+ void Timeout();
void Restart();
void Reap(const siginfo_t& siginfo);
void DumpState() const;
@@ -117,10 +120,12 @@
bool process_cgroup_empty() const { return process_cgroup_empty_; }
unsigned long start_order() const { return start_order_; }
void set_sigstop(bool value) { sigstop_ = value; }
+ std::chrono::seconds restart_period() const { return restart_period_; }
+ std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
private:
- using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
+ using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
class OptionParserMap;
Result<Success> SetUpMountNamespace() const;
@@ -133,37 +138,39 @@
void KillProcessGroup(int signal);
void SetProcessAttributes();
- Result<Success> ParseCapabilities(const std::vector<std::string>& args);
- Result<Success> ParseClass(const std::vector<std::string>& args);
- Result<Success> ParseConsole(const std::vector<std::string>& args);
- Result<Success> ParseCritical(const std::vector<std::string>& args);
- Result<Success> ParseDisabled(const std::vector<std::string>& args);
- Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
- Result<Success> ParseGroup(const std::vector<std::string>& args);
- Result<Success> ParsePriority(const std::vector<std::string>& args);
- Result<Success> ParseInterface(const std::vector<std::string>& args);
- Result<Success> ParseIoprio(const std::vector<std::string>& args);
- Result<Success> ParseKeycodes(const std::vector<std::string>& args);
- Result<Success> ParseOneshot(const std::vector<std::string>& args);
- Result<Success> ParseOnrestart(const std::vector<std::string>& args);
- Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
- Result<Success> ParseOverride(const std::vector<std::string>& args);
- Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
- Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
- Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
- Result<Success> ParseNamespace(const std::vector<std::string>& args);
- Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
- Result<Success> ParseSeclabel(const std::vector<std::string>& args);
- Result<Success> ParseSetenv(const std::vector<std::string>& args);
- Result<Success> ParseShutdown(const std::vector<std::string>& args);
- Result<Success> ParseSigstop(const std::vector<std::string>& args);
- Result<Success> ParseSocket(const std::vector<std::string>& args);
- Result<Success> ParseFile(const std::vector<std::string>& args);
- Result<Success> ParseUser(const std::vector<std::string>& args);
- Result<Success> ParseWritepid(const std::vector<std::string>& args);
+ 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> 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);
template <typename T>
- Result<Success> AddDescriptor(const std::vector<std::string>& args);
+ Result<Success> AddDescriptor(std::vector<std::string>&& args);
static unsigned long next_start_order_;
static bool is_exec_service_running_;
@@ -220,6 +227,9 @@
bool sigstop_ = false;
+ std::chrono::seconds restart_period_ = 5s;
+ std::optional<std::chrono::seconds> timeout_period_;
+
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
diff --git a/init/stable_properties.h b/init/stable_properties.h
deleted file mode 100644
index 4972d10..0000000
--- a/init/stable_properties.h
+++ /dev/null
@@ -1,66 +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.
- */
-
-#ifndef _INIT_STABLE_PROPERTIES_H
-#define _INIT_STABLE_PROPERTIES_H
-
-#include <set>
-#include <string>
-
-namespace android {
-namespace init {
-
-static constexpr const char* kPartnerPrefixes[] = {
- "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.", "init.svc.odm.", "ro.odm.",
- "persist.odm.", "odm.", "ro.boot.",
-};
-
-static const std::set<std::string> kExportedActionableProperties = {
- "dev.bootcomplete",
- "init.svc.console",
- "init.svc.mediadrm",
- "init.svc.surfaceflinger",
- "init.svc.zygote",
- "persist.bluetooth.btsnoopenable",
- "persist.sys.crash_rcu",
- "persist.sys.usb.usbradio.config",
- "persist.sys.zram_enabled",
- "ro.board.platform",
- "ro.bootmode",
- "ro.build.type",
- "ro.crypto.state",
- "ro.crypto.type",
- "ro.debuggable",
- "sys.boot_completed",
- "sys.boot_from_charger_mode",
- "sys.retaildemo.enabled",
- "sys.shutdown.requested",
- "sys.usb.config",
- "sys.usb.configfs",
- "sys.usb.ffs.mtp.ready",
- "sys.usb.ffs.ready",
- "sys.user.0.ce_available",
- "sys.vdso",
- "vold.decrypt",
- "vold.post_fs_data_done",
- "vts.native_server.on",
- "wlan.driver.status",
-};
-
-} // namespace init
-} // namespace android
-
-#endif
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index 6307993..eae03e3 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"});
+ subcontext.Execute(std::vector<std::string>{"return_success"}).IgnoreError();
}
if (subcontext.pid() > 0) {
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 95be6af..66491dd 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -175,7 +175,7 @@
//
// When a subprocess crashes, we fatally abort from ueventd. init will restart ueventd when
// init reaps it, and the cold boot process will start again. If this continues to fail, then
- // since ueventd is marked as a critical service, init will reboot to recovery.
+ // since ueventd is marked as a critical service, init will reboot to bootloader.
//
// When a subprocess gets stuck, keep ueventd spinning waiting for it. init has a timeout for
// cold boot and will reboot to the bootloader if ueventd does not complete in time.
diff --git a/init/util.cpp b/init/util.cpp
index 105ac87..3781141 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -436,5 +436,21 @@
return true;
}
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function) {
+ // Make stdin/stdout/stderr all point to /dev/null.
+ int fd = open("/dev/null", O_RDWR);
+ if (fd == -1) {
+ int saved_errno = errno;
+ android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+ errno = saved_errno;
+ PLOG(FATAL) << "Couldn't open /dev/null";
+ }
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) close(fd);
+ android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 07e4864..53f4547 100644
--- a/init/util.h
+++ b/init/util.h
@@ -64,6 +64,8 @@
bool IsLegalPropertyName(const std::string& name);
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function);
+
} // namespace init
} // namespace android
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index c42ae49..7f9a18a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -27,15 +27,6 @@
enabled: false,
},
},
-
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
}
libbacktrace_sources = [
@@ -108,7 +99,7 @@
whole_static_libs: ["libdemangle"],
}
-cc_library_shared {
+cc_test_library {
name: "libbacktrace_test",
defaults: ["libbacktrace_common"],
host_supported: true,
@@ -121,6 +112,17 @@
shared_libs: [
"libunwindstack",
],
+ relative_install_path: "backtrace_test_libs",
+
+ target: {
+ linux_glibc: {
+ // This forces the creation of eh_frame with unwind information
+ // for host.
+ cflags: [
+ "-fcxx-exceptions"
+ ],
+ },
+ },
}
//-------------------------------------------------------------------------
@@ -128,12 +130,12 @@
//-------------------------------------------------------------------------
cc_test {
name: "backtrace_test",
+ isolated: true,
defaults: ["libbacktrace_common"],
host_supported: true,
srcs: [
"backtrace_offline_test.cpp",
"backtrace_test.cpp",
- "GetPss.cpp",
],
cflags: [
@@ -143,7 +145,6 @@
],
shared_libs: [
- "libbacktrace_test",
"libbacktrace",
"libbase",
"liblog",
@@ -152,17 +153,10 @@
group_static_libs: true,
- target: {
- android: {
- cflags: ["-DENABLE_PSS_TESTS"],
- shared_libs: [
- "libutils",
- ],
- },
- linux_glibc: {
- static_libs: ["libutils"],
- },
- },
+ // So that the dlopen can find the libbacktrace_test.so.
+ ldflags: [
+ "-Wl,--rpath,${ORIGIN}/../backtrace_test_libs",
+ ],
test_suites: ["device-tests"],
data: [
diff --git a/libbacktrace/BacktraceTest.h b/libbacktrace/BacktraceTest.h
new file mode 100644
index 0000000..c38af04
--- /dev/null
+++ b/libbacktrace/BacktraceTest.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 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 _LIBBACKTRACE_BACKTRACE_TEST_H
+#define _LIBBACKTRACE_BACKTRACE_TEST_H
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+class BacktraceTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ dl_handle_ = dlopen("libbacktrace_test.so", RTLD_NOW | RTLD_LOCAL);
+
+ test_level_one_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_one"));
+
+ test_level_two_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_two"));
+
+ test_level_three_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_three"));
+
+ test_level_four_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_four"));
+
+ test_recursive_call_ = reinterpret_cast<int (*)(int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_recursive_call"));
+
+ test_get_context_and_wait_ = reinterpret_cast<void (*)(void*, volatile int*)>(
+ dlsym(dl_handle_, "test_get_context_and_wait"));
+
+ test_signal_action_ =
+ reinterpret_cast<void (*)(int, siginfo_t*, void*)>(dlsym(dl_handle_, "test_signal_action"));
+
+ test_signal_handler_ =
+ reinterpret_cast<void (*)(int)>(dlsym(dl_handle_, "test_signal_handler"));
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(dl_handle_ != nullptr);
+ ASSERT_TRUE(test_level_one_ != nullptr);
+ ASSERT_TRUE(test_level_two_ != nullptr);
+ ASSERT_TRUE(test_level_three_ != nullptr);
+ ASSERT_TRUE(test_level_four_ != nullptr);
+ ASSERT_TRUE(test_recursive_call_ != nullptr);
+ ASSERT_TRUE(test_get_context_and_wait_ != nullptr);
+ ASSERT_TRUE(test_signal_action_ != nullptr);
+ ASSERT_TRUE(test_signal_handler_ != nullptr);
+ }
+
+ public:
+ static void* dl_handle_;
+ static int (*test_level_one_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_two_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_three_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_four_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_recursive_call_)(int, void (*)(void*), void*);
+ static void (*test_get_context_and_wait_)(void*, volatile int*);
+ static void (*test_signal_action_)(int, siginfo_t*, void*);
+ static void (*test_signal_handler_)(int);
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_TEST_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
deleted file mode 100644
index 6d750ea..0000000
--- a/libbacktrace/GetPss.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2014 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 <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-// This is an extremely simplified version of libpagemap.
-
-#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
-
-#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1))
-#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1))
-#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6))
-#define PAGEMAP_PFN(x) (_BITS(x, 0, 55))
-#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
-#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5))
-
-static bool ReadData(int fd, off_t place, uint64_t *data) {
- if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
- return false;
- }
- if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
- return false;
- }
- return true;
-}
-
-size_t GetPssBytes() {
- FILE* maps = fopen("/proc/self/maps", "r");
- if (maps == nullptr) {
- return 0;
- }
-
- int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
- if (pagecount_fd == -1) {
- fclose(maps);
- return 0;
- }
-
- int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
- if (pagemap_fd == -1) {
- fclose(maps);
- close(pagecount_fd);
- return 0;
- }
-
- char line[4096];
- size_t total_pss = 0;
- int pagesize = getpagesize();
- while (fgets(line, sizeof(line), maps)) {
- uintptr_t start, end;
- if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
- total_pss = 0;
- break;
- }
- for (off_t page = static_cast<off_t>(start/pagesize);
- page < static_cast<off_t>(end/pagesize); page++) {
- uint64_t data;
- if (ReadData(pagemap_fd, page, &data)) {
- if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
- uint64_t count;
- if (ReadData(pagecount_fd, static_cast<off_t>(PAGEMAP_PFN(data)), &count)) {
- total_pss += (count >= 1) ? pagesize / count : 0;
- }
- }
- }
- }
- }
-
- fclose(maps);
-
- close(pagecount_fd);
- close(pagemap_fd);
-
- return total_pss;
-}
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 4e7f761..fe28eba 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -49,6 +49,7 @@
unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
regs, stack_map->process_memory());
unwinder.SetResolveNames(stack_map->ResolveNames());
+ stack_map->SetArch(regs->Arch());
if (stack_map->GetJitDebug() != nullptr) {
unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
}
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index d2d6ab8..9d15af2 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -25,6 +25,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
#include "UnwindStackMap.h"
@@ -106,7 +107,17 @@
return "";
}
- unwindstack::Elf* elf = map_info->GetElf(process_memory(), true);
+ if (arch_ == unwindstack::ARCH_UNKNOWN) {
+ if (pid_ == getpid()) {
+ arch_ = unwindstack::Regs::CurrentArch();
+ } else {
+ // Create a remote regs, to figure out the architecture.
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid_));
+ arch_ = regs->Arch();
+ }
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(process_memory(), arch_);
std::string name;
uint64_t func_offset;
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 039f4a2..e19b605 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -30,6 +30,7 @@
#if !defined(NO_LIBDEXFILE_SUPPORT)
#include <unwindstack/DexFiles.h>
#endif
+#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
@@ -58,6 +59,8 @@
unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
#endif
+ void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
+
protected:
uint64_t GetLoadBias(size_t index) override;
@@ -67,6 +70,8 @@
#if !defined(NO_LIBDEXFILE_SUPPORT)
std::unique_ptr<unwindstack::DexFiles> dex_files_;
#endif
+
+ unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
};
class UnwindStackOfflineMap : public UnwindStackMap {
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 7d1027e..662fb99 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -37,15 +37,7 @@
#include <gtest/gtest.h>
-extern "C" {
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, void (*)(void*), void*);
-int test_level_two(int, int, int, int, void (*)(void*), void*);
-int test_level_three(int, int, int, int, void (*)(void*), void*);
-int test_level_four(int, int, int, int, void (*)(void*), void*);
-int test_recursive_call(int, void (*)(void*), void*);
-void test_get_context_and_wait(void* context, volatile int* exit_flag);
-}
+#include "BacktraceTest.h"
struct FunctionSymbol {
std::string name;
@@ -56,12 +48,13 @@
static std::vector<FunctionSymbol> GetFunctionSymbols() {
std::vector<FunctionSymbol> symbols = {
{"unknown_start", 0, 0},
- {"test_level_one", reinterpret_cast<uint64_t>(&test_level_one), 0},
- {"test_level_two", reinterpret_cast<uint64_t>(&test_level_two), 0},
- {"test_level_three", reinterpret_cast<uint64_t>(&test_level_three), 0},
- {"test_level_four", reinterpret_cast<uint64_t>(&test_level_four), 0},
- {"test_recursive_call", reinterpret_cast<uint64_t>(&test_recursive_call), 0},
- {"test_get_context_and_wait", reinterpret_cast<uint64_t>(&test_get_context_and_wait), 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(
@@ -100,7 +93,7 @@
static void* OfflineThreadFunc(void* arg) {
OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
fn_arg->tid = android::base::GetThreadId();
- test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
+ BacktraceTest::test_get_context_and_wait_(&fn_arg->ucontext, &fn_arg->exit_flag);
return nullptr;
}
@@ -109,7 +102,7 @@
}
// This test is disable because it is for generating test data.
-TEST(libbacktrace, DISABLED_generate_offline_testdata) {
+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);
@@ -304,22 +297,22 @@
}
// For now, these tests can only run on the given architectures.
-TEST(libbacktrace, offline_eh_frame) {
+TEST_F(BacktraceTest, offline_eh_frame) {
BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
}
-TEST(libbacktrace, offline_debug_frame) {
+TEST_F(BacktraceTest, offline_debug_frame) {
BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
}
-TEST(libbacktrace, offline_gnu_debugdata) {
+TEST_F(BacktraceTest, offline_gnu_debugdata) {
BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
}
-TEST(libbacktrace, offline_arm_exidx) {
+TEST_F(BacktraceTest, offline_arm_exidx) {
BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
}
@@ -373,32 +366,32 @@
// 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(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
+TEST_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
}
-TEST(libbacktrace, offline_debug_frame_with_load_bias) {
+TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
}
-TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
+TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
}
-TEST(libbacktrace, offline_cie_with_P_augmentation) {
+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(libbacktrace, offline_empty_eh_frame_hdr) {
+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(libbacktrace, offline_max_frames_limit) {
+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/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 06a32c7..f4191b9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <malloc.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
@@ -55,6 +56,7 @@
// For the THREAD_SIGNAL definition.
#include "BacktraceCurrent.h"
+#include "BacktraceTest.h"
#include "backtrace_testlib.h"
// Number of microseconds per milliseconds.
@@ -95,6 +97,23 @@
static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
map_create_func_t map_func = nullptr);
+void* BacktraceTest::dl_handle_;
+int (*BacktraceTest::test_level_one_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_two_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_three_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_four_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_recursive_call_)(int, void (*)(void*), void*);
+void (*BacktraceTest::test_get_context_and_wait_)(void*, volatile int*);
+void (*BacktraceTest::test_signal_action_)(int, siginfo_t*, void*);
+void (*BacktraceTest::test_signal_handler_)(int);
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+ static const char* initial_args[] = {"--slow_threshold_ms=8000", "--deadline_threshold_ms=15000"};
+ *args = initial_args;
+ *num_args = 2;
+ return true;
+}
+
static uint64_t NanoTime() {
struct timespec t = { 0, 0 };
clock_gettime(CLOCK_MONOTONIC, &t);
@@ -250,7 +269,7 @@
return false;
}
-TEST(libbacktrace, local_no_unwind_frames) {
+TEST_F(BacktraceTest, local_no_unwind_frames) {
// Verify that a local unwind does not include any frames within
// libunwind or libbacktrace.
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -270,7 +289,7 @@
}
}
-TEST(libbacktrace, local_unwind_frames) {
+TEST_F(BacktraceTest, local_unwind_frames) {
// Verify that a local unwind with the skip frames disabled does include
// frames within the backtrace libraries.
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -302,8 +321,8 @@
<< DumpFrames(backtrace.get());
}
-TEST(libbacktrace, local_trace) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_trace) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
}
static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
@@ -357,12 +376,12 @@
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
}
-TEST(libbacktrace, local_trace_ignore_frames) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
+TEST_F(BacktraceTest, local_trace_ignore_frames) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
}
-TEST(libbacktrace, local_max_trace) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_max_trace) {
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxBacktrace, nullptr), 0);
}
static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
@@ -402,10 +421,10 @@
ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
}
-TEST(libbacktrace, ptrace_trace) {
+TEST_F(BacktraceTest, ptrace_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
@@ -416,10 +435,10 @@
ASSERT_EQ(waitpid(pid, &status, 0), pid);
}
-TEST(libbacktrace, ptrace_max_trace) {
+TEST_F(BacktraceTest, ptrace_max_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
@@ -446,10 +465,10 @@
VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
}
-TEST(libbacktrace, ptrace_ignore_frames) {
+TEST_F(BacktraceTest, ptrace_ignore_frames) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
@@ -462,7 +481,7 @@
// Create a process with multiple threads and dump all of the threads.
static void* PtraceThreadLevelRun(void*) {
- EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
return nullptr;
}
@@ -483,7 +502,7 @@
}
}
-TEST(libbacktrace, ptrace_threads) {
+TEST_F(BacktraceTest, ptrace_threads) {
pid_t pid;
if ((pid = fork()) == 0) {
for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
@@ -494,7 +513,7 @@
pthread_t thread;
ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
}
- ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
@@ -532,8 +551,8 @@
VerifyLevelDump(backtrace.get());
}
-TEST(libbacktrace, thread_current_level) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_level) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
}
static void VerifyMaxThread(void*) {
@@ -545,19 +564,19 @@
VerifyMaxDump(backtrace.get());
}
-TEST(libbacktrace, thread_current_max) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_max) {
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxThread, nullptr), 0);
}
static void* ThreadLevelRun(void* data) {
thread_t* thread = reinterpret_cast<thread_t*>(data);
thread->tid = android::base::GetThreadId();
- EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+ EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, ThreadSetState, data), 0);
return nullptr;
}
-TEST(libbacktrace, thread_level_trace) {
+TEST_F(BacktraceTest, thread_level_trace) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -607,7 +626,7 @@
EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
}
-TEST(libbacktrace, thread_ignore_frames) {
+TEST_F(BacktraceTest, thread_ignore_frames) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -644,11 +663,12 @@
thread_t* thread = reinterpret_cast<thread_t*>(data);
thread->tid = android::base::GetThreadId();
- EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+ EXPECT_NE(BacktraceTest::test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, ThreadSetState, data),
+ 0);
return nullptr;
}
-TEST(libbacktrace, thread_max_trace) {
+TEST_F(BacktraceTest, thread_max_trace) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -742,17 +762,17 @@
}
}
-TEST(libbacktrace, thread_multiple_dump) {
+TEST_F(BacktraceTest, thread_multiple_dump) {
MultipleThreadDumpTest(false);
}
-TEST(libbacktrace, thread_multiple_dump_same_map) {
+TEST_F(BacktraceTest, thread_multiple_dump_same_map) {
MultipleThreadDumpTest(true);
}
// This test is for UnwindMaps that should share the same map cursor when
// multiple maps are created for the current process at the same time.
-TEST(libbacktrace, simultaneous_maps) {
+TEST_F(BacktraceTest, simultaneous_maps) {
BacktraceMap* map1 = BacktraceMap::Create(getpid());
BacktraceMap* map2 = BacktraceMap::Create(getpid());
BacktraceMap* map3 = BacktraceMap::Create(getpid());
@@ -779,7 +799,7 @@
delete map3;
}
-TEST(libbacktrace, fillin_erases) {
+TEST_F(BacktraceTest, fillin_erases) {
BacktraceMap* back_map = BacktraceMap::Create(getpid());
backtrace_map_t map;
@@ -798,7 +818,7 @@
ASSERT_EQ("", map.name);
}
-TEST(libbacktrace, format_test) {
+TEST_F(BacktraceTest, format_test) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
@@ -969,7 +989,7 @@
ASSERT_TRUE(test_it == test_maps.end());
}
-TEST(libbacktrace, verify_map_remote) {
+TEST_F(BacktraceTest, verify_map_remote) {
pid_t pid;
CreateRemoteProcess(&pid);
@@ -1069,7 +1089,7 @@
delete[] expected;
}
-TEST(libbacktrace, thread_read) {
+TEST_F(BacktraceTest, thread_read) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -1120,7 +1140,7 @@
}
}
-TEST(libbacktrace, process_read) {
+TEST_F(BacktraceTest, process_read) {
g_ready = 0;
pid_t pid;
if ((pid = fork()) == 0) {
@@ -1187,29 +1207,23 @@
}
static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
- std::string system_dir;
-
-#if defined(__BIONIC__)
- system_dir = "/system/lib";
-#else
- const char* host_out_env = getenv("ANDROID_HOST_OUT");
- ASSERT_TRUE(host_out_env != nullptr);
- system_dir = std::string(host_out_env) + "/lib";
-#endif
-
-#if defined(__LP64__)
- system_dir += "64";
-#endif
+ std::string test_lib(testing::internal::GetArgvs()[0]);
+ auto const value = test_lib.find_last_of('/');
+ if (value == std::string::npos) {
+ test_lib = "../backtrace_test_libs/";
+ } else {
+ test_lib = test_lib.substr(0, value + 1) + "../backtrace_test_libs/";
+ }
+ test_lib += "libbacktrace_test.so";
*tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
- std::string cp_cmd =
- android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+ std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
// Copy the shared so to a tempory directory.
ASSERT_EQ(0, system(cp_cmd.c_str()));
}
-TEST(libbacktrace, check_unreadable_elf_local) {
+TEST_F(BacktraceTest, check_unreadable_elf_local) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1251,7 +1265,7 @@
VerifyFunctionsFound(found_functions);
}
-TEST(libbacktrace, check_unreadable_elf_remote) {
+TEST_F(BacktraceTest, check_unreadable_elf_remote) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1390,7 +1404,7 @@
typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
-TEST(libbacktrace, unwind_through_unreadable_elf_local) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_local) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1405,11 +1419,9 @@
ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace, reinterpret_cast<void*>(test_func)),
0);
-
- ASSERT_TRUE(dlclose(lib_handle) == 0);
}
-TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_remote) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1428,7 +1440,6 @@
exit(0);
}
ASSERT_TRUE(pid > 0);
- ASSERT_TRUE(dlclose(lib_handle) == 0);
uint64_t start = NanoTime();
bool done = false;
@@ -1465,7 +1476,7 @@
ASSERT_TRUE(done) << "Test function never found in unwind.";
}
-TEST(libbacktrace, unwind_thread_doesnt_exist) {
+TEST_F(BacktraceTest, unwind_thread_doesnt_exist) {
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
ASSERT_TRUE(backtrace.get() != nullptr);
@@ -1473,18 +1484,18 @@
ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
}
-TEST(libbacktrace, local_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, local_get_function_name_before_unwind) {
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
// Verify that trying to get a function name before doing an unwind works.
- uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
uint64_t offset;
ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
}
-TEST(libbacktrace, remote_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, remote_get_function_name_before_unwind) {
pid_t pid;
CreateRemoteProcess(&pid);
@@ -1492,7 +1503,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
// Verify that trying to get a function name before doing an unwind works.
- uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
uint64_t offset;
ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
@@ -1579,7 +1590,7 @@
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
- uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(BacktraceTest::test_level_one_) + 1;
// Now verify the device map flag actually causes the function name to be empty.
backtrace->FillInMap(cur_func_offset, &map);
ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
@@ -1628,7 +1639,7 @@
ASSERT_EQ(0U, backtrace->NumFrames());
}
-TEST(libbacktrace, unwind_disallow_device_map_local) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_local) {
void* device_map;
SetupDeviceMap(&device_map);
@@ -1642,7 +1653,7 @@
munmap(device_map, DEVICE_MAP_SIZE);
}
-TEST(libbacktrace, unwind_disallow_device_map_remote) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_remote) {
void* device_map;
SetupDeviceMap(&device_map);
@@ -1698,13 +1709,13 @@
pid_t pid;
if ((pid = fork()) == 0) {
if (use_action) {
- ScopedSignalHandler ssh(SIGUSR1, test_signal_action);
+ ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_action_);
- test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
} else {
- ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);
+ ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_handler_);
- test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
}
}
ASSERT_NE(-1, pid);
@@ -1805,11 +1816,11 @@
FinishRemoteProcess(pid);
}
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_handler) {
UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
}
-TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_action) {
UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
}
@@ -1822,49 +1833,41 @@
ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
}
-TEST(libbacktrace, unwind_frame_skip_numbering) {
+TEST_F(BacktraceTest, unwind_frame_skip_numbering) {
TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
}
-#if defined(ENABLE_PSS_TESTS)
-#include "GetPss.h"
-
#define MAX_LEAK_BYTES (32*1024UL)
static void CheckForLeak(pid_t pid, pid_t tid) {
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
- // Do a few runs to get the PSS stable.
- for (size_t i = 0; i < 100; i++) {
- Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
- ASSERT_TRUE(backtrace != nullptr);
- ASSERT_TRUE(backtrace->Unwind(0));
- VERIFY_NO_ERROR(backtrace->GetError().error_code);
- delete backtrace;
- }
- size_t stable_pss = GetPssBytes();
- ASSERT_TRUE(stable_pss != 0);
-
// Loop enough that even a small leak should be detectable.
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
for (size_t i = 0; i < 4096; i++) {
Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
VERIFY_NO_ERROR(backtrace->GetError().error_code);
delete backtrace;
- }
- size_t new_pss = GetPssBytes();
- ASSERT_TRUE(new_pss != 0);
- if (new_pss > stable_pss) {
- ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+
+ size_t allocated_bytes = mallinfo().uordblks;
+ if (first_allocated_bytes == 0) {
+ first_allocated_bytes = allocated_bytes;
+ } else if (last_allocated_bytes > first_allocated_bytes) {
+ // Check that the memory did not increase too much over the first loop.
+ ASSERT_LE(last_allocated_bytes - first_allocated_bytes, MAX_LEAK_BYTES);
+ }
+ last_allocated_bytes = allocated_bytes;
}
}
-TEST(libbacktrace, check_for_leak_local) {
+TEST_F(BacktraceTest, check_for_leak_local) {
CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
}
-TEST(libbacktrace, check_for_leak_local_thread) {
+TEST_F(BacktraceTest, check_for_leak_local_thread) {
thread_t thread_data = { 0, 0, 0, nullptr };
pthread_t thread;
ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
@@ -1880,7 +1883,7 @@
ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
}
-TEST(libbacktrace, check_for_leak_remote) {
+TEST_F(BacktraceTest, check_for_leak_remote) {
pid_t pid;
CreateRemoteProcess(&pid);
@@ -1888,4 +1891,3 @@
FinishRemoteProcess(pid);
}
-#endif
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index b24468b..285f150 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __CUTILS_SOCKETS_H
-#define __CUTILS_SOCKETS_H
+#pragma once
#include <errno.h>
#include <limits.h>
@@ -141,19 +140,6 @@
const cutils_socket_buffer_t* buffers,
size_t num_buffers);
-/*
- * socket_peer_is_trusted - Takes a socket which is presumed to be a
- * connected local socket (e.g. AF_LOCAL) and returns whether the peer
- * (the userid that owns the process on the other end of that socket)
- * is one of the two trusted userids, root or shell.
- *
- * Note: This only works as advertised on the Android OS and always
- * just returns true when called on other operating systems.
- */
-extern bool socket_peer_is_trusted(int fd);
-
#ifdef __cplusplus
}
#endif
-
-#endif /* __CUTILS_SOCKETS_H */
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 3be8ad0..845c586 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -131,6 +131,7 @@
#define AID_SECURE_ELEMENT 1068 /* secure element subsystem */
#define AID_LMKD 1069 /* low memory killer daemon */
#define AID_LLKD 1070 /* live lock daemon */
+#define AID_IORAPD 1071 /* input/output readahead and pin daemon */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/partition_utils.cpp b/libcutils/partition_utils.cpp
index 6735d6c..2211ff6 100644
--- a/libcutils/partition_utils.cpp
+++ b/libcutils/partition_utils.cpp
@@ -25,7 +25,7 @@
#include <cutils/properties.h>
-static int only_one_char(char *buf, int len, char c)
+static int only_one_char(uint8_t *buf, int len, uint8_t c)
{
int i, ret;
@@ -41,7 +41,7 @@
int partition_wiped(char *source)
{
- char buf[4096];
+ uint8_t buf[4096];
int fd, ret;
if ((fd = open(source, O_RDONLY)) < 0) {
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 0cb8a4d..2248817 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -32,34 +32,6 @@
#include "android_get_control_env.h"
-#if defined(__ANDROID__)
-/* For the socket trust (credentials) check */
-#include <private/android_filesystem_config.h>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-bool socket_peer_is_trusted(int fd __android_unused) {
-#if defined(__ANDROID__)
- ucred cr;
- socklen_t len = sizeof(cr);
- int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-
- if (n != 0) {
- ALOGE("could not get socket credentials: %s\n", strerror(errno));
- return false;
- }
-
- if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
- ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
- return false;
- }
-#endif
-
- return true;
-}
-
int socket_close(int sock) {
return close(sock);
}
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2e2bf87..574a386 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -72,7 +72,7 @@
explicit MapString(const std::string& str)
: alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
}
- MapString(MapString&& rval)
+ MapString(MapString&& rval) noexcept
: alloc(rval.alloc), str(rval.data(), rval.length()) {
rval.alloc = NULL;
}
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index adabfd8..b9dead5 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -33,12 +33,12 @@
}
~ScopedPipe() { Close(); }
- ScopedPipe(ScopedPipe&& other) {
+ ScopedPipe(ScopedPipe&& other) noexcept {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
}
- ScopedPipe& operator=(ScopedPipe&& other) {
+ ScopedPipe& operator=(ScopedPipe&& other) noexcept {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
return *this;
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 355679f..f3ab652 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -38,7 +38,7 @@
Node(T* ptr, Allocator<Node> allocator)
: references_in(allocator), references_out(allocator), ptr(ptr){};
- Node(Node&& rhs) = default;
+ Node(Node&& rhs) noexcept = default;
void Edge(Node<T>* ref) {
references_out.emplace(ref);
ref->references_in.emplace(this);
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 1551b5b..2d327ee 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -11,7 +11,11 @@
export_include_dirs: ["include"],
local_include_dirs: ["include"],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libstatssocket",
+ ],
whole_static_libs: ["libgtest_prod"],
cflags: [
@@ -23,17 +27,20 @@
// metricslogger shared library
// -----------------------------------------------------------------------------
-cc_library_shared {
+cc_library {
name: "libmetricslogger",
srcs: metricslogger_lib_src_files,
defaults: ["metricslogger_defaults"],
+ export_shared_lib_headers: ["libstatssocket"],
}
// static version of libmetricslogger, needed by a few art static binaries
+// TODO(b/117829226): Remove once dependencies are cleaned up.
cc_library_static {
name: "libmetricslogger_static",
srcs: metricslogger_lib_src_files,
defaults: ["metricslogger_defaults"],
+ export_shared_lib_headers: ["libstatssocket"],
}
// metricslogger shared library, debug
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index c305db2..56bd6c4 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -15,6 +15,7 @@
*/
#include <log/log_event_list.h>
+#include <stats_event_list.h>
#include <cstdint>
#include <string>
@@ -43,6 +44,7 @@
class ComplexEventLogger {
private:
android_log_event_list logger;
+ stats_event_list stats_logger;
public:
// Create a complex event with category|category|.
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6a32153..2a1b137 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,11 +18,15 @@
#include <cstdlib>
+#include <android-base/chrono_utils.h>
#include <log/event_tag_map.h>
-#include <log/log_event_list.h>
+
+using namespace android;
namespace {
+const static int kStatsEventTag = 1937006964;
+const static int kKeyValuePairAtomId = 83;
#ifdef __ANDROID__
EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
const int kSysuiMultiActionTag = android_lookupEventTagNum(
@@ -32,6 +36,12 @@
const int kSysuiMultiActionTag = 0;
#endif
+int64_t getElapsedTimeNanoSinceBoot() {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(
+ android::base::boot_clock::now().time_since_epoch())
+ .count();
+}
+
} // namespace
namespace android {
@@ -42,6 +52,12 @@
android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
<< LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+
+ stats_event_list stats_log(kStatsEventTag);
+ stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event << LOGBUILDER_BUCKET << data
+ << LOGBUILDER_VALUE << 1;
+ stats_log.write(LOG_ID_STATS);
}
// Mirror com.android.internal.logging.MetricsLogger#count().
@@ -49,6 +65,11 @@
android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
<< val << LOG_ID_EVENTS;
+
+ stats_event_list stats_log(kStatsEventTag);
+ stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE << val;
+ stats_log.write(LOG_ID_STATS);
}
// Mirror com.android.internal.logging.MetricsLogger#action().
@@ -56,34 +77,48 @@
android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
<< field << value << LOG_ID_EVENTS;
+
+ stats_event_list stats_log(kStatsEventTag);
+ stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << category << LOGBUILDER_TYPE << TYPE_ACTION << field << value;
+ stats_log.write(LOG_ID_STATS);
}
-ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+ComplexEventLogger::ComplexEventLogger(int category)
+ : logger(kSysuiMultiActionTag), stats_logger(kStatsEventTag) {
logger << LOGBUILDER_CATEGORY << category;
+ stats_logger << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+ << category;
}
void ComplexEventLogger::SetPackageName(const std::string& package_name) {
logger << LOGBUILDER_PACKAGENAME << package_name;
+ stats_logger << LOGBUILDER_PACKAGENAME << package_name;
}
void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::AddTaggedData(int tag, float value) {
logger << tag << value;
+ stats_logger << tag << value;
}
void ComplexEventLogger::Record() {
logger << LOG_ID_EVENTS;
+ stats_logger.write(LOG_ID_STATS);
}
} // namespace metricslogger
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 6e63b74..92fd2a2 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -10,9 +10,11 @@
host_supported: true,
srcs: ["native_bridge.cc"],
+ header_libs: [
+ "libbase_headers",
+ ],
shared_libs: [
"liblog",
- "libbase",
],
export_include_dirs: ["include"],
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 5b9ba1c..4ed6e20 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -29,10 +29,12 @@
shared_libraries := \
liblog \
- libbase \
libnativebridge \
libnativebridge-dummy
+header_libraries := \
+ libbase_headers
+
libnativebridge_tests_common_cflags := \
-Wall \
-Werror \
@@ -40,6 +42,7 @@
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -49,6 +52,7 @@
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index ac009a9..f47b6e4 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -171,7 +171,7 @@
}
mAssembly->resize( int(pc()-base())*4 );
-
+
// the instruction cache is flushed by CodeCache
const int64_t duration = ggl_system_time() - mDuration;
const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
@@ -183,8 +183,8 @@
printf(format, name, int(pc()-base()), base(), pc(), duration);
disassemble(name);
}
-
- return NO_ERROR;
+
+ return OK;
}
uint32_t* ARMAssembler::pcForLabel(const char* label)
@@ -213,14 +213,14 @@
// multiply...
void ARMAssembler::MLA(int cc, int s,
int Rd, int Rm, int Rs, int Rn) {
- if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
+ if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
LOG_FATAL_IF(Rd==Rm, "MLA(r%u,r%u,r%u,r%u)", Rd,Rm,Rs,Rn);
*mPC++ = (cc<<28) | (1<<21) | (s<<20) |
(Rd<<16) | (Rn<<12) | (Rs<<8) | 0x90 | Rm;
}
void ARMAssembler::MUL(int cc, int s,
int Rd, int Rm, int Rs) {
- if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
+ if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
LOG_FATAL_IF(Rd==Rm, "MUL(r%u,r%u,r%u)", Rd,Rm,Rs);
*mPC++ = (cc<<28) | (s<<20) | (Rd<<16) | (Rs<<8) | 0x90 | Rm;
}
@@ -577,4 +577,3 @@
}
}; // namespace android
-
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index aebc129..8926776 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -325,7 +325,7 @@
printf(format, name, int(pc()-base()), base(), pc(), duration);
disassemble(name);
}
- return NO_ERROR;
+ return OK;
}
uint32_t* ArmToArm64Assembler::pcForLabel(const char* label)
@@ -1238,4 +1238,3 @@
}
}; // namespace android
-
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 039a725..7de8cc1 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -1421,7 +1421,7 @@
disassemble(name);
}
- return NO_ERROR;
+ return OK;
}
uint32_t* MIPSAssembler::pcForLabel(const char* label)
@@ -1953,5 +1953,3 @@
}; // namespace android:
-
-
diff --git a/libstats/Android.bp b/libstats/Android.bp
index d58f294..f5ee1da 100644
--- a/libstats/Android.bp
+++ b/libstats/Android.bp
@@ -17,12 +17,13 @@
// ==========================================================
// Native library to write stats log to statsd socket
// ==========================================================
-cc_library_static {
+cc_library {
name: "libstatssocket",
srcs: [
"stats_event_list.c",
"statsd_writer.c",
],
+ host_supported: true,
cflags: [
"-Wall",
"-Werror",
@@ -32,6 +33,7 @@
],
export_include_dirs: ["include"],
shared_libs: [
+ "libcutils",
"liblog",
],
}
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 5d174ae..a9832db 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -24,6 +24,8 @@
#endif
void reset_log_context(android_log_context ctx);
int write_to_logger(android_log_context context, log_id_t id);
+void note_log_drop();
+void stats_log_close();
#ifdef __cplusplus
}
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 3d746db..72770d4 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -17,6 +17,7 @@
#include "include/stats_event_list.h"
#include <string.h>
+#include <sys/time.h>
#include "statsd_writer.h"
#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
@@ -119,6 +120,19 @@
return retValue;
}
+void note_log_drop() {
+ statsdLoggerWrite.noteDrop();
+}
+
+void stats_log_close() {
+ statsd_writer_init_lock();
+ write_to_statsd = __write_to_statsd_init;
+ if (statsdLoggerWrite.close) {
+ (*statsdLoggerWrite.close)();
+ }
+ statsd_writer_init_unlock();
+}
+
/* log_init_lock assumed */
static int __write_to_statsd_initialize_locked() {
if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
@@ -143,7 +157,14 @@
}
save_errno = errno;
+#if defined(__ANDROID__)
clock_gettime(CLOCK_REALTIME, &ts);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+#endif
int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
errno = save_errno;
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 9953bba..88f7d44 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -15,8 +15,9 @@
*/
#include "statsd_writer.h"
+#include <cutils/fs.h>
#include <cutils/sockets.h>
-#include <endian.h>
+#include <cutils/threads.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -37,7 +38,16 @@
/* branchless on many architectures. */
#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+#ifndef htole32
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole32(x) (x)
+#else
+#define htole32(x) __bswap_32(x)
+#endif
+#endif
+
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static atomic_int dropped = 0;
void statsd_writer_init_lock() {
/*
@@ -59,14 +69,16 @@
static int statsdOpen();
static void statsdClose();
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+static void statsdNoteDrop();
struct android_log_transport_write statsdLoggerWrite = {
- .name = "statsd",
- .sock = -EBADF,
- .available = statsdAvailable,
- .open = statsdOpen,
- .close = statsdClose,
- .write = statsdWrite,
+ .name = "statsd",
+ .sock = -EBADF,
+ .available = statsdAvailable,
+ .open = statsdOpen,
+ .close = statsdClose,
+ .write = statsdWrite,
+ .noteDrop = statsdNoteDrop,
};
/* log_init_lock assumed */
@@ -75,7 +87,14 @@
i = atomic_load(&statsdLoggerWrite.sock);
if (i < 0) {
- int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ int flags = SOCK_DGRAM;
+#ifdef SOCK_CLOEXEC
+ flags |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+ flags |= SOCK_NONBLOCK;
+#endif
+ int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
if (sock < 0) {
ret = -errno;
} else {
@@ -131,6 +150,10 @@
return 1;
}
+static void statsdNoteDrop() {
+ atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+}
+
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
ssize_t ret;
int sock;
@@ -138,7 +161,6 @@
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
- static atomic_int dropped;
sock = atomic_load(&statsdLoggerWrite.sock);
if (sock < 0) switch (sock) {
@@ -252,8 +274,6 @@
if (ret > (ssize_t)sizeof(header)) {
ret -= sizeof(header);
- } else if (ret == -EAGAIN) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
}
return ret;
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 82e14e0..7289441 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -38,6 +38,8 @@
void (*close)(); /* free up resources */
/* write log to transport, returns number of bytes propagated, or -errno */
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+ /* note one log drop */
+ void (*noteDrop)();
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsystem/include/system/camera.h b/libsystem/include/system/camera.h
index 7d79673..2ca90c3 100644
--- a/libsystem/include/system/camera.h
+++ b/libsystem/include/system/camera.h
@@ -158,8 +158,8 @@
*
* When any camera method returns error, the client can use ping command
* to see if the camera has been taken away by other clients. If the result
- * is NO_ERROR, it means the camera hardware is not released. If the result
- * is not NO_ERROR, the camera has been released and the existing client
+ * is OK, it means the camera hardware is not released. If the result
+ * is not OK, the camera has been released and the existing client
* can silently finish itself or show a dialog.
*/
CAMERA_CMD_PING = 9,
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 24ea7aa..9dc2699 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -374,6 +374,7 @@
continue;
if (!if_indextoname(* (int *) RTA_DATA(rta), dev))
return false;
+ continue;
default:
continue;
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index be2145d..14f82c7 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -58,6 +58,7 @@
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
+ "Global.cpp",
"JitDebug.cpp",
"Log.cpp",
"MapInfo.cpp",
@@ -178,6 +179,7 @@
"tests/JitDebugTest.cpp",
"tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
+ "tests/MapInfoCreateMemoryTest.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
"tests/MapsTest.cpp",
@@ -188,6 +190,7 @@
"tests/MemoryOfflineBufferTest.cpp",
"tests/MemoryOfflineTest.cpp",
"tests/MemoryRangeTest.cpp",
+ "tests/MemoryRangesTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
"tests/RegsInfoTest.cpp",
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 430f6c5..451a0b9 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -43,10 +43,10 @@
uint64_t dex_file;
};
-DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {}
DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
- : memory_(memory), search_libs_(search_libs) {}
+ : Global(memory, search_libs) {}
DexFiles::~DexFiles() {
for (auto& entry : files_) {
@@ -54,8 +54,8 @@
}
}
-void DexFiles::SetArch(ArchEnum arch) {
- switch (arch) {
+void DexFiles::ProcessArch() {
+ switch (arch()) {
case ARCH_ARM:
case ARCH_MIPS:
case ARCH_X86:
@@ -117,6 +117,11 @@
return true;
}
+bool DexFiles::ReadVariableData(uint64_t ptr_offset) {
+ entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset);
+ return entry_addr_ != 0;
+}
+
void DexFiles::Init(Maps* maps) {
if (initialized_) {
return;
@@ -124,36 +129,7 @@
initialized_ = true;
entry_addr_ = 0;
- const std::string dex_debug_name("__dex_debug_descriptor");
- for (MapInfo* info : *maps) {
- if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
- continue;
- }
-
- if (!search_libs_.empty()) {
- bool found = false;
- const char* lib = basename(info->name.c_str());
- for (const std::string& name : search_libs_) {
- if (name == lib) {
- found = true;
- break;
- }
- }
- if (!found) {
- continue;
- }
- }
-
- Elf* elf = info->GetElf(memory_, true);
- uint64_t ptr;
- // Find first non-empty list (libart might be loaded multiple times).
- if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) {
- entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start);
- if (entry_addr_ != 0) {
- break;
- }
- }
- }
+ FindAndReadVariable(maps, "__dex_debug_descriptor");
}
DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4723606..4d72ead 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -40,7 +40,7 @@
std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
std::mutex* Elf::cache_lock_;
-bool Elf::Init(bool init_gnu_debugdata) {
+bool Elf::Init() {
load_bias_ = 0;
if (!memory_) {
return false;
@@ -54,11 +54,7 @@
valid_ = interface_->Init(&load_bias_);
if (valid_) {
interface_->InitHeaders(load_bias_);
- if (init_gnu_debugdata) {
- InitGnuDebugdata();
- } else {
- gnu_debugdata_interface_.reset(nullptr);
- }
+ InitGnuDebugdata();
} else {
interface_.reset(nullptr);
}
@@ -92,6 +88,11 @@
}
}
+void Elf::Invalidate() {
+ interface_.reset(nullptr);
+ valid_ = false;
+}
+
bool Elf::GetSoname(std::string* name) {
std::lock_guard<std::mutex> guard(lock_);
return valid_ && interface_->GetSoname(name);
@@ -194,26 +195,26 @@
return true;
}
-void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+bool Elf::GetInfo(Memory* memory, uint64_t* size) {
if (!IsValidElf(memory)) {
- *valid = false;
- return;
+ return false;
}
*size = 0;
- *valid = true;
- // Now read the section header information.
uint8_t class_type;
if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
- return;
+ return false;
}
+
+ // Get the maximum size of the elf data from the header.
if (class_type == ELFCLASS32) {
ElfInterface32::GetMaxSize(memory, size);
} else if (class_type == ELFCLASS64) {
ElfInterface64::GetMaxSize(memory, size);
} else {
- *valid = false;
+ return false;
}
+ return true;
}
bool Elf::IsValidPc(uint64_t pc) {
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
new file mode 100644
index 0000000..7a3de01
--- /dev/null
+++ b/libunwindstack/Global.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 <stdint.h>
+#include <sys/mman.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Global.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+Global::Global(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+ : memory_(memory), search_libs_(search_libs) {}
+
+void Global::SetArch(ArchEnum arch) {
+ if (arch_ == ARCH_UNKNOWN) {
+ arch_ = arch;
+ ProcessArch();
+ }
+}
+
+uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
+ if (!search_libs_.empty()) {
+ bool found = false;
+ const char* lib = basename(info->name.c_str());
+ for (const std::string& name : search_libs_) {
+ if (name == lib) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return 0;
+ }
+ }
+
+ Elf* elf = info->GetElf(memory_, arch());
+ uint64_t ptr;
+ // Find first non-empty list (libraries might be loaded multiple times).
+ if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) {
+ return ptr + info->start;
+ }
+ return 0;
+}
+
+void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
+ std::string variable(var_str);
+ // When looking for global variables, do not arbitrarily search every
+ // readable map. Instead look for a specific pattern that must exist.
+ // The pattern should be a readable map, followed by a read-write
+ // map with a non-zero offset.
+ // For example:
+ // f0000-f1000 0 r-- /system/lib/libc.so
+ // f1000-f2000 1000 r-x /system/lib/libc.so
+ // f2000-f3000 2000 rw- /system/lib/libc.so
+ // This also works:
+ // f0000-f2000 0 r-- /system/lib/libc.so
+ // f2000-f3000 2000 rw- /system/lib/libc.so
+ MapInfo* map_start = nullptr;
+ for (MapInfo* info : *maps) {
+ if (map_start != nullptr) {
+ if (map_start->name == info->name) {
+ if (info->offset != 0 &&
+ (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
+ uint64_t ptr = GetVariableOffset(map_start, variable);
+ if (ptr != 0 && ReadVariableData(ptr)) {
+ break;
+ } else {
+ // Failed to find the global variable, do not bother trying again.
+ map_start = nullptr;
+ }
+ }
+ } else {
+ map_start = nullptr;
+ }
+ }
+ if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
+ !info->name.empty()) {
+ map_start = info;
+ }
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 0c319ec..20bc4b9 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -69,10 +69,10 @@
uint64_t first_entry;
};
-JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {}
JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
- : memory_(memory), search_libs_(search_libs) {}
+ : Global(memory, search_libs) {}
JitDebug::~JitDebug() {
for (auto* elf : elf_list_) {
@@ -141,8 +141,8 @@
return code.next;
}
-void JitDebug::SetArch(ArchEnum arch) {
- switch (arch) {
+void JitDebug::ProcessArch() {
+ switch (arch()) {
case ARCH_X86:
read_descriptor_func_ = &JitDebug::ReadDescriptor32;
read_entry_func_ = &JitDebug::ReadEntry32Pack;
@@ -165,6 +165,11 @@
}
}
+bool JitDebug::ReadVariableData(uint64_t ptr) {
+ entry_addr_ = (this->*read_descriptor_func_)(ptr);
+ return entry_addr_ != 0;
+}
+
void JitDebug::Init(Maps* maps) {
if (initialized_) {
return;
@@ -172,37 +177,7 @@
// Regardless of what happens below, consider the init finished.
initialized_ = true;
- const std::string descriptor_name("__jit_debug_descriptor");
- for (MapInfo* info : *maps) {
- if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
- continue;
- }
-
- if (!search_libs_.empty()) {
- bool found = false;
- const char* lib = basename(info->name.c_str());
- for (std::string& name : search_libs_) {
- if (strcmp(name.c_str(), lib) == 0) {
- found = true;
- break;
- }
- }
- if (!found) {
- continue;
- }
- }
-
- Elf* elf = info->GetElf(memory_, true);
- uint64_t descriptor_addr;
- if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr)) {
- // Search for the first non-zero entry.
- descriptor_addr += info->start;
- entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr);
- if (entry_addr_ != 0) {
- break;
- }
- }
- }
+ FindAndReadVariable(maps, "__jit_debug_descriptor");
}
Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
@@ -226,7 +201,7 @@
entry_addr_ = (this->*read_entry_func_)(&start, &size);
Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
- elf->Init(true);
+ elf->Init();
if (!elf->valid()) {
// The data is not formatted in a way we understand, do not attempt
// to process any other entries.
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 952b332..5b2fadf 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -88,6 +88,7 @@
bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
unwindstack::RegsGetLocal(regs.get());
+ ArchEnum arch = regs->Arch();
size_t num_frames = 0;
bool adjust_pc = false;
@@ -100,7 +101,7 @@
break;
}
- Elf* elf = map_info->GetElf(process_memory_, true);
+ Elf* elf = map_info->GetElf(process_memory_, arch);
uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
uint64_t step_pc = rel_pc;
uint64_t pc_adjustment;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 39378a3..fe32b5e 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -53,10 +53,8 @@
return nullptr;
}
- bool valid;
uint64_t max_size;
- Elf::GetInfo(memory.get(), &valid, &max_size);
- if (!valid) {
+ if (!Elf::GetInfo(memory.get(), &max_size)) {
// Init as if the whole file is an elf.
if (memory->Init(name, 0)) {
elf_offset = offset;
@@ -102,10 +100,53 @@
if (!(flags & PROT_READ)) {
return nullptr;
}
- return new MemoryRange(process_memory, start, end - start, 0);
+
+ // Need to verify that this elf is valid. It's possible that
+ // only part of the elf file to be mapped into memory is in the executable
+ // map. In this case, there will be another read-only map that includes the
+ // first part of the elf file. This is done if the linker rosegment
+ // option is used.
+ std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
+ if (Elf::IsValidElf(memory.get())) {
+ return memory.release();
+ }
+
+ if (name.empty() || maps_ == nullptr) {
+ return nullptr;
+ }
+
+ // Find the read-only map that has the same name and has an offset closest
+ // to the current offset but less than the offset of the current map.
+ // For shared libraries, there should be a r-x map that has a non-zero
+ // offset and then a r-- map that has a zero offset.
+ // For shared libraries loaded from an apk, there should be a r-x map that
+ // has a non-zero offset and then a r-- map that has a non-zero offset less
+ // than the offset from the r-x map.
+ uint64_t closest_offset = 0;
+ MapInfo* ro_map_info = nullptr;
+ for (auto map_info : *maps_) {
+ if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
+ map_info->offset >= closest_offset) {
+ ro_map_info = map_info;
+ closest_offset = ro_map_info->offset;
+ }
+ }
+
+ if (ro_map_info != nullptr) {
+ // Make sure that relative pc values are corrected properly.
+ elf_offset = offset - closest_offset;
+
+ MemoryRanges* ranges = new MemoryRanges;
+ ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
+ ro_map_info->end - ro_map_info->start, 0));
+ ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+ return ranges;
+ }
+ return nullptr;
}
-Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
// Make sure no other thread is trying to add the elf to this map.
std::lock_guard<std::mutex> guard(mutex_);
@@ -134,7 +175,11 @@
elf.reset(new Elf(memory));
// If the init fails, keep the elf around as an invalid object so we
// don't try to reinit the object.
- elf->Init(init_gnu_debugdata);
+ elf->Init();
+ if (elf->valid() && expected_arch != elf->arch()) {
+ // Make the elf invalid, mismatch between arch and expected arch.
+ elf->Invalidate();
+ }
if (locked) {
Elf::CacheAdd(this);
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index e676a5a..8729871 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -66,13 +66,13 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+ maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name, uint64_t load_bias) {
- MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+ MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
map_info->load_bias = load_bias;
maps_.push_back(map_info);
}
@@ -97,7 +97,7 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+ maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
});
}
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index beb2aad..cfa8c6d 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -316,6 +316,18 @@
return memory_->Read(read_addr, dst, read_length);
}
+void MemoryRanges::Insert(MemoryRange* memory) {
+ maps_.emplace(memory->offset() + memory->length(), memory);
+}
+
+size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
+ auto entry = maps_.upper_bound(addr);
+ if (entry != maps_.end()) {
+ return entry->second->Read(addr, dst, size);
+ }
+ return 0;
+}
+
bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
auto memory_file = std::make_shared<MemoryFileAtOffset>();
if (!memory_file->Init(file, offset)) {
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index e6dd33c..e445a91 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -41,7 +41,7 @@
}
inline AddressType* Save(uint32_t reg) {
- if (reg > MAX_REGISTERS) {
+ if (reg >= MAX_REGISTERS) {
// This should never happen since all currently supported
// architectures have < 64 total registers.
abort();
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 099cc9e..b3c5549 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -135,6 +135,8 @@
last_error_.code = ERROR_NONE;
last_error_.address = 0;
+ ArchEnum arch = regs_->Arch();
+
bool return_address_attempt = false;
bool adjust_pc = false;
std::unique_ptr<JitDebug> jit_debug;
@@ -155,7 +157,7 @@
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
}
- elf = map_info->GetElf(process_memory_, true);
+ elf = map_info->GetElf(process_memory_, arch);
step_pc = regs_->pc();
rel_pc = elf->GetRelPc(step_pc, map_info);
// Everyone except elf data in gdb jit debug maps uses the relative pc.
@@ -190,6 +192,12 @@
FillInDexFrame();
// Clear the dex pc so that we don't repeat this frame later.
regs_->set_dex_pc(0);
+
+ // Make sure there is enough room for the real frame.
+ if (frames_.size() == max_frames_) {
+ last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+ break;
+ }
}
FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 26f5d35..c202a33 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -25,16 +25,18 @@
#include <unordered_map>
#include <vector>
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
namespace unwindstack {
// Forward declarations.
class DexFile;
class Maps;
struct MapInfo;
-class Memory;
enum ArchEnum : uint8_t;
-class DexFiles {
+class DexFiles : public Global {
public:
explicit DexFiles(std::shared_ptr<Memory>& memory);
DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
@@ -45,8 +47,6 @@
void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
uint64_t* method_offset);
- void SetArch(ArchEnum arch);
-
private:
void Init(Maps* maps);
@@ -60,8 +60,9 @@
bool ReadEntry64();
- std::shared_ptr<Memory> memory_;
- std::vector<std::string> search_libs_;
+ bool ReadVariableData(uint64_t ptr_offset) override;
+
+ void ProcessArch() override;
std::mutex lock_;
bool initialized_ = false;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index f4cdbda..e5b0a89 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -53,10 +53,12 @@
Elf(Memory* memory) : memory_(memory) {}
virtual ~Elf() = default;
- bool Init(bool init_gnu_debugdata);
+ bool Init();
void InitGnuDebugdata();
+ void Invalidate();
+
bool GetSoname(std::string* name);
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
@@ -94,7 +96,7 @@
static bool IsValidElf(Memory* memory);
- static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+ static bool GetInfo(Memory* memory, uint64_t* size);
static uint64_t GetLoadBias(Memory* memory);
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
new file mode 100644
index 0000000..a7e6c15
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_GLOBAL_H
+#define _LIBUNWINDSTACK_GLOBAL_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Maps;
+struct MapInfo;
+
+class Global {
+ public:
+ explicit Global(std::shared_ptr<Memory>& memory);
+ Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+ virtual ~Global() = default;
+
+ void SetArch(ArchEnum arch);
+
+ ArchEnum arch() { return arch_; }
+
+ protected:
+ uint64_t GetVariableOffset(MapInfo* info, const std::string& variable);
+ void FindAndReadVariable(Maps* maps, const char* variable);
+
+ virtual bool ReadVariableData(uint64_t offset) = 0;
+
+ virtual void ProcessArch() = 0;
+
+ ArchEnum arch_ = ARCH_UNKNOWN;
+
+ std::shared_ptr<Memory> memory_;
+ std::vector<std::string> search_libs_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_GLOBAL_H
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index 0bcd0b0..f64b04f 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -24,15 +24,17 @@
#include <string>
#include <vector>
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
namespace unwindstack {
// Forward declarations.
class Elf;
class Maps;
-class Memory;
enum ArchEnum : uint8_t;
-class JitDebug {
+class JitDebug : public Global {
public:
explicit JitDebug(std::shared_ptr<Memory>& memory);
JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
@@ -40,19 +42,9 @@
Elf* GetElf(Maps* maps, uint64_t pc);
- void SetArch(ArchEnum arch);
-
private:
void Init(Maps* maps);
- std::shared_ptr<Memory> memory_;
- uint64_t entry_addr_ = 0;
- bool initialized_ = false;
- std::vector<Elf*> elf_list_;
- std::vector<std::string> search_libs_;
-
- std::mutex lock_;
-
uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
@@ -62,6 +54,16 @@
uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
+
+ bool ReadVariableData(uint64_t ptr_offset) override;
+
+ void ProcessArch() override;
+
+ uint64_t entry_addr_ = 0;
+ bool initialized_ = false;
+ std::vector<Elf*> elf_list_;
+
+ std::mutex lock_;
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index ac0df41..9c6b552 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -29,20 +29,25 @@
namespace unwindstack {
// Forward declarations.
+class Maps;
class Memory;
struct MapInfo {
- MapInfo() = default;
- MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
- MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
- : start(start),
+ MapInfo(Maps* maps) : maps_(maps) {}
+ MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
+ MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name)
+ : maps_(maps),
+ start(start),
end(end),
offset(offset),
flags(flags),
name(name),
load_bias(static_cast<uint64_t>(-1)) {}
- MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
- : start(start),
+ MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const std::string& name)
+ : maps_(maps),
+ start(start),
end(end),
offset(offset),
flags(flags),
@@ -50,6 +55,8 @@
load_bias(static_cast<uint64_t>(-1)) {}
~MapInfo() = default;
+ Maps* maps_ = nullptr;
+
uint64_t start = 0;
uint64_t end = 0;
uint64_t offset = 0;
@@ -65,18 +72,18 @@
std::atomic_uint64_t load_bias;
// This function guarantees it will never return nullptr.
- Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+ Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
+ Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
private:
MapInfo(const MapInfo&) = delete;
void operator=(const MapInfo&) = delete;
Memory* GetFileMemory();
- Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
-
// Protect the creation of the elf object.
std::mutex mutex_;
};
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dee5e98..9c425cb 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -22,6 +22,7 @@
#include <unistd.h>
#include <atomic>
+#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -119,6 +120,9 @@
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_;
@@ -126,6 +130,19 @@
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;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 4dd8cb0..40f9f8e 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -120,7 +120,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -134,7 +134,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -148,7 +148,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -156,7 +156,7 @@
TEST(DexFileTest, create_using_memory_empty_file) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -164,7 +164,7 @@
TEST(DexFileTest, create_using_memory_file_does_not_exist) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -178,7 +178,7 @@
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
@@ -200,7 +200,7 @@
TEST(DexFileTest, get_method) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
@@ -227,7 +227,7 @@
TEST(DexFileTest, get_method_empty) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index d029bb0..1ea9e5c 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -36,57 +36,54 @@
class DexFilesTest : public ::testing::Test {
protected:
- void SetUp() override {
- memory_ = new MemoryFake;
- process_memory_.reset(memory_);
-
- dex_files_.reset(new DexFiles(process_memory_));
- dex_files_->SetArch(ARCH_ARM);
-
- maps_.reset(
- new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
- "4000-6000 r--s 00000000 00:00 0\n"
- "6000-8000 -w-s 00000000 00:00 0\n"
- "a000-c000 r-xp 00000000 00:00 0\n"
- "c000-f000 rwxp 00000000 00:00 0\n"
- "f000-11000 r-xp 00000000 00:00 0\n"
- "100000-110000 rw-p 0000000 00:00 0\n"
- "200000-210000 rw-p 0000000 00:00 0\n"
- "300000-400000 rw-p 0000000 00:00 0\n"));
- ASSERT_TRUE(maps_->Parse());
-
- // Global variable in a section that is not readable/executable.
- MapInfo* map_info = maps_->Get(kMapGlobalNonReadableExectable);
- ASSERT_TRUE(map_info != nullptr);
+ void CreateFakeElf(MapInfo* map_info) {
MemoryFake* memory = new MemoryFake;
ElfFake* elf = new ElfFake(memory);
elf->FakeSetValid(true);
ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
+
interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
map_info->elf.reset(elf);
+ }
+
+ void Init(ArchEnum arch) {
+ dex_files_.reset(new DexFiles(process_memory_));
+ dex_files_->SetArch(arch);
+
+ maps_.reset(
+ new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
+ "4000-6000 r--s 00000000 00:00 0 /fake/elf\n"
+ "6000-8000 -wxs 00000000 00:00 0 /fake/elf\n"
+ "a000-c000 r--p 00000000 00:00 0 /fake/elf2\n"
+ "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+ "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+ "100000-110000 rw-p 0001000 00:00 0 /fake/elf3\n"
+ "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
+ "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
+ ASSERT_TRUE(maps_->Parse());
+
+ // Global variable in a section that is not readable.
+ MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info);
// Global variable not set by default.
map_info = maps_->Get(kMapGlobalSetToZero);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
// Global variable set in this map.
map_info = maps_->Get(kMapGlobal);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ Init(ARCH_ARM);
}
void WriteDescriptor32(uint64_t addr, uint32_t head);
@@ -95,9 +92,10 @@
void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
void WriteDex(uint64_t dex_file);
- static constexpr size_t kMapGlobalNonReadableExectable = 3;
- static constexpr size_t kMapGlobalSetToZero = 4;
+ static constexpr size_t kMapGlobalNonReadable = 2;
+ static constexpr size_t kMapGlobalSetToZero = 3;
static constexpr size_t kMapGlobal = 5;
+ static constexpr size_t kMapGlobalRw = 6;
static constexpr size_t kMapDexFileEntries = 7;
static constexpr size_t kMapDexFiles = 8;
@@ -168,11 +166,12 @@
}
TEST_F(DexFilesTest, get_method_information_64) {
+ Init(ARCH_ARM64);
+
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- dex_files_->SetArch(ARCH_ARM64);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x301000);
WriteDex(0x301000);
@@ -198,11 +197,12 @@
}
TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+ Init(ARCH_ARM64);
+
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- dex_files_->SetArch(ARCH_ARM64);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0x200100, 0, 0x100000);
WriteEntry64(0x200100, 0, 0x200000, 0x300000);
@@ -256,6 +256,9 @@
map_info->name = "/system/lib/libart.so";
dex_files_.reset(new DexFiles(process_memory_, libs));
dex_files_->SetArch(ARCH_ARM);
+ // Set the rw map to the same name or this will not scan this entry.
+ map_info = maps_->Get(kMapGlobalRw);
+ map_info->name = "/system/lib/libart.so";
// Make sure that clearing out copy of the libs doesn't affect the
// DexFiles object.
libs.clear();
@@ -271,7 +274,7 @@
MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
- WriteDescriptor32(0xc800, 0);
+ WriteDescriptor32(0xa800, 0);
WriteDescriptor32(0xf800, 0x200000);
WriteEntry32(0x200000, 0, 0, 0x300000);
@@ -286,25 +289,26 @@
dex_files_->SetArch(ARCH_ARM);
method_name = "fail";
method_offset = 0x123;
- WriteDescriptor32(0xc800, 0x100000);
+ WriteDescriptor32(0xa800, 0x100000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+ Init(ARCH_ARM64);
+
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
- WriteDescriptor64(0xc800, 0);
+ WriteDescriptor64(0xa800, 0);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
- dex_files_->SetArch(ARCH_ARM64);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
@@ -314,7 +318,7 @@
dex_files_->SetArch(ARCH_ARM64);
method_name = "fail";
method_offset = 0x123;
- WriteDescriptor64(0xc800, 0x100000);
+ WriteDescriptor64(0xa800, 0x100000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 89331ea..d9acdec 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -79,12 +79,12 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
- MapInfo info1(start, end, 0, 0x5, tf.path);
- MapInfo info2(start, end, 0, 0x5, tf.path);
+ MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
- Elf* elf1 = info1.GetElf(memory_, true);
+ Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf1->valid());
- Elf* elf2 = info2.GetElf(memory_, true);
+ Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf2->valid());
if (cache_enabled) {
@@ -120,22 +120,22 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Will have an elf at offset 0 in file.
- MapInfo info0_1(start, end, 0, 0x5, tf.path);
- MapInfo info0_2(start, end, 0, 0x5, tf.path);
+ MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
// Will have an elf at offset 0x100 in file.
- MapInfo info100_1(start, end, 0x100, 0x5, tf.path);
- MapInfo info100_2(start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
// Will have an elf at offset 0x200 in file.
- MapInfo info200_1(start, end, 0x200, 0x5, tf.path);
- MapInfo info200_2(start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
// Will have an elf at offset 0 in file.
- MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
- Elf* elf0_1 = info0_1.GetElf(memory_, true);
+ Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf0_1->valid());
EXPECT_EQ(ARCH_ARM, elf0_1->arch());
- Elf* elf0_2 = info0_2.GetElf(memory_, true);
+ Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf0_2->valid());
EXPECT_EQ(ARCH_ARM, elf0_2->arch());
EXPECT_EQ(0U, info0_1.elf_offset);
@@ -146,10 +146,10 @@
EXPECT_NE(elf0_1, elf0_2);
}
- Elf* elf100_1 = info100_1.GetElf(memory_, true);
+ Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
ASSERT_TRUE(elf100_1->valid());
EXPECT_EQ(ARCH_X86, elf100_1->arch());
- Elf* elf100_2 = info100_2.GetElf(memory_, true);
+ Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
ASSERT_TRUE(elf100_2->valid());
EXPECT_EQ(ARCH_X86, elf100_2->arch());
EXPECT_EQ(0U, info100_1.elf_offset);
@@ -160,10 +160,10 @@
EXPECT_NE(elf100_1, elf100_2);
}
- Elf* elf200_1 = info200_1.GetElf(memory_, true);
+ Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
ASSERT_TRUE(elf200_1->valid());
EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
- Elf* elf200_2 = info200_2.GetElf(memory_, true);
+ Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
ASSERT_TRUE(elf200_2->valid());
EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
EXPECT_EQ(0U, info200_1.elf_offset);
@@ -174,10 +174,10 @@
EXPECT_NE(elf200_1, elf200_2);
}
- Elf* elf300_1 = info300_1.GetElf(memory_, true);
+ Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_1->valid());
EXPECT_EQ(ARCH_ARM, elf300_1->arch());
- Elf* elf300_2 = info300_2.GetElf(memory_, true);
+ Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_2->valid());
EXPECT_EQ(ARCH_ARM, elf300_2->arch());
EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -217,15 +217,15 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Multiple info sections at different offsets will have non-zero elf offsets.
- MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
- MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
- MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+ MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
+ MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
- Elf* elf300_1 = info300_1.GetElf(memory_, true);
+ Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_1->valid());
EXPECT_EQ(ARCH_ARM, elf300_1->arch());
- Elf* elf300_2 = info300_2.GetElf(memory_, true);
+ Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_2->valid());
EXPECT_EQ(ARCH_ARM, elf300_2->arch());
EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -236,10 +236,10 @@
EXPECT_NE(elf300_1, elf300_2);
}
- Elf* elf400_1 = info400_1.GetElf(memory_, true);
+ Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf400_1->valid());
EXPECT_EQ(ARCH_ARM, elf400_1->arch());
- Elf* elf400_2 = info400_2.GetElf(memory_, true);
+ Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf400_2->valid());
EXPECT_EQ(ARCH_ARM, elf400_2->arch());
EXPECT_EQ(0x400U, info400_1.elf_offset);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 55fe16f..ccf8927 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -110,7 +110,7 @@
TEST_F(ElfTest, invalid_memory) {
Elf elf(memory_);
- ASSERT_FALSE(elf.Init(false));
+ ASSERT_FALSE(elf.Init());
ASSERT_FALSE(elf.valid());
}
@@ -122,7 +122,7 @@
// Corrupt the ELF signature.
memory_->SetData32(0, 0x7f000000);
- ASSERT_FALSE(elf.Init(false));
+ ASSERT_FALSE(elf.Init());
ASSERT_FALSE(elf.valid());
ASSERT_TRUE(elf.interface() == nullptr);
@@ -142,7 +142,7 @@
InitElf32(EM_PPC);
ResetLogs();
- ASSERT_FALSE(elf.Init(false));
+ ASSERT_FALSE(elf.Init());
ASSERT_EQ("", GetFakeLogBuf());
ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
@@ -155,7 +155,7 @@
InitElf64(EM_PPC64);
ResetLogs();
- ASSERT_FALSE(elf.Init(false));
+ ASSERT_FALSE(elf.Init());
ASSERT_EQ("", GetFakeLogBuf());
ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
@@ -167,7 +167,7 @@
InitElf32(EM_ARM);
- ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.valid());
ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -179,7 +179,7 @@
InitElf32(EM_MIPS);
- ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.valid());
ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -191,7 +191,7 @@
InitElf32(EM_386);
- ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.valid());
ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -203,7 +203,7 @@
InitElf64(EM_AARCH64);
- ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.valid());
ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -215,7 +215,7 @@
InitElf64(EM_X86_64);
- ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.valid());
ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -227,41 +227,13 @@
InitElf64(EM_MIPS);
- ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.valid());
ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
ASSERT_EQ(ELFCLASS64, elf.class_type());
ASSERT_TRUE(elf.interface() != nullptr);
}
-TEST_F(ElfTest, gnu_debugdata_init_fail32) {
- TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
- [&](uint64_t offset, const void* ptr, size_t size) {
- memory_->SetMemory(offset, ptr, size);
- });
-
- Elf elf(memory_);
- ASSERT_TRUE(elf.Init(false));
- ASSERT_TRUE(elf.interface() != nullptr);
- ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
- EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
- EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
-TEST_F(ElfTest, gnu_debugdata_init_fail64) {
- TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
- [&](uint64_t offset, const void* ptr, size_t size) {
- memory_->SetMemory(offset, ptr, size);
- });
-
- Elf elf(memory_);
- ASSERT_TRUE(elf.Init(false));
- ASSERT_TRUE(elf.interface() != nullptr);
- ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
- EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
- EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
TEST_F(ElfTest, gnu_debugdata_init32) {
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -269,7 +241,7 @@
});
Elf elf(memory_);
- ASSERT_TRUE(elf.Init(true));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.interface() != nullptr);
ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
@@ -283,7 +255,7 @@
});
Elf elf(memory_);
- ASSERT_TRUE(elf.Init(true));
+ ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.interface() != nullptr);
ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
@@ -297,7 +269,7 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- MapInfo map_info(0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index c1c45f8..b1ca111 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -35,27 +35,7 @@
class JitDebugTest : public ::testing::Test {
protected:
- void SetUp() override {
- memory_ = new MemoryFake;
- process_memory_.reset(memory_);
-
- jit_debug_.reset(new JitDebug(process_memory_));
- jit_debug_->SetArch(ARCH_ARM);
-
- maps_.reset(
- new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
- "4000-6000 r--s 00000000 00:00 0\n"
- "6000-8000 -w-s 00000000 00:00 0\n"
- "a000-c000 --xp 00000000 00:00 0\n"
- "c000-f000 rwxp 00000000 00:00 0\n"
- "f000-11000 r-xp 00000000 00:00 0\n"
- "12000-14000 r-xp 00000000 00:00 0\n"
- "100000-110000 rw-p 0000000 00:00 0\n"
- "200000-210000 rw-p 0000000 00:00 0\n"));
- ASSERT_TRUE(maps_->Parse());
-
- MapInfo* map_info = maps_->Get(3);
- ASSERT_TRUE(map_info != nullptr);
+ void CreateFakeElf(MapInfo* map_info) {
MemoryFake* memory = new MemoryFake;
ElfFake* elf = new ElfFake(memory);
elf->FakeSetValid(true);
@@ -63,26 +43,43 @@
elf->FakeSetInterface(interface);
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
map_info->elf.reset(elf);
+ }
+
+ void Init(ArchEnum arch) {
+ jit_debug_.reset(new JitDebug(process_memory_));
+ jit_debug_->SetArch(arch);
+
+ maps_.reset(
+ new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
+ "4000-6000 r--s 00000000 00:00 0 /fake/elf1\n"
+ "6000-8000 -wxs 00000000 00:00 0 /fake/elf1\n"
+ "a000-c000 --xp 00000000 00:00 0 /fake/elf2\n"
+ "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+ "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+ "11000-12000 rw-p 00001000 00:00 0 /fake/elf3\n"
+ "12000-14000 r--p 00000000 00:00 0 /fake/elf4\n"
+ "100000-110000 rw-p 0001000 00:00 0 /fake/elf4\n"
+ "200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n"));
+ ASSERT_TRUE(maps_->Parse());
+
+ MapInfo* map_info = maps_->Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info);
map_info = maps_->Get(5);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
- map_info = maps_->Get(6);
+ map_info = maps_->Get(7);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ Init(ARCH_ARM);
}
template <typename EhdrType, typename ShdrType>
@@ -325,6 +322,8 @@
}
TEST_F(JitDebugTest, get_elf_x86) {
+ Init(ARCH_X86);
+
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
WriteDescriptor32(0xf800, 0x200000);
@@ -342,12 +341,13 @@
}
TEST_F(JitDebugTest, get_elf_64) {
+ Init(ARCH_ARM64);
+
CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
- jit_debug_->SetArch(ARCH_ARM64);
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
ASSERT_TRUE(elf != nullptr);
@@ -397,6 +397,8 @@
// Change the name of the map that includes the value and verify this works.
MapInfo* map_info = maps_->Get(5);
map_info->name = "/system/lib/libart.so";
+ map_info = maps_->Get(6);
+ map_info->name = "/system/lib/libart.so";
jit_debug_.reset(new JitDebug(process_memory_, libs));
// Make sure that clearing our copy of the libs doesn't affect the
// JitDebug object.
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 866b5b4..2a73c7e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -32,8 +32,10 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
+#include "ElfTestUtils.h"
#include "MemoryFake.h"
namespace unwindstack {
@@ -94,7 +96,7 @@
TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
- MapInfo info(0x100, 0x100, 0, 0, elf_.path);
+ MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
@@ -112,7 +114,7 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
+ MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -133,7 +135,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
+ MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -156,7 +158,7 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+ MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -172,7 +174,7 @@
}
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -192,27 +194,24 @@
// Set up some memory so that a valid local memory object would
// be returned if the file mapping fails, but the device check is incorrect.
std::vector<uint8_t> buffer(1024);
- MapInfo info;
- info.start = reinterpret_cast<uint64_t>(buffer.data());
- info.end = info.start + buffer.size();
- info.offset = 0;
+ uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
+ MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
- info.flags = 0x8000;
- info.name = "/dev/something";
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
}
TEST_F(MapInfoCreateMemoryTest, process_memory) {
- MapInfo info;
- info.start = 0x2000;
- info.end = 0x3000;
- info.offset = 0;
+ MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ std::vector<uint8_t> buffer(1024);
+ memcpy(buffer.data(), &ehdr, sizeof(ehdr));
// Verify that the the process_memory object is used, so seed it
// with memory.
- std::vector<uint8_t> buffer(1024);
- for (size_t i = 0; i < buffer.size(); i++) {
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
buffer[i] = i % 256;
}
memory_->SetMemory(info.start, buffer.data(), buffer.size());
@@ -222,7 +221,8 @@
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
- for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
}
@@ -230,4 +230,87 @@
ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
}
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2600, 0, PROT_READ, "/only/in/memory.so", 0);
+ maps.Add(0x3000, 0x5000, 0x4000, PROT_READ | PROT_EXEC, "/only/in/memory.so", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x1600 - sizeof(ehdr), 0xab);
+
+ // Set the memory in the r-x map.
+ memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
+
+ MapInfo* map_info = maps.Find(0x3000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_EQ(0x4000UL, map_info->elf_offset);
+ EXPECT_EQ(0x4000UL, map_info->offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1600UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0xab, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x4000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x5d, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+ maps.Add(0x3000, 0x4000, 0xa000, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x4000, 0x5000, 0xb000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+ // Setup an elf at offset 0x1000 in memory.
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x2000 - sizeof(ehdr), 0x12);
+ memory_->SetMemoryBlock(0x2000, 0x1000, 0x23);
+
+ // Setup an elf at offset 0x3000 in memory..
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
+ memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
+
+ MapInfo* map_info = maps.Find(0x4000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_EQ(0x1000UL, map_info->elf_offset);
+ EXPECT_EQ(0xb000UL, map_info->offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x1000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x43, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 861b82f..4d74696 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -69,22 +69,22 @@
};
TEST_F(MapInfoGetElfTest, invalid) {
- MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
// The map is empty, but this should still create an invalid elf object.
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, valid32) {
- MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -92,60 +92,40 @@
}
TEST_F(MapInfoGetElfTest, valid64) {
- MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
EXPECT_EQ(ELFCLASS64, elf->class_type());
}
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
- MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
+TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
+ MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
- TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
- [&](uint64_t offset, const void* ptr, size_t size) {
- memory_->SetMemory(0x4000 + offset, ptr, size);
- });
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86);
ASSERT_TRUE(elf != nullptr);
- ASSERT_TRUE(elf->valid());
- EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
- EXPECT_EQ(ELFCLASS32, elf->class_type());
- EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
-}
-
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
- MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
-
- TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
- [&](uint64_t offset, const void* ptr, size_t size) {
- memory_->SetMemory(0x6000 + offset, ptr, size);
- });
-
- Elf* elf = info.GetElf(process_memory_, false);
- ASSERT_TRUE(elf != nullptr);
- ASSERT_TRUE(elf->valid());
- EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
- EXPECT_EQ(ELFCLASS64, elf->class_type());
- EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+ ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
- MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x2000 + offset, ptr, size);
});
- Elf* elf = info.GetElf(process_memory_, true);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -154,14 +134,14 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
- MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x5000 + offset, ptr, size);
});
- Elf* elf = info.GetElf(process_memory_, true);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -170,26 +150,26 @@
}
TEST_F(MapInfoGetElfTest, end_le_start) {
- MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
info.elf.reset();
info.end = 0xfff;
- elf = info.GetElf(process_memory_, false);
+ elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Make sure this test is valid.
info.elf.reset();
info.end = 0x2000;
- elf = info.GetElf(process_memory_, false);
+ elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
}
@@ -197,7 +177,7 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x1000);
memset(buffer.data(), 0, buffer.size());
@@ -206,7 +186,7 @@
memcpy(buffer.data(), &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -226,7 +206,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -235,7 +215,7 @@
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -256,7 +236,7 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -268,7 +248,7 @@
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -284,7 +264,7 @@
}
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -296,7 +276,7 @@
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -312,7 +292,7 @@
}
TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
- MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
+ MapInfo info(nullptr, 0x9000, 0xa000, 0x1000, 0, "");
// Create valid elf data in process memory only.
Elf64_Ehdr ehdr;
@@ -322,18 +302,19 @@
ehdr.e_shnum = 0;
memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
info.elf.reset();
info.flags = PROT_READ;
- elf = info.GetElf(process_memory_, false);
+ elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf->valid());
}
TEST_F(MapInfoGetElfTest, check_device_maps) {
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+ "/dev/something");
// Create valid elf data in process memory for this to verify that only
// the name is causing invalid elf data.
@@ -344,20 +325,20 @@
ehdr.e_shnum = 0;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Set the name to nothing to verify that it still fails.
info.elf.reset();
info.name = "";
- elf = info.GetElf(process_memory_, false);
+ elf = info.GetElf(process_memory_, ARCH_X86_64);
ASSERT_FALSE(elf->valid());
// Change the flags and verify the elf is valid now.
info.elf.reset();
info.flags = PROT_READ;
- elf = info.GetElf(process_memory_, false);
+ elf = info.GetElf(process_memory_, ARCH_X86_64);
ASSERT_TRUE(elf->valid());
}
@@ -378,12 +359,12 @@
wait = true;
// Create all of the threads and have them do the GetElf at the same time
// to make it likely that a race will occur.
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
while (wait)
;
- Elf* elf = info.GetElf(process_memory_, false);
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
elf_in_threads[i] = elf;
});
threads.push_back(thread);
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 7e64a8a..f5ac6cb 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
process_memory_.reset(memory_);
elf_ = new ElfFake(new MemoryFake);
elf_container_.reset(elf_);
- map_info_.reset(new MapInfo(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+ map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
}
void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
};
TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
- MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
}
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9622ba5..6bdd0b2 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -63,7 +63,7 @@
}
TEST(MapsTest, verify_parse_line) {
- MapInfo info;
+ MapInfo info(nullptr);
VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
EXPECT_EQ(1U, info.start);
@@ -136,7 +136,7 @@
}
TEST(MapsTest, verify_large_values) {
- MapInfo info;
+ MapInfo info(nullptr);
#if defined(__LP64__)
VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index 60936cd..5695dfc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -23,6 +23,17 @@
namespace unwindstack {
+void MemoryFake::SetMemoryBlock(uint64_t addr, size_t length, uint8_t value) {
+ for (size_t i = 0; i < length; i++, addr++) {
+ auto entry = data_.find(addr);
+ if (entry != data_.end()) {
+ entry->second = value;
+ } else {
+ data_.insert({addr, value});
+ }
+ }
+}
+
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
for (size_t i = 0; i < length; i++, addr++) {
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 764a6c3..20610a5 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -36,6 +36,8 @@
void SetMemory(uint64_t addr, const void* memory, size_t length);
+ void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
+
void SetData8(uint64_t addr, uint8_t value) {
SetMemory(addr, &value, sizeof(value));
}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index cb1a0c9..2bac95b 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -15,7 +15,6 @@
*/
#include <stdint.h>
-#include <string.h>
#include <memory>
#include <vector>
@@ -28,30 +27,34 @@
namespace unwindstack {
-TEST(MemoryRangeTest, read) {
- std::vector<uint8_t> src(1024);
- memset(src.data(), 0x4c, 1024);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(9001, src);
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ process_memory_.reset();
+ memory_fake_ = new MemoryFake;
+ process_memory_.reset(memory_fake_);
+ }
- MemoryRange range(process_memory, 9001, src.size(), 0);
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_fake_ = nullptr;
+};
+
+TEST_F(MemoryRangeTest, read_fully) {
+ memory_fake_->SetMemoryBlock(9000, 2048, 0x4c);
+
+ MemoryRange range(process_memory_, 9001, 1024, 0);
std::vector<uint8_t> dst(1024);
- ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
- for (size_t i = 0; i < 1024; i++) {
+ ASSERT_TRUE(range.ReadFully(0, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
}
-TEST(MemoryRangeTest, read_near_limit) {
- std::vector<uint8_t> src(4096);
- memset(src.data(), 0x4c, 4096);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read_fully_near_limit) {
+ memory_fake_->SetMemoryBlock(0, 8192, 0x4c);
- MemoryRange range(process_memory, 1000, 1024, 0);
+ MemoryRange range(process_memory_, 1000, 1024, 0);
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
@@ -68,7 +71,7 @@
ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
}
-TEST(MemoryRangeTest, read_overflow) {
+TEST_F(MemoryRangeTest, read_fully_overflow) {
std::vector<uint8_t> buffer(100);
std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
@@ -76,19 +79,28 @@
ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
}
-TEST(MemoryRangeTest, Read) {
- std::vector<uint8_t> src(4096);
- memset(src.data(), 0x4c, 4096);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read) {
+ memory_fake_->SetMemoryBlock(0, 4096, 0x4c);
- MemoryRange range(process_memory, 1000, 1024, 0);
+ MemoryRange range(process_memory_, 1000, 1024, 0);
+
std::vector<uint8_t> dst(1024);
- ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+ ASSERT_EQ(4U, range.Read(1020, dst.data(), dst.size()));
for (size_t i = 0; i < 4; i++) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
}
+TEST_F(MemoryRangeTest, read_non_zero_offset) {
+ memory_fake_->SetMemoryBlock(1000, 1024, 0x12);
+
+ MemoryRange range(process_memory_, 1000, 1024, 400);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_EQ(1024U, range.Read(400, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
+ ASSERT_EQ(0x12U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
new file mode 100644
index 0000000..d24fcd2
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryRangesTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ MemoryFake* memory = new MemoryFake;
+ process_memory_.reset(memory);
+ memory->SetMemoryBlock(1000, 5000, 0x15);
+ memory->SetMemoryBlock(6000, 12000, 0x26);
+ memory->SetMemoryBlock(14000, 20000, 0x37);
+ memory->SetMemoryBlock(20000, 22000, 0x48);
+
+ ranges_.reset(new MemoryRanges);
+ ranges_->Insert(new MemoryRange(process_memory_, 15000, 100, 4000));
+ ranges_->Insert(new MemoryRange(process_memory_, 10000, 2000, 2000));
+ ranges_->Insert(new MemoryRange(process_memory_, 3000, 1000, 0));
+ ranges_->Insert(new MemoryRange(process_memory_, 19000, 1000, 6000));
+ ranges_->Insert(new MemoryRange(process_memory_, 20000, 1000, 7000));
+ }
+
+ std::shared_ptr<Memory> process_memory_;
+ std::unique_ptr<MemoryRanges> ranges_;
+};
+
+TEST_F(MemoryRangesTest, read) {
+ std::vector<uint8_t> dst(2000);
+ size_t bytes = ranges_->Read(0, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x15U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(2000, dst.data(), dst.size());
+ ASSERT_EQ(2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x26U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(4000, dst.data(), dst.size());
+ ASSERT_EQ(100UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryRangesTest, read_fail) {
+ std::vector<uint8_t> dst(4096);
+ ASSERT_EQ(0UL, ranges_->Read(1000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(5000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(8000, dst.data(), dst.size()));
+}
+
+TEST_F(MemoryRangesTest, read_across_ranges) {
+ // The MemoryRanges object does not support reading across a range,
+ // so this will only read in the first range.
+ std::vector<uint8_t> dst(4096);
+ size_t bytes = ranges_->Read(6000, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
index 052b5bf..a6bc2c5 100644
--- a/libunwindstack/tests/RegsInfoTest.cpp
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -82,4 +82,11 @@
}
}
+TEST(RegsInfoTest, invalid_register) {
+ RegsImplFake<uint64_t> regs(64);
+ RegsInfo<uint64_t> info(®s);
+
+ EXPECT_DEATH(info.Save(RegsInfo<uint64_t>::MAX_REGISTERS), "");
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 90c3fe6..00264c2 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
RegsX86_64 regs_x86_64;
RegsMips regs_mips;
RegsMips64 regs_mips64;
- MapInfo map_info(0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000);
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2428f68..831d3b5 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -58,51 +58,54 @@
protected:
static void SetUpTestCase() {
maps_.FakeClear();
- MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+ MapInfo* info =
+ new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
ElfFake* elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
+ info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+ info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
"/dev/fake_device");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
+ info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
+ "/system/fake/libunwind.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
+ info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
+ info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
+ info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
+ info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/fake.vdex");
info->load_bias = 0;
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/fake/fake_load_bias.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
@@ -110,7 +113,7 @@
elf->FakeSetLoadBias(0x5000);
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/fake/fake_offset.oat");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
@@ -943,6 +946,33 @@
EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
}
+TEST_F(UnwinderTest, dex_pc_max_frames) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xa3400);
+
+ Unwinder unwinder(1, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xa3400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0xa3000U, frame->map_start);
+ EXPECT_EQ(0xa4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
// Verify format frame code.
TEST_F(UnwinderTest, format_frame_static) {
FrameData frame;
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
index 55aaaf6..5657373 100644
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
@@ -1,3 +1,4 @@
d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000>
e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e4ae8000-e4ae9000 rw-p 1000 00:00 0 libart.so
e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index f25c781..3cd9d40 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -2,7 +2,9 @@
dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so
e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex
e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000
-e648e000-e690f000 r-xp 00000000 00:00 0 libart.so
+e648e000-e690f000 r-xp 0 00:00 0 libart.so
+e690f000-e6910000 rw-p 1000 00:00 0 libart.so
ed306000-ed801000 r-xp 0 00:00 0 libartd.so
+ed801000-ed802000 rw-p 1000 00:00 0 libartd.so
eda88000-edb23000 r-xp 0 00:00 0 libc.so
ede4e000-ede50000 r-xp 0 00:00 0 anonymous:ede4e000
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index db4f9f7..a8d215c 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -3,4 +3,5 @@
ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
+f732b000-f732c000 rw-p 1000 00:00 0 libartd.so
f734b000-f74fc000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 266a6db..aebeb95 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -113,7 +113,7 @@
}
Elf elf(memory);
- if (!elf.Init(true) || !elf.valid()) {
+ 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 0f01566..4b6f49a 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -173,7 +173,7 @@
}
Elf elf(memory);
- if (!elf.Init(true) || !elf.valid()) {
+ if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
return 1;
}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index f8e3e92..9128430 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -66,7 +66,7 @@
}
unwindstack::Elf elf(memory);
- if (!elf.Init(true) || !elf.valid()) {
+ if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", argv[1]);
return 1;
}
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 583c6b9..5feb2aa 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -62,11 +62,17 @@
}
// Move Constructor.
-FileMap::FileMap(FileMap&& other)
- : mFileName(other.mFileName), mBasePtr(other.mBasePtr), mBaseLength(other.mBaseLength),
- mDataOffset(other.mDataOffset), mDataPtr(other.mDataPtr), mDataLength(other.mDataLength)
+FileMap::FileMap(FileMap&& other) noexcept
+ : mFileName(other.mFileName),
+ mBasePtr(other.mBasePtr),
+ mBaseLength(other.mBaseLength),
+ mDataOffset(other.mDataOffset),
+ mDataPtr(other.mDataPtr),
+ mDataLength(other.mDataLength)
#if defined(__MINGW32__)
- , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
+ ,
+ mFileHandle(other.mFileHandle),
+ mFileMapping(other.mFileMapping)
#endif
{
other.mFileName = nullptr;
@@ -79,7 +85,7 @@
}
// Move assign operator.
-FileMap& FileMap::operator=(FileMap&& other) {
+FileMap& FileMap::operator=(FileMap&& other) noexcept {
mFileName = other.mFileName;
mBasePtr = other.mBasePtr;
mBaseLength = other.mBaseLength;
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
index b8c065d..f00272a 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -208,7 +208,7 @@
mTokenizer->nextLine();
}
- return NO_ERROR;
+ return OK;
}
} // namespace android
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 5c0b406..818b171 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -157,12 +157,12 @@
if (begin >= N) {
SharedBuffer::bufferFromData(mString)->release();
mString = getEmptyString();
- return NO_ERROR;
+ return OK;
}
if ((begin+len) > N) len = N-begin;
if (begin == 0 && len == N) {
setTo(other);
- return NO_ERROR;
+ return OK;
}
if (&other == this) {
@@ -191,7 +191,7 @@
memmove(str, other, len*sizeof(char16_t));
str[len] = 0;
mString = str;
- return NO_ERROR;
+ return OK;
}
return NO_MEMORY;
}
@@ -202,9 +202,9 @@
const size_t otherLen = other.size();
if (myLen == 0) {
setTo(other);
- return NO_ERROR;
+ return OK;
} else if (otherLen == 0) {
- return NO_ERROR;
+ return OK;
}
if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
@@ -218,7 +218,7 @@
char16_t* str = (char16_t*)buf->data();
memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
mString = str;
- return NO_ERROR;
+ return OK;
}
return NO_MEMORY;
}
@@ -228,9 +228,9 @@
const size_t myLen = size();
if (myLen == 0) {
setTo(chrs, otherLen);
- return NO_ERROR;
+ return OK;
} else if (otherLen == 0) {
- return NO_ERROR;
+ return OK;
}
if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
@@ -245,7 +245,7 @@
memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
str[myLen+otherLen] = 0;
mString = str;
- return NO_ERROR;
+ return OK;
}
return NO_MEMORY;
}
@@ -260,9 +260,9 @@
const size_t myLen = size();
if (myLen == 0) {
return setTo(chrs, len);
- return NO_ERROR;
+ return OK;
} else if (len == 0) {
- return NO_ERROR;
+ return OK;
}
if (pos > myLen) pos = myLen;
@@ -286,7 +286,7 @@
#if 0
printf("Result (%d chrs): %s\n", size(), String8(*this).string());
#endif
- return NO_ERROR;
+ return OK;
}
return NO_MEMORY;
}
@@ -357,7 +357,7 @@
edit[i] = tolower((char)v);
}
}
- return NO_ERROR;
+ return OK;
}
status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
@@ -378,7 +378,7 @@
edit[i] = withThis;
}
}
- return NO_ERROR;
+ return OK;
}
status_t String16::remove(size_t len, size_t begin)
@@ -387,11 +387,11 @@
if (begin >= N) {
SharedBuffer::bufferFromData(mString)->release();
mString = getEmptyString();
- return NO_ERROR;
+ return OK;
}
if ((begin+len) > N) len = N-begin;
if (begin == 0 && len == N) {
- return NO_ERROR;
+ return OK;
}
if (begin > 0) {
@@ -410,7 +410,7 @@
char16_t* str = (char16_t*)buf->data();
str[len] = 0;
mString = str;
- return NO_ERROR;
+ return OK;
}
return NO_MEMORY;
}
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 8d318f7..0025c56 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -230,7 +230,7 @@
const char *newString = allocFromUTF8(other, strlen(other));
SharedBuffer::bufferFromData(mString)->release();
mString = newString;
- if (mString) return NO_ERROR;
+ if (mString) return OK;
mString = getEmptyString();
return NO_MEMORY;
@@ -241,7 +241,7 @@
const char *newString = allocFromUTF8(other, len);
SharedBuffer::bufferFromData(mString)->release();
mString = newString;
- if (mString) return NO_ERROR;
+ if (mString) return OK;
mString = getEmptyString();
return NO_MEMORY;
@@ -252,7 +252,7 @@
const char *newString = allocFromUTF16(other, len);
SharedBuffer::bufferFromData(mString)->release();
mString = newString;
- if (mString) return NO_ERROR;
+ if (mString) return OK;
mString = getEmptyString();
return NO_MEMORY;
@@ -263,7 +263,7 @@
const char *newString = allocFromUTF32(other, len);
SharedBuffer::bufferFromData(mString)->release();
mString = newString;
- if (mString) return NO_ERROR;
+ if (mString) return OK;
mString = getEmptyString();
return NO_MEMORY;
@@ -274,9 +274,9 @@
const size_t otherLen = other.bytes();
if (bytes() == 0) {
setTo(other);
- return NO_ERROR;
+ return OK;
} else if (otherLen == 0) {
- return NO_ERROR;
+ return OK;
}
return real_append(other.string(), otherLen);
@@ -292,7 +292,7 @@
if (bytes() == 0) {
return setTo(other, otherLen);
} else if (otherLen == 0) {
- return NO_ERROR;
+ return OK;
}
return real_append(other, otherLen);
@@ -311,7 +311,7 @@
status_t String8::appendFormatV(const char* fmt, va_list args)
{
- int n, result = NO_ERROR;
+ int n, result = OK;
va_list tmp_args;
/* args is undefined after vsnprintf.
@@ -346,7 +346,7 @@
str += myLen;
memcpy(str, other, otherLen);
str[otherLen] = '\0';
- return NO_ERROR;
+ return OK;
}
return NO_MEMORY;
}
@@ -382,7 +382,7 @@
mString = str;
}
- return NO_ERROR;
+ return OK;
}
ssize_t String8::find(const char* other, size_t start) const
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 43ec6c1..64bc402 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -379,7 +379,7 @@
{
DWORD dwWaitResult;
dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE);
- return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR;
+ return dwWaitResult != WAIT_OBJECT_0 ? -1 : OK;
}
void Mutex::unlock()
@@ -506,7 +506,7 @@
ReleaseMutex(condState->internalMutex);
WaitForSingleObject(hMutex, INFINITE);
- return res == WAIT_OBJECT_0 ? NO_ERROR : -1;
+ return res == WAIT_OBJECT_0 ? OK : -1;
}
} WinCondition;
@@ -639,13 +639,15 @@
*/
Thread::Thread(bool canCallJava)
- : mCanCallJava(canCallJava),
- mThread(thread_id_t(-1)),
- mLock("Thread::mLock"),
- mStatus(NO_ERROR),
- mExitPending(false), mRunning(false)
+ : mCanCallJava(canCallJava),
+ mThread(thread_id_t(-1)),
+ mLock("Thread::mLock"),
+ mStatus(OK),
+ mExitPending(false),
+ mRunning(false)
#if defined(__ANDROID__)
- , mTid(-1)
+ ,
+ mTid(-1)
#endif
{
}
@@ -656,7 +658,7 @@
status_t Thread::readyToRun()
{
- return NO_ERROR;
+ return OK;
}
status_t Thread::run(const char* name, int32_t priority, size_t stack)
@@ -672,7 +674,7 @@
// reset status and exitPending to their default value, so we can
// try again after an error happened (either below, or in readyToRun())
- mStatus = NO_ERROR;
+ mStatus = OK;
mExitPending = false;
mThread = thread_id_t(-1);
@@ -700,10 +702,10 @@
}
// Do not refer to mStatus here: The thread is already running (may, in fact
- // already have exited with a valid mStatus result). The NO_ERROR indication
+ // already have exited with a valid mStatus result). The OK indication
// here merely indicates successfully starting the thread and does not
// imply successful termination/execution.
- return NO_ERROR;
+ return OK;
// Exiting scope of mLock is a memory barrier and allows new thread to run
}
@@ -728,7 +730,7 @@
if (first) {
first = false;
self->mStatus = self->readyToRun();
- result = (self->mStatus == NO_ERROR);
+ result = (self->mStatus == OK);
if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index f73d699..98dd2fd 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -48,7 +48,7 @@
status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
*outTokenizer = nullptr;
- int result = NO_ERROR;
+ int result = OK;
int fd = ::open(filename.string(), O_RDONLY);
if (fd < 0) {
result = -errno;
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index e16f88d..c97a19b 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -61,7 +61,7 @@
"[%p] subclasses of VectorImpl must call finish_vector()"
" in their destructor. Leaking %d bytes.",
this, (int)(mCount*mItemSize));
- // We can't call _do_destroy() here because the vtable is already gone.
+ // We can't call _do_destroy() here because the vtable is already gone.
}
VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
@@ -197,7 +197,7 @@
_do_copy(temp, item, 1);
ssize_t j = i-1;
- void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
+ void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
do {
_do_destroy(next, 1);
_do_copy(next, curr, 1);
@@ -214,13 +214,13 @@
}
i++;
}
-
+
if (temp) {
_do_destroy(temp, 1);
free(temp);
}
}
- return NO_ERROR;
+ return OK;
}
void VectorImpl::pop()
@@ -354,7 +354,7 @@
}
ssize_t VectorImpl::resize(size_t size) {
- ssize_t result = NO_ERROR;
+ ssize_t result = OK;
if (size > mCount) {
result = insertAt(mCount, size - mCount);
} else if (size < mCount) {
@@ -370,7 +370,7 @@
if (sb->release(SharedBuffer::eKeepStorage) == 1) {
_do_destroy(mStorage, mCount);
SharedBuffer::dealloc(sb);
- }
+ }
}
}
@@ -644,13 +644,13 @@
}
}
}
- return NO_ERROR;
+ return OK;
}
ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
{
// we've merging a sorted vector... nice!
- ssize_t err = NO_ERROR;
+ ssize_t err = OK;
if (!vector.isEmpty()) {
// first take care of the case where the vectors are sorted together
if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
@@ -677,4 +677,3 @@
/*****************************************************************************/
}; // namespace android
-
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index 7093a20..1e03677 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -14,22 +14,19 @@
* limitations under the License.
*/
-#ifndef ANDROID_ERRORS_H
-#define ANDROID_ERRORS_H
+#pragma once
-#include <sys/types.h>
#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
namespace android {
-// use this type to return error codes
-#ifdef _WIN32
-typedef int status_t;
-#else
-typedef int32_t status_t;
-#endif
-
-/* the MS C runtime lacks a few error codes */
+/**
+ * The type used to return success/failure from frameworks APIs.
+ * See the anonymous enum below for valid values.
+ */
+typedef int32_t status_t;
/*
* Error codes.
@@ -43,8 +40,8 @@
#endif
enum {
- OK = 0, // Everything's swell.
- NO_ERROR = 0, // No errors.
+ OK = 0, // Preferred constant for checking success.
+ NO_ERROR = OK, // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
@@ -82,7 +79,3 @@
#endif
} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_ERRORS_H
diff --git a/libutils/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
index 8d402a3..f9f8f3c 100644
--- a/libutils/include/utils/FileMap.h
+++ b/libutils/include/utils/FileMap.h
@@ -52,8 +52,8 @@
public:
FileMap(void);
- FileMap(FileMap&& f);
- FileMap& operator=(FileMap&& f);
+ FileMap(FileMap&& f) noexcept;
+ FileMap& operator=(FileMap&& f) noexcept;
/*
* Create a new mapping on an open file.
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 0a19019..9d00602 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -190,11 +190,11 @@
inline status_t flatten(void* buffer, size_t size) const {
if (size < sizeof(T)) return NO_MEMORY;
memcpy(buffer, static_cast<T const*>(this), sizeof(T));
- return NO_ERROR;
+ return OK;
}
inline status_t unflatten(void const* buffer, size_t) {
memcpy(static_cast<T*>(this), buffer, sizeof(T));
- return NO_ERROR;
+ return OK;
}
};
diff --git a/libutils/include/utils/Functor.h b/libutils/include/utils/Functor.h
index c0c8d57..c458699 100644
--- a/libutils/include/utils/Functor.h
+++ b/libutils/include/utils/Functor.h
@@ -29,7 +29,7 @@
public:
Functor() {}
virtual ~Functor() {}
- virtual status_t operator ()(int /*what*/, void* /*data*/) { return NO_ERROR; }
+ virtual status_t operator()(int /*what*/, void* /*data*/) { return OK; }
};
} // namespace android
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 3abce17..1571129 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -56,7 +56,7 @@
sp(T* other); // NOLINT(implicit)
sp(const sp<T>& other);
- sp(sp<T>&& other);
+ sp(sp<T>&& other) noexcept;
template<typename U> sp(U* other); // NOLINT(implicit)
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
@@ -67,7 +67,7 @@
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
- sp& operator = (sp<T>&& other);
+ sp& operator=(sp<T>&& other) noexcept;
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
@@ -125,9 +125,8 @@
m_ptr->incStrong(this);
}
-template<typename T>
-sp<T>::sp(sp<T>&& other)
- : m_ptr(other.m_ptr) {
+template <typename T>
+sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
other.m_ptr = nullptr;
}
@@ -169,8 +168,8 @@
return *this;
}
-template<typename T>
-sp<T>& sp<T>::operator =(sp<T>&& other) {
+template <typename T>
+sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
diff --git a/libutils/include/utils/Tokenizer.h b/libutils/include/utils/Tokenizer.h
index bb25f37..61c5ff7 100644
--- a/libutils/include/utils/Tokenizer.h
+++ b/libutils/include/utils/Tokenizer.h
@@ -37,7 +37,7 @@
/**
* Opens a file and maps it into memory.
*
- * Returns NO_ERROR and a tokenizer for the file, if successful.
+ * Returns OK and a tokenizer for the file, if successful.
* Otherwise returns an error and sets outTokenizer to NULL.
*/
static status_t open(const String8& filename, Tokenizer** outTokenizer);
@@ -45,7 +45,7 @@
/**
* Prepares to tokenize the contents of a string.
*
- * Returns NO_ERROR and a tokenizer for the string, if successful.
+ * Returns OK and a tokenizer for the string, if successful.
* Otherwise returns an error and sets outTokenizer to NULL.
*/
static status_t fromContents(const String8& filename,
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index fd3f602..2095189 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -23,11 +23,21 @@
"-D_FILE_OFFSET_BITS=64",
],
cppflags: [
- "-Wold-style-cast",
// Incorrectly warns when C++11 empty brace {} initializer is used.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
"-Wno-missing-field-initializers",
],
+
+ // Enable -Wold-style-cast only for non-Windows targets. _islower_l,
+ // _isupper_l etc. in MinGW locale_win32.h (included from
+ // libcxx/include/__locale) has an old-style-cast.
+ target: {
+ not_windows: {
+ cppflags: [
+ "-Wold-style-cast",
+ ],
+ },
+ },
}
cc_defaults {
@@ -63,6 +73,7 @@
enabled: true,
},
double_loadable: true,
+ export_shared_lib_headers: ["libbase"],
defaults: [
"libziparchive_defaults",
@@ -74,14 +85,6 @@
"libz",
],
target: {
- android: {
- shared_libs: [
- "libutils",
- ],
- },
- host: {
- static_libs: ["libutils"],
- },
linux_bionic: {
enabled: true,
},
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 018b1a9..ab38dfd 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -14,17 +14,18 @@
* limitations under the License.
*/
+#pragma once
+
/*
* Read-only access to Zip archives, with minimal heap allocation.
*/
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_H_
#include <stdint.h>
#include <string.h>
#include <sys/cdefs.h>
#include <sys/types.h>
-#include <utils/Compat.h>
+
+#include "android-base/off64_t.h"
/* Zip compression methods we support */
enum {
@@ -103,7 +104,8 @@
off64_t offset;
};
-typedef void* ZipArchiveHandle;
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
/*
* Open a Zip archive, and sets handle to the value of the opaque
@@ -144,7 +146,7 @@
* this handle for any further operations without an intervening
* call to one of the OpenArchive variants.
*/
-void CloseArchive(ZipArchiveHandle handle);
+void CloseArchive(ZipArchiveHandle archive);
/*
* Find an entry in the Zip archive, by name. |entryName| must be a null
@@ -162,7 +164,7 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
/*
* Start iterating over all entries of a zip file. The order of iteration
@@ -177,8 +179,8 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
- const ZipString* optional_suffix);
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+ const ZipString* optional_prefix, const ZipString* optional_suffix);
/*
* Advance to the next element in the zipfile in iteration order.
@@ -203,7 +205,7 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd);
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd);
/**
* Uncompress a given zip entry to the memory region at |begin| and of
@@ -213,9 +215,9 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size);
-int GetFileDescriptor(const ZipArchiveHandle handle);
+int GetFileDescriptor(const ZipArchiveHandle archive);
const char* ErrorCodeString(int32_t error_code);
@@ -226,7 +228,7 @@
* Stream the uncompressed data through the supplied function,
* passing cookie to it each time it gets called.
*/
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
ProcessZipEntryFunction func, void* cookie);
#endif
@@ -272,5 +274,3 @@
int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
} // namespace zip_archive
-
-#endif // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
index b4766f8..8c6ca79 100644
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -15,12 +15,13 @@
*/
// Read-only stream access to Zip archives entries.
-#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
-#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
#include <vector>
-#include <ziparchive/zip_archive.h>
+#include "android-base/off64_t.h"
class ZipArchiveStreamEntry {
public:
@@ -43,5 +44,3 @@
off64_t offset_ = 0;
uint32_t crc32_ = 0u;
};
-
-#endif // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index c350a27..f6c8427 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
-#define LIBZIPARCHIVE_ZIPWRITER_H_
+#pragma once
#include <cstdio>
#include <ctime>
@@ -25,7 +24,7 @@
#include <vector>
#include "android-base/macros.h"
-#include "utils/Compat.h"
+#include "android-base/off64_t.h"
struct z_stream_s;
typedef struct z_stream_s z_stream;
@@ -91,10 +90,10 @@
explicit ZipWriter(FILE* f);
// Move constructor.
- ZipWriter(ZipWriter&& zipWriter);
+ ZipWriter(ZipWriter&& zipWriter) noexcept;
// Move assignment.
- ZipWriter& operator=(ZipWriter&& zipWriter);
+ ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
/**
* Starts a new zip entry with the given path and flags.
@@ -183,5 +182,3 @@
std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
std::vector<uint8_t> buffer_;
};
-
-#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f8d1356..9eb7f2c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -20,7 +20,8 @@
#define LOG_TAG "ziparchive"
-#include <assert.h>
+#include "ziparchive/zip_archive.h"
+
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -33,6 +34,10 @@
#include <memory>
#include <vector>
+#if defined(__APPLE__)
+#define lseek64 lseek
+#endif
+
#if defined(__BIONIC__)
#include <android/fdsan.h>
#endif
@@ -40,12 +45,10 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#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/utf8.h>
#include <log/log.h>
-#include <utils/Compat.h>
-#include <utils/FileMap.h>
-#include "ziparchive/zip_archive.h"
#include "zlib.h"
#include "entry_name_utils-inl.h"
@@ -58,12 +61,6 @@
// specified in the local file header and the central directory.
static const bool kCrcChecksEnabled = false;
-// This is for windows. If we don't open a file in binary mode, weird
-// things will happen.
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
// The maximum number of bytes to scan backwards for the EOCD start.
static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
@@ -196,7 +193,7 @@
close_file(assume_ownership),
directory_offset(0),
central_directory(),
- directory_map(new android::FileMap()),
+ directory_map(),
num_entries(0),
hash_table_size(0),
hash_table(nullptr) {
@@ -212,7 +209,7 @@
close_file(false),
directory_offset(0),
central_directory(),
- directory_map(new android::FileMap()),
+ directory_map(),
num_entries(0),
hash_table_size(0),
hash_table(nullptr) {}
@@ -303,8 +300,7 @@
* in archive.
*/
- if (!archive->InitializeCentralDirectory(debug_file_name,
- static_cast<off64_t>(eocd->cd_start_offset),
+ if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
static_cast<size_t>(eocd->cd_size))) {
ALOGE("Zip: failed to intialize central directory.\n");
return kMmapFailed;
@@ -517,8 +513,7 @@
/*
* Close a ZipArchive, closing the file and freeing the contents.
*/
-void CloseArchive(ZipArchiveHandle handle) {
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+void CloseArchive(ZipArchiveHandle archive) {
ALOGV("Closing archive %p", archive);
delete archive;
}
@@ -745,10 +740,8 @@
}
};
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
- const ZipString* optional_suffix) {
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
-
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+ const ZipString* optional_prefix, const ZipString* optional_suffix) {
if (archive == NULL || archive->hash_table == NULL) {
ALOGW("Zip: Invalid ZipArchiveHandle");
return kInvalidHandle;
@@ -766,8 +759,7 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
- const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+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);
return kInvalidEntryName;
@@ -827,7 +819,7 @@
virtual bool Append(uint8_t* buf, size_t buf_size) override {
if (bytes_written_ + buf_size > size_) {
- ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+ ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
bytes_written_ + buf_size);
return false;
}
@@ -903,7 +895,7 @@
return FileWriter(fd, declared_length);
}
- FileWriter(FileWriter&& other)
+ FileWriter(FileWriter&& other) noexcept
: fd_(other.fd_),
declared_length_(other.declared_length_),
total_bytes_written_(other.total_bytes_written_) {
@@ -914,7 +906,7 @@
virtual bool Append(uint8_t* buf, size_t buf_size) override {
if (total_bytes_written_ + buf_size > declared_length_) {
- ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
+ ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
total_bytes_written_ + buf_size);
return false;
}
@@ -923,7 +915,7 @@
if (result) {
total_bytes_written_ += buf_size;
} else {
- ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+ ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
}
return result;
@@ -1052,7 +1044,7 @@
}
} while (zerr == Z_OK);
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+ CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
// NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
// "feature" of zlib to tell it there won't be a zlib file header. zlib
@@ -1116,8 +1108,7 @@
return 0;
}
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer) {
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) {
const uint16_t method = entry->method;
// this should default to kUnknownCompressionMethod.
@@ -1145,18 +1136,18 @@
return return_value;
}
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) {
MemoryWriter writer(begin, size);
- return ExtractToWriter(handle, entry, &writer);
+ return ExtractToWriter(archive, entry, &writer);
}
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) {
auto writer = FileWriter::Create(fd, entry);
if (!writer.IsValid()) {
return kIoError;
}
- return ExtractToWriter(handle, entry, &writer);
+ return ExtractToWriter(archive, entry, &writer);
}
const char* ErrorCodeString(int32_t error_code) {
@@ -1173,8 +1164,8 @@
return "Unknown return code";
}
-int GetFileDescriptor(const ZipArchiveHandle handle) {
- return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
+int GetFileDescriptor(const ZipArchiveHandle archive) {
+ return archive->mapped_zip.GetFileDescriptor();
}
ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
@@ -1198,10 +1189,10 @@
void* cookie_;
};
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
ProcessZipEntryFunction func, void* cookie) {
ProcessWriter writer(func, cookie);
- return ExtractToWriter(handle, entry, &writer);
+ return ExtractToWriter(archive, entry, &writer);
}
#endif //! defined(_WIN32)
@@ -1260,16 +1251,14 @@
length_ = cd_size;
}
-bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
- size_t cd_size) {
+bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
if (mapped_zip.HasFd()) {
- if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
- cd_size, true /* read only */)) {
- return false;
- }
+ directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
+ cd_start_offset, cd_size, PROT_READ);
+ if (!directory_map) return false;
- CHECK_EQ(directory_map->getDataLength(), cd_size);
- central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
+ CHECK_EQ(directory_map->size(), cd_size);
+ central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
} else {
if (mapped_zip.GetBasePtr() == nullptr) {
ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 83cb11f..330a02a 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
#include <stdint.h>
#include <stdlib.h>
@@ -24,9 +25,8 @@
#include <memory>
#include <vector>
-#include <utils/FileMap.h>
-#include <ziparchive/zip_archive.h>
#include "android-base/macros.h"
+#include "android-base/mapped_file.h"
static const char* kErrorMessages[] = {
"Success",
@@ -164,7 +164,7 @@
// mapped central directory area
off64_t directory_offset;
CentralDirectory central_directory;
- std::unique_ptr<android::FileMap> directory_map;
+ std::unique_ptr<android::base::MappedFile> directory_map;
// number of entries in the Zip archive
uint16_t num_entries;
@@ -180,8 +180,5 @@
ZipArchive(void* address, size_t length);
~ZipArchive();
- bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
- size_t cd_size);
+ bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
};
-
-#endif // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 23dd5dc..0ea7d5d 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -27,10 +27,10 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/mapped_file.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
-#include <utils/FileMap.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_archive_stream_entry.h>
@@ -348,7 +348,7 @@
// Read the file back to a buffer and make sure the contents are
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
- ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
ASSERT_EQ(file_contents, buffer);
@@ -392,7 +392,7 @@
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
- ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
@@ -404,7 +404,7 @@
// Assert that the total length of the file is sane
ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
- lseek64(tmp_file.fd, 0, SEEK_END));
+ lseek(tmp_file.fd, 0, SEEK_END));
}
#if !defined(_WIN32)
@@ -416,11 +416,10 @@
ASSERT_EQ(0, fstat(fd, &sb));
// Memory map the file first and open the archive from the memory region.
- android::FileMap file_map;
- file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
+ auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)};
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
- zip_path.c_str(), &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");
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6ad3366..981df3a 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -26,8 +26,6 @@
#include <vector>
#include "android-base/logging.h"
-#include "utils/Compat.h"
-#include "utils/Log.h"
#include "entry_name_utils-inl.h"
#include "zip_archive_common.h"
@@ -97,7 +95,7 @@
}
}
-ZipWriter::ZipWriter(ZipWriter&& writer)
+ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
: file_(writer.file_),
seekable_(writer.seekable_),
current_offset_(writer.current_offset_),
@@ -109,7 +107,7 @@
writer.state_ = State::kError;
}
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
file_ = writer.file_;
seekable_ = writer.seekable_;
current_offset_ = writer.current_offset_;
@@ -303,10 +301,10 @@
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+ LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
return HandleError(kZlibError);
} else {
- ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+ LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
return HandleError(kZlibError);
}
}
diff --git a/llkd/README.md b/llkd/README.md
index 1f69718..e5be850 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -127,7 +127,7 @@
default 2 minutes samples of threads for D or Z.
#### ro.llk.stack
-default *empty* or false, comma separated list of kernel symbols.
+default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
The string "*false*" is the equivalent to an *empty* list.
Look for kernel stack symbols that if ever persistently present can
indicate a subsystem is locked up.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index d0188ec..2c62fca 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -48,16 +48,16 @@
/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
#define LLK_CHECK_STACK_PROPERTY "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT ""
+#define LLK_CHECK_STACK_DEFAULT "cma_alloc,__get_user_pages"
#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
#define LLK_BLACKLIST_PROCESS_DEFAULT \
- "0,1,2,init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
+ "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
#define LLK_BLACKLIST_PARENT_PROPERTY "ro.llk.blacklist.parent"
#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd]"
#define LLK_BLACKLIST_UID_PROPERTY "ro.llk.blacklist.uid"
#define LLK_BLACKLIST_UID_DEFAULT ""
#define LLK_BLACKLIST_STACK_PROPERTY "ro.llk.blacklist.process.stack"
-#define LLK_BLACKLIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,/system/bin/keystore"
+#define LLK_BLACKLIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
/* clang-format on */
__END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 58c2ba8..2727aab 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -265,7 +265,7 @@
}
content.erase(pos);
uid_t ret;
- if (!android::base::ParseInt(content, &ret, uid_t(0))) {
+ if (!android::base::ParseUint(content, &ret, uid_t(0))) {
return -1;
}
return ret;
@@ -712,6 +712,7 @@
if (llkSkipName(std::to_string(procp->pid), llkBlacklistStack)) return false;
if (llkSkipName(procp->getComm(), llkBlacklistStack)) return false;
if (llkSkipName(procp->getCmdline(), llkBlacklistStack)) return false;
+ if (llkSkipName(android::base::Basename(procp->getCmdline()), llkBlacklistStack)) return false;
auto kernel_stack = ReadFile(piddir + "/stack");
if (kernel_stack.empty()) {
@@ -995,13 +996,18 @@
if (llkSkipName(procp->getCmdline())) {
break;
}
+ if (llkSkipName(android::base::Basename(procp->getCmdline()))) {
+ break;
+ }
auto pprocp = llkTidLookup(ppid);
if (pprocp == nullptr) {
pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
}
- if ((pprocp != nullptr) && (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
- llkSkipName(pprocp->getCmdline(), llkBlacklistParent))) {
+ if ((pprocp != nullptr) &&
+ (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
+ llkSkipName(pprocp->getCmdline(), llkBlacklistParent) ||
+ llkSkipName(android::base::Basename(pprocp->getCmdline()), llkBlacklistParent))) {
break;
}
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index fe6364d..e72d159 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -30,6 +30,8 @@
LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
LMK_PROCPRIO, /* Register a process and set its oom_adj_score */
LMK_PROCREMOVE, /* Unregister a process */
+ LMK_PROCPURGE, /* Purge all registered processes */
+ LMK_GETKILLCNT, /* Get number of kills */
};
/*
@@ -142,6 +144,53 @@
return 2 * sizeof(int);
}
+/*
+ * Prepare LMK_PROCPURGE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+ packet[0] = htonl(LMK_PROCPURGE);
+ return sizeof(int);
+}
+
+/* LMK_GETKILLCNT packet payload */
+struct lmk_getkillcnt {
+ int min_oomadj;
+ int max_oomadj;
+};
+
+/*
+ * For LMK_GETKILLCNT packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
+ struct lmk_getkillcnt *params) {
+ params->min_oomadj = ntohl(packet[1]);
+ params->max_oomadj = ntohl(packet[2]);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
+ struct lmk_getkillcnt *params) {
+ packet[0] = htonl(LMK_GETKILLCNT);
+ packet[1] = htonl(params->min_oomadj);
+ packet[2] = htonl(params->max_oomadj);
+ return 3 * sizeof(int);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT reply packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
+ packet[0] = htonl(LMK_GETKILLCNT);
+ packet[1] = htonl(kill_cnt);
+ return 2 * sizeof(int);
+}
+
__END_DECLS
#endif /* _LMKD_H_ */
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 1980dc6..c9c9e8e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -313,7 +313,20 @@
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
-static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
+#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
+static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
+
+#define MAX_DISTINCT_OOM_ADJ 32
+#define KILLCNT_INVALID_IDX 0xFF
+/*
+ * Because killcnt array is sparse a two-level indirection is used
+ * to keep the size small. killcnt_idx stores index of the element in
+ * killcnt array. Index KILLCNT_INVALID_IDX indicates an unused slot.
+ */
+static uint8_t killcnt_idx[ADJTOSLOT_COUNT];
+static uint16_t killcnt[MAX_DISTINCT_OOM_ADJ];
+static int killcnt_free_idx = 0;
+static uint32_t killcnt_total = 0;
/* PAGE_SIZE / 1024 */
static long page_k;
@@ -559,7 +572,7 @@
} else if (params.oomadj >= 300) {
soft_limit_mult = 1;
} else if (params.oomadj >= 200) {
- soft_limit_mult = 2;
+ soft_limit_mult = 8;
} else if (params.oomadj >= 100) {
soft_limit_mult = 10;
} else if (params.oomadj >= 0) {
@@ -612,9 +625,99 @@
}
lmkd_pack_get_procremove(packet, ¶ms);
+ /*
+ * WARNING: After pid_remove() procp is freed and can't be used!
+ * Therefore placed at the end of the function.
+ */
pid_remove(params.pid);
}
+static void cmd_procpurge() {
+ int i;
+ struct proc *procp;
+ struct proc *next;
+
+ if (use_inkernel_interface) {
+ return;
+ }
+
+ for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
+ procadjslot_list[i].next = &procadjslot_list[i];
+ procadjslot_list[i].prev = &procadjslot_list[i];
+ }
+
+ for (i = 0; i < PIDHASH_SZ; i++) {
+ procp = pidhash[i];
+ while (procp) {
+ next = procp->pidhash_next;
+ free(procp);
+ procp = next;
+ }
+ }
+ memset(&pidhash[0], 0, sizeof(pidhash));
+}
+
+static void inc_killcnt(int oomadj) {
+ int slot = ADJTOSLOT(oomadj);
+ uint8_t idx = killcnt_idx[slot];
+
+ if (idx == KILLCNT_INVALID_IDX) {
+ /* index is not assigned for this oomadj */
+ if (killcnt_free_idx < MAX_DISTINCT_OOM_ADJ) {
+ killcnt_idx[slot] = killcnt_free_idx;
+ killcnt[killcnt_free_idx] = 1;
+ killcnt_free_idx++;
+ } else {
+ ALOGW("Number of distinct oomadj levels exceeds %d",
+ MAX_DISTINCT_OOM_ADJ);
+ }
+ } else {
+ /*
+ * wraparound is highly unlikely and is detectable using total
+ * counter because it has to be equal to the sum of all counters
+ */
+ killcnt[idx]++;
+ }
+ /* increment total kill counter */
+ killcnt_total++;
+}
+
+static int get_killcnt(int min_oomadj, int max_oomadj) {
+ int slot;
+ int count = 0;
+
+ if (min_oomadj > max_oomadj)
+ return 0;
+
+ /* special case to get total kill count */
+ if (min_oomadj > OOM_SCORE_ADJ_MAX)
+ return killcnt_total;
+
+ while (min_oomadj <= max_oomadj &&
+ (slot = ADJTOSLOT(min_oomadj)) < ADJTOSLOT_COUNT) {
+ uint8_t idx = killcnt_idx[slot];
+ if (idx != KILLCNT_INVALID_IDX) {
+ count += killcnt[idx];
+ }
+ min_oomadj++;
+ }
+
+ return count;
+}
+
+static int cmd_getkillcnt(LMKD_CTRL_PACKET packet) {
+ struct lmk_getkillcnt params;
+
+ if (use_inkernel_interface) {
+ /* kernel driver does not expose this information */
+ return 0;
+ }
+
+ lmkd_pack_get_getkillcnt(packet, ¶ms);
+
+ return get_killcnt(params.min_oomadj, params.max_oomadj);
+}
+
static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
int i;
struct lmk_target target;
@@ -719,12 +822,28 @@
return ret;
}
+static int ctrl_data_write(int dsock_idx, char *buf, size_t bufsz) {
+ int ret = 0;
+
+ ret = TEMP_FAILURE_RETRY(write(data_sock[dsock_idx].sock, buf, bufsz));
+
+ if (ret == -1) {
+ ALOGE("control data socket write failed; errno=%d", errno);
+ } else if (ret == 0) {
+ ALOGE("Got EOF on control data socket");
+ ret = -1;
+ }
+
+ return ret;
+}
+
static void ctrl_command_handler(int dsock_idx) {
LMKD_CTRL_PACKET packet;
int len;
enum lmk_cmd cmd;
int nargs;
int targets;
+ int kill_cnt;
len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
if (len <= 0)
@@ -757,6 +876,19 @@
goto wronglen;
cmd_procremove(packet);
break;
+ case LMK_PROCPURGE:
+ if (nargs != 0)
+ goto wronglen;
+ cmd_procpurge();
+ break;
+ case LMK_GETKILLCNT:
+ if (nargs != 2)
+ goto wronglen;
+ kill_cnt = cmd_getkillcnt(packet);
+ len = lmkd_pack_set_getkillcnt_repl(packet, kill_cnt);
+ if (ctrl_data_write(dsock_idx, (char *)packet, len) != len)
+ return;
+ break;
default:
ALOGE("Received unknown command code %d", cmd);
return;
@@ -822,7 +954,7 @@
}
#ifdef LMKD_LOG_STATS
-static void memory_stat_parse_line(char *line, struct memory_stat *mem_st) {
+static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
char key[LINE_MAX + 1];
int64_t value;
@@ -844,17 +976,10 @@
mem_st->swap_in_bytes = value;
}
-static int memory_stat_parse(struct memory_stat *mem_st, int pid, uid_t uid) {
+static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
FILE *fp;
char buf[PATH_MAX];
- /*
- * Per-application memory.stat files are available only when
- * per-application memcgs are enabled.
- */
- if (!per_app_memcg)
- return -1;
-
snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
fp = fopen(buf, "r");
@@ -864,13 +989,50 @@
return -1;
}
- while (fgets(buf, PAGE_SIZE, fp) != NULL ) {
+ while (fgets(buf, PAGE_SIZE, fp) != NULL) {
memory_stat_parse_line(buf, mem_st);
}
fclose(fp);
return 0;
}
+
+static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
+ char path[PATH_MAX];
+ char buffer[PROC_STAT_BUFFER_SIZE];
+ int fd, ret;
+
+ snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
+ if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+ ALOGE("%s open failed: %s", path, strerror(errno));
+ return -1;
+ }
+
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret < 0) {
+ ALOGE("%s read failed: %s", path, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ // field 10 is pgfault
+ // field 12 is pgmajfault
+ // field 24 is rss_in_pages
+ int64_t pgfault = 0, pgmajfault = 0, rss_in_pages = 0;
+ if (sscanf(buffer,
+ "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
+ "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
+ "%*d %*d %" SCNd64 "",
+ &pgfault, &pgmajfault, &rss_in_pages) != 3) {
+ return -1;
+ }
+ mem_st->pgfault = pgfault;
+ mem_st->pgmajfault = pgmajfault;
+ mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
+
+ return 0;
+}
#endif
/* /prop/zoneinfo parsing routines */
@@ -1098,6 +1260,8 @@
return maxprocp;
}
+static int last_killed_pid = -1;
+
/* Kill one process specified by procp. Returns the size of the process killed */
static int kill_one_process(struct proc* procp) {
int pid = procp->pid;
@@ -1105,6 +1269,7 @@
char *taskname;
int tasksize;
int r;
+ int result = -1;
#ifdef LMKD_LOG_STATS
struct memory_stat mem_st = {};
@@ -1113,19 +1278,21 @@
taskname = proc_get_name(pid);
if (!taskname) {
- pid_remove(pid);
- return -1;
+ goto out;
}
tasksize = proc_get_size(pid);
if (tasksize <= 0) {
- pid_remove(pid);
- return -1;
+ goto out;
}
#ifdef LMKD_LOG_STATS
if (enable_stats_log) {
- memory_stat_parse_result = memory_stat_parse(&mem_st, pid, uid);
+ if (per_app_memcg) {
+ memory_stat_parse_result = memory_stat_from_cgroup(&mem_st, pid, uid);
+ } else {
+ memory_stat_parse_result = memory_stat_from_procfs(&mem_st, pid);
+ }
}
#endif
@@ -1133,38 +1300,47 @@
/* CAP_KILL required */
r = kill(pid, SIGKILL);
+ inc_killcnt(procp->oomadj);
ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
taskname, pid, uid, procp->oomadj, tasksize * page_k);
- pid_remove(pid);
TRACE_KILL_END();
+ last_killed_pid = pid;
+
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
- return -1;
+ goto out;
} else {
#ifdef LMKD_LOG_STATS
if (memory_stat_parse_result == 0) {
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
mem_st.cache_in_bytes, mem_st.swap_in_bytes);
+ } else if (enable_stats_log) {
+ stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
+ -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1);
}
#endif
- return tasksize;
+ result = tasksize;
}
- return tasksize;
+out:
+ /*
+ * WARNING: After pid_remove() procp is freed and can't be used!
+ * Therefore placed at the end of the function.
+ */
+ pid_remove(pid);
+ return result;
}
/*
- * Find processes to kill to free required number of pages.
- * If pages_to_free is set to 0 only one process will be killed.
- * Returns the size of the killed processes.
+ * Find one process to kill at or above the given oom_adj level.
+ * Returns size of the killed process.
*/
-static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
+static int find_and_kill_process(int min_score_adj) {
int i;
- int killed_size;
- int pages_freed = 0;
+ int killed_size = 0;
#ifdef LMKD_LOG_STATS
bool lmk_state_change_start = false;
@@ -1189,20 +1365,12 @@
LMK_STATE_CHANGE_START);
}
#endif
-
- pages_freed += killed_size;
- if (pages_freed >= pages_to_free) {
-
-#ifdef LMKD_LOG_STATS
- if (enable_stats_log && lmk_state_change_start) {
- stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
- LMK_STATE_CHANGE_STOP);
- }
-#endif
- return pages_freed;
- }
+ break;
}
}
+ if (killed_size) {
+ break;
+ }
}
#ifdef LMKD_LOG_STATS
@@ -1211,7 +1379,7 @@
}
#endif
- return pages_freed;
+ return killed_size;
}
static int64_t get_memory_usage(struct reread_data *file_data) {
@@ -1271,6 +1439,23 @@
level - 1 : level);
}
+static bool is_kill_pending(void) {
+ char buf[24];
+
+ if (last_killed_pid < 0) {
+ return false;
+ }
+
+ snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
+ if (access(buf, F_OK) == 0) {
+ return true;
+ }
+
+ // reset last killed PID because there's nothing pending
+ last_killed_pid = -1;
+ return false;
+}
+
static void mp_event_common(int data, uint32_t events __unused) {
int ret;
unsigned long long evcount;
@@ -1285,7 +1470,6 @@
enum vmpressure_level level = (enum vmpressure_level)data;
long other_free = 0, other_file = 0;
int min_score_adj;
- int pages_to_free = 0;
int minfree = 0;
static struct reread_data mem_usage_file_data = {
.filename = MEMCG_MEMORY_USAGE,
@@ -1316,7 +1500,11 @@
}
if (kill_timeout_ms) {
- if (get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+ // If we're within the timeout, see if there's pending reclaim work
+ // from the last killed process. If there is (as evidenced by
+ // /proc/<pid> continuing to exist), skip killing for now.
+ if ((get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) &&
+ (low_ram_device || is_kill_pending())) {
kill_skip_count++;
return;
}
@@ -1363,9 +1551,6 @@
return;
}
- /* Free up enough pages to push over the highest minfree level */
- pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
- ((other_free < other_file) ? other_free : other_file);
goto do_kill;
}
@@ -1421,7 +1606,7 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_processes(level_oomadj[level], 0) == 0) {
+ if (find_and_kill_process(level_oomadj[level]) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
@@ -1445,10 +1630,7 @@
return;
}
/* Free up enough memory to downgrate the memory pressure to low level */
- if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) {
- pages_to_free = low_pressure_mem.max_nr_free_pages -
- mi.field.nr_free_pages;
- } else {
+ if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
if (debug_process_killing) {
ALOGI("Ignoring pressure since more memory is "
"available (%" PRId64 ") than watermark (%" PRId64 ")",
@@ -1459,7 +1641,7 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_processes(min_score_adj, pages_to_free);
+ pages_freed = find_and_kill_process(min_score_adj);
if (pages_freed == 0) {
/* Rate limit kill reports when nothing was reclaimed */
@@ -1467,25 +1649,24 @@
report_skip_count++;
return;
}
+ } else {
+ /* If we killed anything, update the last killed timestamp. */
+ last_kill_tm = curr_tm;
}
/* Log meminfo whenever we kill or when report rate limit allows */
meminfo_log(&mi);
- if (pages_freed >= pages_to_free) {
- /* Reset kill time only if reclaimed enough memory */
- last_kill_tm = curr_tm;
- }
if (use_minfree_levels) {
- ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+ ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
"free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
- pages_to_free * page_k, pages_freed * page_k,
+ pages_freed * page_k,
other_file * page_k, mi.field.nr_free_pages * page_k,
zi.field.totalreserve_pages * page_k,
minfree * page_k, min_score_adj);
} else {
- ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
- pages_to_free * page_k, pages_freed * page_k, min_score_adj);
+ ALOGI("Reclaimed %ldkB at oom_adj %d",
+ pages_freed * page_k, min_score_adj);
}
if (report_skip_count > 0) {
@@ -1625,6 +1806,8 @@
procadjslot_list[i].prev = &procadjslot_list[i];
}
+ memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
+
return 0;
}
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index edebb19..8458480 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -67,6 +67,9 @@
};
#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
+#define PROC_STAT_FILE_PATH "/proc/%d/stat"
+#define PROC_STAT_BUFFER_SIZE 1024
+#define BYTES_IN_KILOBYTE 1024
/**
* Logs the change in LMKD state which is used as start/stop boundaries for logging
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 115b1a3..87bc6ae 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -478,10 +478,10 @@
" -G <size>, --buffer-size=<size>\n"
" Set size of log ring buffer, may suffix with K or M.\n"
" -L, --last Dump logs from prior to last reboot\n"
- // Leave security (Device Owner only installations) and
- // kernel (userdebug and eng) buffers undocumented.
" -b <buffer>, --buffer=<buffer> Request alternate ring buffer, 'main',\n"
" 'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
+ " Additionally, 'kernel' for userdebug and eng builds, and\n"
+ " 'security' for Device Owner installations.\n"
" Multiple -b parameters or comma separated list of buffers are\n"
" allowed. Buffers interleaved. Default -b main,system,crash.\n"
" -B, --binary Output the log in binary.\n"
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index 658e079..bd17555 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -42,7 +42,7 @@
LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
- entry = (*it);
+ entry = it->get();
if (entry->mClient == client) {
if (!entry->isWatchingMultiple(mLogMask)) {
LogTimeEntry::unlock();
@@ -63,31 +63,12 @@
}
}
entry->triggerReader_Locked();
- if (entry->runningReader_Locked()) {
- LogTimeEntry::unlock();
- return;
- }
- entry->incRef_Locked();
- break;
+ LogTimeEntry::unlock();
+ return;
}
it++;
}
- if (it == times.end()) {
- // Create LogTimeEntry in notifyNewLog() ?
- if (mTail == (unsigned long)-1) {
- LogTimeEntry::unlock();
- return;
- }
- entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask,
- mPid, mStart, mTimeout);
- times.push_front(entry);
- }
-
- client->incRef();
-
- // release client and entry reference counts once done
- entry->startReader_Locked();
LogTimeEntry::unlock();
}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 543dfc3..ceaf393 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -27,36 +27,11 @@
class FlushCommand : public SocketClientCommand {
LogReader& mReader;
- bool mNonBlock;
- unsigned long mTail;
log_mask_t mLogMask;
- pid_t mPid;
- log_time mStart;
- uint64_t mTimeout;
public:
- // for opening a reader
- explicit FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
- log_mask_t logMask, pid_t pid, log_time start,
- uint64_t timeout)
- : mReader(reader),
- mNonBlock(nonBlock),
- mTail(tail),
- mLogMask(logMask),
- mPid(pid),
- mStart(start),
- mTimeout((start != log_time::EPOCH) ? timeout : 0) {
- }
-
- // for notification of an update
explicit FlushCommand(LogReader& reader, log_mask_t logMask)
- : mReader(reader),
- mNonBlock(false),
- mTail(-1),
- mLogMask(logMask),
- mPid(0),
- mStart(log_time::EPOCH),
- mTimeout(0) {
+ : mReader(reader), mLogMask(logMask) {
}
virtual void runSocketCommand(SocketClient* client);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index fd1b8b2..fbdbf79 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -105,10 +105,8 @@
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
- if (entry->owned_Locked()) {
- entry->triggerReader_Locked();
- }
+ LogTimeEntry* entry = times->get();
+ entry->triggerReader_Locked();
times++;
}
@@ -409,17 +407,15 @@
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
- if (entry->owned_Locked()) {
- if (!entry->mNonBlock) {
- end_always = true;
- break;
- }
- // it passing mEnd is blocked by the following checks.
- if (!end_set || (end <= entry->mEnd)) {
- end = entry->mEnd;
- end_set = true;
- }
+ LogTimeEntry* entry = times->get();
+ if (!entry->mNonBlock) {
+ end_always = true;
+ break;
+ }
+ // it passing mEnd is blocked by the following checks.
+ if (!end_set || (end <= entry->mEnd)) {
+ end = entry->mEnd;
+ end_set = true;
}
times++;
}
@@ -710,8 +706,8 @@
// Region locked?
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
- if (entry->owned_Locked() && entry->isWatching(id) &&
+ LogTimeEntry* entry = times->get();
+ if (entry->isWatching(id) &&
(!oldest || (oldest->mStart > entry->mStart) ||
((oldest->mStart == entry->mStart) &&
(entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
@@ -1052,9 +1048,9 @@
LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
+ LogTimeEntry* entry = times->get();
// Killer punch
- if (entry->owned_Locked() && entry->isWatching(id)) {
+ if (entry->isWatching(id)) {
entry->release_Locked();
}
times++;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 2b6556d..9db8c00 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -41,6 +41,7 @@
runOnEachSocket(&command);
}
+// Note returning false will release the SocketClient instance.
bool LogReader::onDataAvailable(SocketClient* cli) {
static bool name_set;
if (!name_set) {
@@ -57,6 +58,18 @@
}
buffer[len] = '\0';
+ // Clients are only allowed to send one command, disconnect them if they
+ // send another.
+ LogTimeEntry::wrlock();
+ for (const auto& entry : mLogbuf.mTimes) {
+ if (entry->mClient == cli) {
+ entry->release_Locked();
+ LogTimeEntry::unlock();
+ return false;
+ }
+ }
+ LogTimeEntry::unlock();
+
unsigned long tail = 0;
static const char _tail[] = " tail=";
char* cp = strstr(buffer, _tail);
@@ -199,14 +212,29 @@
cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
logMask, (int)pid, sequence.nsec(), timeout);
- FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+ if (sequence == log_time::EPOCH) {
+ timeout = 0;
+ }
+
+ LogTimeEntry::wrlock();
+ auto entry = std::make_unique<LogTimeEntry>(
+ *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
+ if (!entry->startReader_Locked()) {
+ LogTimeEntry::unlock();
+ return false;
+ }
+
+ // release client and entry reference counts once done
+ cli->incRef();
+ mLogbuf.mTimes.emplace_front(std::move(entry));
// Set acceptable upper limit to wait for slow reader processing b/27242723
struct timeval t = { LOGD_SNDTIMEO, 0 };
setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
sizeof(t));
- command.runSocketCommand(cli);
+ LogTimeEntry::unlock();
+
return true;
}
@@ -215,9 +243,8 @@
LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
- LogTimeEntry* entry = (*it);
+ LogTimeEntry* entry = it->get();
if (entry->mClient == cli) {
- times.erase(it);
entry->release_Locked();
break;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index d6b8ab3..469f6dc 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -531,7 +531,7 @@
name = std::string_view(alloc->c_str(), alloc->size());
}
- explicit TagNameKey(TagNameKey&& rval)
+ explicit TagNameKey(TagNameKey&& rval) noexcept
: alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
rval.alloc = nullptr;
}
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 7a6f84b..1715501 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -30,11 +30,7 @@
LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
bool nonBlock, unsigned long tail, log_mask_t logMask,
pid_t pid, log_time start, uint64_t timeout)
- : mRefCount(1),
- mRelease(false),
- mError(false),
- threadRunning(false),
- leadingDropped(false),
+ : leadingDropped(false),
mReader(reader),
mLogMask(logMask),
mPid(pid),
@@ -52,65 +48,21 @@
cleanSkip_Locked();
}
-void LogTimeEntry::startReader_Locked(void) {
+bool LogTimeEntry::startReader_Locked() {
pthread_attr_t attr;
- threadRunning = true;
-
if (!pthread_attr_init(&attr)) {
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
this)) {
pthread_attr_destroy(&attr);
- return;
+ return true;
}
}
pthread_attr_destroy(&attr);
}
- threadRunning = false;
- if (mClient) {
- mClient->decRef();
- }
- decRef_Locked();
-}
-void LogTimeEntry::threadStop(void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- wrlock();
-
- if (me->mNonBlock) {
- me->error_Locked();
- }
-
- SocketClient* client = me->mClient;
-
- if (me->isError_Locked()) {
- LogReader& reader = me->mReader;
- LastLogTimes& times = reader.logbuf().mTimes;
-
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- if (*it == me) {
- times.erase(it);
- me->release_nodelete_Locked();
- break;
- }
- it++;
- }
-
- me->mClient = nullptr;
- reader.release(client);
- }
-
- if (client) {
- client->decRef();
- }
-
- me->threadRunning = false;
- me->decRef_Locked();
-
- unlock();
+ return false;
}
void* LogTimeEntry::threadStart(void* obj) {
@@ -118,13 +70,7 @@
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- pthread_cleanup_push(threadStop, obj);
-
SocketClient* client = me->mClient;
- if (!client) {
- me->error();
- return nullptr;
- }
LogBuffer& logbuf = me->mReader.logbuf();
@@ -137,14 +83,14 @@
log_time start = me->mStart;
- while (me->threadRunning && !me->isError_Locked()) {
+ while (!me->mRelease) {
if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
if (pthread_cond_timedwait(&me->threadTriggeredCondition,
×Lock, &me->mTimeout) == ETIMEDOUT) {
me->mTimeout.tv_sec = 0;
me->mTimeout.tv_nsec = 0;
}
- if (!me->threadRunning || me->isError_Locked()) {
+ if (me->mRelease) {
break;
}
}
@@ -162,13 +108,12 @@
wrlock();
if (start == LogBufferElement::FLUSH_ERROR) {
- me->error_Locked();
break;
}
me->mStart = start + log_time(0, 1);
- if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
+ if (me->mNonBlock || me->mRelease) {
break;
}
@@ -179,9 +124,21 @@
}
}
- unlock();
+ LogReader& reader = me->mReader;
+ reader.release(client);
- pthread_cleanup_pop(true);
+ client->decRef();
+
+ LastLogTimes& times = reader.logbuf().mTimes;
+ auto it =
+ std::find_if(times.begin(), times.end(),
+ [&me](const auto& other) { return other.get() == me; });
+
+ if (it != times.end()) {
+ times.erase(it);
+ }
+
+ unlock();
return nullptr;
}
@@ -247,10 +204,6 @@
goto skip;
}
- if (me->isError_Locked()) {
- goto stop;
- }
-
if (!me->mTail) {
goto ok;
}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 76d016c..f4e165f 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -22,6 +22,7 @@
#include <time.h>
#include <list>
+#include <memory>
#include <log/log.h>
#include <sysutils/SocketClient.h>
@@ -33,16 +34,12 @@
class LogTimeEntry {
static pthread_mutex_t timesLock;
- unsigned int mRefCount;
- bool mRelease;
- bool mError;
- bool threadRunning;
+ bool mRelease = false;
bool leadingDropped;
pthread_cond_t threadTriggeredCondition;
pthread_t mThread;
LogReader& mReader;
static void* threadStart(void* me);
- static void threadStop(void* me);
const log_mask_t mLogMask;
const pid_t mPid;
unsigned int skipAhead[LOG_ID_MAX];
@@ -73,11 +70,8 @@
pthread_mutex_unlock(×Lock);
}
- void startReader_Locked(void);
+ bool startReader_Locked();
- bool runningReader_Locked(void) const {
- return threadRunning || mRelease || mError || mNonBlock;
- }
void triggerReader_Locked(void) {
pthread_cond_signal(&threadTriggeredCondition);
}
@@ -87,54 +81,11 @@
}
void cleanSkip_Locked(void);
- // These called after LogTimeEntry removed from list, lock implicitly held
- void release_nodelete_Locked(void) {
- mRelease = true;
- pthread_cond_signal(&threadTriggeredCondition);
- // assumes caller code path will call decRef_Locked()
- }
-
void release_Locked(void) {
mRelease = true;
pthread_cond_signal(&threadTriggeredCondition);
- if (mRefCount || threadRunning) {
- return;
- }
- // No one else is holding a reference to this
- delete this;
}
- // Called to mark socket in jeopardy
- void error_Locked(void) {
- mError = true;
- }
- void error(void) {
- wrlock();
- error_Locked();
- unlock();
- }
-
- bool isError_Locked(void) const {
- return mRelease || mError;
- }
-
- // Mark Used
- // Locking implied, grabbed when protection around loop iteration
- void incRef_Locked(void) {
- ++mRefCount;
- }
-
- bool owned_Locked(void) const {
- return mRefCount != 0;
- }
-
- void decRef_Locked(void) {
- if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
- return;
- }
- // No one else is holding a reference to this
- delete this;
- }
bool isWatching(log_id_t id) const {
return mLogMask & (1 << id);
}
@@ -146,6 +97,6 @@
static int FilterSecondPass(const LogBufferElement* element, void* me);
};
-typedef std::list<LogTimeEntry*> LastLogTimes;
+typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
#endif // _LOGD_LOG_TIMES_H__
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b68dc34..aad00ad 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,13 @@
LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
endif
+EXPORT_GLOBAL_HWASAN_OPTIONS :=
+ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
+ ifneq ($(HWADDRESS_SANITIZER_GLOBAL_OPTIONS),)
+ EXPORT_GLOBAL_HWASAN_OPTIONS := export HWASAN_OPTIONS $(HWADDRESS_SANITIZER_GLOBAL_OPTIONS)
+ endif
+endif
+
EXPORT_GLOBAL_GCOV_OPTIONS :=
ifeq ($(NATIVE_COVERAGE),true)
EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
@@ -77,7 +84,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 $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ sbin dev proc sys system data odm oem acct config storage mnt apex $(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; \
@@ -117,6 +124,7 @@
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib64 $(TARGET_ROOT_OUT)/odm/lib64
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/overlay
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
@@ -138,22 +146,14 @@
include $(BUILD_SYSTEM)/base_rules.mk
-# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
-bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
-bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
-$(bcp_dep) :
- $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
@echo "Generate: $< -> $@"
@mkdir -p $(dir $@)
$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
-
-bcp_md5 :=
-bcp_dep :=
+ $(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
# Append PLATFORM_VNDK_VERSION to base name.
define append_vndk_version
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index 6029ae7..ca22eb8 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,3 +1,4 @@
+ccross@google.com
jeffv@google.com
jiyong@google.com
smoreland@google.com
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 2e2ab74..4576776 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -11,3 +11,4 @@
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
%EXPORT_GLOBAL_GCOV_OPTIONS%
+ %EXPORT_GLOBAL_HWASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f39ea7c..025e3c3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -269,6 +269,19 @@
# that they can be chown'd to system:system later on boot
write /sys/class/leds/vibrator/trigger "transient"
+ # Setup APEX mount point and its security context
+ mount tmpfs tmpfs /apex nodev noexec nosuid
+ chmod 0755 /apex
+ chown root root /apex
+ restorecon /apex
+
+ # Start logd before any other services run to ensure we capture all of their logs.
+ start logd
+ # Start essential services.
+ start servicemanager
+ start hwservicemanager
+ start vndservicemanager
+
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
@@ -325,11 +338,8 @@
# /vendor/build.prop and
# /factory/factory.prop
load_system_props
- # start essential services
- start logd
- start servicemanager
- start hwservicemanager
- start vndservicemanager
+ start vold
+ exec - system system -- /system/bin/vdc checkpoint markBootAttempt
# Once everything is setup, no need to modify /.
# The bind+remount combination allows this to work in containers.
@@ -392,6 +402,10 @@
class_start early_hal
on post-fs-data
+ # Start checkpoint before we touch data
+ start vold
+ exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
+
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chmod 0771 /data
@@ -399,7 +413,6 @@
restorecon /data
# Make sure we have the device encryption key.
- start vold
installkey /data
# Start bootcharting as soon as possible after the data partition is
@@ -476,6 +489,8 @@
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
+ mkdir /data/preloads 0775 system system
+
mkdir /data/vendor 0771 root root
mkdir /data/vendor_ce 0771 root root
mkdir /data/vendor_de 0771 root root
@@ -523,6 +538,8 @@
mkdir /data/anr 0775 system system
+ mkdir /data/apex 0770 root root
+
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc
mkdir /data/nfc/param 0770 nfc nfc
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 17ea25b..45f1d4d 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -161,7 +161,7 @@
storaged_sp->update_uid_io_interval(time_window);
}
- return NO_ERROR;
+ return OK;
}
binder::Status StoragedService::onUserStarted(int32_t userId) {
diff --git a/storaged/uid_info.cpp b/storaged/uid_info.cpp
index 58e3fd2..0f718de 100644
--- a/storaged/uid_info.cpp
+++ b/storaged/uid_info.cpp
@@ -32,7 +32,7 @@
parcel->writeCString(task_it.second.comm.c_str());
parcel->write(&task_it.second.io, sizeof(task_it.second.io));
}
- return NO_ERROR;
+ return OK;
}
status_t UidInfo::readFromParcel(const Parcel* parcel) {
@@ -48,5 +48,5 @@
parcel->read(&task.io, sizeof(task.io));
tasks[task.pid] = task;
}
- return NO_ERROR;
+ return OK;
}
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 0849ee9..98cbcc3 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -112,7 +112,8 @@
}
}
}
- KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} {
+ KmParamSet(KmParamSet&& other) noexcept
+ : keymaster_key_param_set_t{other.params, other.length} {
other.length = 0;
other.params = nullptr;
}