Merge "fastboot: Allow fastboot to asynchronously differentiate between fastboot and fastbootd."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9b6213a..7933629 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -28,12 +28,6 @@
"name": "fs_mgr_vendor_overlay_test"
},
{
- "name": "init_kill_services_test"
- },
- {
- "name": "libbase_test"
- },
- {
"name": "libpackagelistparser_test"
},
{
@@ -63,15 +57,6 @@
},
{
"name": "propertyinfoserializer_tests"
- },
- {
- "name": "ziparchive-tests"
- }
- ],
-
- "postsubmit": [
- {
- "name": "ziparchive_tests_large"
}
]
}
diff --git a/adb/Android.bp b/adb/Android.bp
index 432770c..9db151d 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -218,6 +218,7 @@
"client/usb_dispatch.cpp",
"client/transport_local.cpp",
"client/transport_mdns.cpp",
+ "client/mdns_utils.cpp",
"client/transport_usb.cpp",
"client/pairing/pairing_client.cpp",
],
@@ -264,7 +265,10 @@
cc_test_host {
name: "adb_test",
defaults: ["adb_defaults"],
- srcs: libadb_test_srcs,
+ srcs: libadb_test_srcs + [
+ "client/mdns_utils_test.cpp",
+ ],
+
static_libs: [
"libadb_crypto_static",
"libadb_host",
diff --git a/adb/adb.cpp b/adb/adb.cpp
index dcec0ba..08986b7 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -109,7 +109,9 @@
{
D("adb: online");
t->online = 1;
+#if ADB_HOST
t->SetConnectionEstablished(true);
+#endif
}
void handle_offline(atransport *t)
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 43a9252..124e2d8 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -73,6 +73,7 @@
typedef std::list<std::unique_ptr<alistener>> ListenerList;
static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
+#if ADB_HOST
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
if (ev & FDE_READ) {
unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
@@ -88,6 +89,7 @@
}
}
}
+#endif
static void listener_event_func(int _fd, unsigned ev, void* _l)
{
@@ -164,7 +166,7 @@
}
}
-void enable_daemon_sockets() EXCLUDES(listener_list_mutex) {
+void enable_server_sockets() EXCLUDES(listener_list_mutex) {
std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto& l : listener_list) {
if (l->connect_to == "*smartsocket*") {
@@ -173,6 +175,7 @@
}
}
+#if ADB_HOST
void close_smartsockets() EXCLUDES(listener_list_mutex) {
std::lock_guard<std::mutex> lock(listener_list_mutex);
auto pred = [](const std::unique_ptr<alistener>& listener) {
@@ -180,6 +183,7 @@
};
listener_list.remove_if(pred);
}
+#endif
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
atransport* transport, int flags, int* resolved_tcp_port,
@@ -188,7 +192,7 @@
for (auto& l : listener_list) {
if (local_name == l->local_name) {
// Can't repurpose a smartsocket.
- if(l->connect_to[0] == '*') {
+ if (l->connect_to[0] == '*') {
*error = "cannot repurpose smartsocket";
return INSTALL_STATUS_INTERNAL_ERROR;
}
@@ -227,7 +231,11 @@
close_on_exec(listener->fd);
if (listener->connect_to == "*smartsocket*") {
+#if ADB_HOST
listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get());
+#else
+ LOG(FATAL) << "attempted to connect to *smartsocket* in daemon";
+#endif
} else {
listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
}
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 354dcc5..0aa774a 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __ADB_LISTENERS_H
-#define __ADB_LISTENERS_H
+#pragma once
#include "adb.h"
@@ -44,7 +43,7 @@
InstallStatus remove_listener(const char* local_name, atransport* transport);
void remove_all_listeners(void);
-void enable_daemon_sockets();
+#if ADB_HOST
+void enable_server_sockets();
void close_smartsockets();
-
-#endif /* __ADB_LISTENERS_H */
+#endif
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index c579dde..210241c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -90,7 +90,7 @@
int adb_trace_mask;
std::string get_trace_setting() {
-#if ADB_HOST
+#if ADB_HOST || !defined(__ANDROID__)
const char* setting = getenv("ADB_TRACE");
if (setting == nullptr) {
setting = "";
diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h
index 3a6b0b1..42f414b 100644
--- a/adb/adb_wifi.h
+++ b/adb/adb_wifi.h
@@ -16,6 +16,7 @@
#pragma once
+#include <optional>
#include <string>
#include "adb.h"
@@ -30,6 +31,19 @@
std::string mdns_check();
std::string mdns_list_discovered_services();
+struct MdnsInfo {
+ std::string service_name;
+ std::string service_type;
+ std::string addr;
+ uint16_t port = 0;
+
+ MdnsInfo(std::string_view name, std::string_view type, std::string_view addr, uint16_t port)
+ : service_name(name), service_type(type), addr(addr), port(port) {}
+};
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name);
+std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name);
+
#else // !ADB_HOST
struct AdbdAuthContext;
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 31c938c..a308732 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -417,17 +417,19 @@
}
const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
- static std::string cached_error [[clang::no_destroy]];
- static const std::optional<FeatureSet> features
- [[clang::no_destroy]] ([]() -> std::optional<FeatureSet> {
- std::string result;
- if (adb_query(format_host_command("features"), &result, &cached_error)) {
- return StringToFeatureSet(result);
- }
- return std::nullopt;
- }());
- if (!features && error) {
- *error = cached_error;
+ static std::mutex feature_mutex [[clang::no_destroy]];
+ static std::optional<FeatureSet> features [[clang::no_destroy]] GUARDED_BY(feature_mutex);
+ std::lock_guard<std::mutex> lock(feature_mutex);
+ if (!features) {
+ std::string result;
+ std::string err;
+ if (adb_query(format_host_command("features"), &result, &err)) {
+ features = StringToFeatureSet(result);
+ } else {
+ if (error) {
+ *error = err;
+ }
+ }
}
return features;
}
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index e562f8b..d6f536e 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -301,7 +301,7 @@
}
template <class TimePoint>
-static int msBetween(TimePoint start, TimePoint end) {
+static int ms_between(TimePoint start, TimePoint end) {
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}
@@ -310,7 +310,7 @@
const auto start = clock::now();
int first_apk = -1;
int last_apk = -1;
- std::vector<std::string_view> args = {"package"sv};
+ incremental::Args passthrough_args = {};
for (int i = 0; i < argc; ++i) {
const auto arg = std::string_view(argv[i]);
if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) {
@@ -318,12 +318,11 @@
if (first_apk == -1) {
first_apk = i;
}
- } else if (arg.starts_with("install-"sv)) {
+ } else if (arg.starts_with("install"sv)) {
// incremental installation command on the device is the same for all its variations in
// the adb, e.g. install-multiple or install-multi-package
- args.push_back("install"sv);
} else {
- args.push_back(arg);
+ passthrough_args.push_back(arg);
}
}
@@ -344,13 +343,13 @@
}
printf("Performing Incremental Install\n");
- auto server_process = incremental::install(files, silent);
+ auto server_process = incremental::install(files, passthrough_args, silent);
if (!server_process) {
return -1;
}
const auto end = clock::now();
- printf("Install command complete in %d ms\n", msBetween(start, end));
+ printf("Install command complete in %d ms\n", ms_between(start, end));
if (wait) {
(*server_process).wait();
@@ -359,9 +358,9 @@
return 0;
}
-static std::pair<InstallMode, std::optional<InstallMode>> calculateInstallMode(
- InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incrementalRequest) {
- if (incrementalRequest == CmdlineOption::Enable) {
+static std::pair<InstallMode, std::optional<InstallMode>> calculate_install_mode(
+ InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incremental_request) {
+ if (incremental_request == CmdlineOption::Enable) {
if (fastdeploy) {
error_exit(
"--incremental and --fast-deploy options are incompatible. "
@@ -370,30 +369,30 @@
}
if (modeFromArgs != INSTALL_DEFAULT) {
- if (incrementalRequest == CmdlineOption::Enable) {
+ if (incremental_request == CmdlineOption::Enable) {
error_exit("--incremental is not compatible with other installation modes");
}
return {modeFromArgs, std::nullopt};
}
- if (incrementalRequest != CmdlineOption::Disable && !is_abb_exec_supported()) {
- if (incrementalRequest == CmdlineOption::None) {
- incrementalRequest = CmdlineOption::Disable;
+ if (incremental_request != CmdlineOption::Disable && !is_abb_exec_supported()) {
+ if (incremental_request == CmdlineOption::None) {
+ incremental_request = CmdlineOption::Disable;
} else {
error_exit("Device doesn't support incremental installations");
}
}
- if (incrementalRequest == CmdlineOption::None) {
+ if (incremental_request == CmdlineOption::None) {
// check if the host is ok with incremental by default
if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) {
using namespace android::base;
auto val = ParseBool(incrementalFromEnv);
if (val == ParseBoolResult::kFalse) {
- incrementalRequest = CmdlineOption::Disable;
+ incremental_request = CmdlineOption::Disable;
}
}
}
- if (incrementalRequest == CmdlineOption::None) {
+ if (incremental_request == CmdlineOption::None) {
// still ok: let's see if the device allows using incremental by default
// it starts feeling like we're looking for an excuse to not to use incremental...
std::string error;
@@ -409,17 +408,17 @@
using namespace android::base;
auto val = ParseBool(buf);
if (val == ParseBoolResult::kFalse) {
- incrementalRequest = CmdlineOption::Disable;
+ incremental_request = CmdlineOption::Disable;
}
}
}
- if (incrementalRequest == CmdlineOption::Enable) {
+ if (incremental_request == CmdlineOption::Enable) {
// explicitly requested - no fallback
return {INSTALL_INCREMENTAL, std::nullopt};
}
const auto bestMode = best_install_mode();
- if (incrementalRequest == CmdlineOption::None) {
+ if (incremental_request == CmdlineOption::None) {
// no opinion - use incremental, fallback to regular on a failure.
return {INSTALL_INCREMENTAL, bestMode};
}
@@ -427,57 +426,75 @@
return {bestMode, std::nullopt};
}
-int install_app(int argc, const char** argv) {
- std::vector<int> processedArgIndices;
- InstallMode installMode = INSTALL_DEFAULT;
- bool use_fastdeploy = false;
- bool is_reinstall = false;
- bool wait = false;
- auto incremental_request = CmdlineOption::None;
- FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+static std::vector<const char*> parse_install_mode(std::vector<const char*> argv,
+ InstallMode* install_mode,
+ CmdlineOption* incremental_request,
+ bool* incremental_wait) {
+ *install_mode = INSTALL_DEFAULT;
+ *incremental_request = CmdlineOption::None;
+ *incremental_wait = false;
- for (int i = 1; i < argc; i++) {
- if (argv[i] == "--streaming"sv) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_STREAM;
- } else if (argv[i] == "--no-streaming"sv) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_PUSH;
- } else if (argv[i] == "-r"sv) {
- // Note that this argument is not added to processedArgIndices because it
- // must be passed through to pm
- is_reinstall = true;
- } else if (argv[i] == "--fastdeploy"sv) {
- processedArgIndices.push_back(i);
- use_fastdeploy = true;
- } else if (argv[i] == "--no-fastdeploy"sv) {
- processedArgIndices.push_back(i);
- use_fastdeploy = false;
- } else if (argv[i] == "--force-agent"sv) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateAlways;
- } else if (argv[i] == "--date-check-agent"sv) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
- } else if (argv[i] == "--version-check-agent"sv) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
- } else if (strlen(argv[i]) >= "--incr"sv.size() && "--incremental"sv.starts_with(argv[i])) {
- processedArgIndices.push_back(i);
- incremental_request = CmdlineOption::Enable;
- } else if (strlen(argv[i]) >= "--no-incr"sv.size() &&
- "--no-incremental"sv.starts_with(argv[i])) {
- processedArgIndices.push_back(i);
- incremental_request = CmdlineOption::Disable;
- } else if (argv[i] == "--wait"sv) {
- processedArgIndices.push_back(i);
- wait = true;
+ std::vector<const char*> passthrough;
+ for (auto&& arg : argv) {
+ if (arg == "--streaming"sv) {
+ *install_mode = INSTALL_STREAM;
+ } else if (arg == "--no-streaming"sv) {
+ *install_mode = INSTALL_PUSH;
+ } else if (strlen(arg) >= "--incr"sv.size() && "--incremental"sv.starts_with(arg)) {
+ *incremental_request = CmdlineOption::Enable;
+ } else if (strlen(arg) >= "--no-incr"sv.size() && "--no-incremental"sv.starts_with(arg)) {
+ *incremental_request = CmdlineOption::Disable;
+ } else if (arg == "--wait"sv) {
+ *incremental_wait = true;
+ } else {
+ passthrough.push_back(arg);
}
}
+ return passthrough;
+}
- auto [primaryMode, fallbackMode] =
- calculateInstallMode(installMode, use_fastdeploy, incremental_request);
- if ((primaryMode == INSTALL_STREAM || fallbackMode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+static std::vector<const char*> parse_fast_deploy_mode(
+ std::vector<const char*> argv, bool* use_fastdeploy,
+ FastDeploy_AgentUpdateStrategy* agent_update_strategy) {
+ *use_fastdeploy = false;
+ *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+ std::vector<const char*> passthrough;
+ for (auto&& arg : argv) {
+ if (arg == "--fastdeploy"sv) {
+ *use_fastdeploy = true;
+ } else if (arg == "--no-fastdeploy"sv) {
+ *use_fastdeploy = false;
+ } else if (arg == "--force-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateAlways;
+ } else if (arg == "--date-check-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+ } else if (arg == "--version-check-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+ } else {
+ passthrough.push_back(arg);
+ }
+ }
+ return passthrough;
+}
+
+int install_app(int argc, const char** argv) {
+ InstallMode install_mode = INSTALL_DEFAULT;
+ auto incremental_request = CmdlineOption::None;
+ bool incremental_wait = false;
+
+ bool use_fastdeploy = false;
+ FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+ auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request,
+ &incremental_wait);
+ auto passthrough_argv =
+ parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy);
+
+ auto [primary_mode, fallback_mode] =
+ calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+ if ((primary_mode == INSTALL_STREAM ||
+ fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
best_install_mode() == INSTALL_PUSH) {
error_exit("Attempting to use streaming install on unsupported device");
}
@@ -491,19 +508,12 @@
}
fastdeploy_set_agent_update_strategy(agent_update_strategy);
- std::vector<const char*> passthrough_argv;
- for (int i = 0; i < argc; i++) {
- if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
- processedArgIndices.end()) {
- passthrough_argv.push_back(argv[i]);
- }
- }
if (passthrough_argv.size() < 2) {
error_exit("install requires an apk argument");
}
- auto runInstallMode = [&](InstallMode installMode, bool silent) {
- switch (installMode) {
+ auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+ switch (install_mode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
@@ -512,20 +522,20 @@
use_fastdeploy);
case INSTALL_INCREMENTAL:
return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
- wait, silent);
+ incremental_wait, silent);
case INSTALL_DEFAULT:
default:
- return 1;
+ error_exit("invalid install mode");
}
};
- auto res = runInstallMode(primaryMode, fallbackMode.has_value());
- if (res && fallbackMode.value_or(primaryMode) != primaryMode) {
- res = runInstallMode(*fallbackMode, false);
+ auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+ if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+ res = run_install_mode(*fallback_mode, false);
}
return res;
}
-int install_multiple_app(int argc, const char** argv) {
+static int install_multiple_app_streamed(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
@@ -551,7 +561,6 @@
if (first_apk == -1) error_exit("need APK file on command line");
const bool use_abb_exec = is_abb_exec_supported();
-
const std::string install_cmd =
use_abb_exec ? "package"
: best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
@@ -674,6 +683,44 @@
return EXIT_SUCCESS;
}
+int install_multiple_app(int argc, const char** argv) {
+ InstallMode install_mode = INSTALL_DEFAULT;
+ auto incremental_request = CmdlineOption::None;
+ bool incremental_wait = false;
+ bool use_fastdeploy = false;
+
+ auto passthrough_argv = parse_install_mode({argv + 1, argv + argc}, &install_mode,
+ &incremental_request, &incremental_wait);
+
+ auto [primary_mode, fallback_mode] =
+ calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+ if ((primary_mode == INSTALL_STREAM ||
+ fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+ best_install_mode() == INSTALL_PUSH) {
+ error_exit("Attempting to use streaming install on unsupported device");
+ }
+
+ auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+ switch (install_mode) {
+ case INSTALL_PUSH:
+ case INSTALL_STREAM:
+ return install_multiple_app_streamed(passthrough_argv.size(),
+ passthrough_argv.data());
+ case INSTALL_INCREMENTAL:
+ return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
+ incremental_wait, silent);
+ case INSTALL_DEFAULT:
+ default:
+ error_exit("invalid install mode");
+ }
+ };
+ auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+ if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+ res = run_install_mode(*fallback_mode, false);
+ }
+ return res;
+}
+
int install_multi_package(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
@@ -698,23 +745,31 @@
fprintf(stderr, "adb: multi-package install is not supported on this device\n");
return EXIT_FAILURE;
}
- std::string install_cmd = "exec:cmd package";
- std::string multi_package_cmd =
- android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+ const bool use_abb_exec = is_abb_exec_supported();
+ const std::string install_cmd = use_abb_exec ? "package" : "exec:cmd package";
+
+ std::vector<std::string> multi_package_cmd_args = {install_cmd, "install-create",
+ "--multi-package"};
+
+ multi_package_cmd_args.reserve(first_package + 4);
for (int i = 1; i < first_package; i++) {
- multi_package_cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ multi_package_cmd_args.push_back(argv[i]);
+ } else {
+ multi_package_cmd_args.push_back(escape_arg(argv[i]));
+ }
}
if (apex_found) {
- multi_package_cmd += " --staged";
+ multi_package_cmd_args.emplace_back("--staged");
}
// Create multi-package install session
std::string error;
char buf[BUFSIZ];
{
- unique_fd fd(adb_connect(multi_package_cmd, &error));
+ unique_fd fd = send_command(multi_package_cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
return EXIT_FAILURE;
@@ -736,6 +791,7 @@
fputs(buf, stderr);
return EXIT_FAILURE;
}
+ const auto parent_session_id_str = std::to_string(parent_session_id);
fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
@@ -743,17 +799,30 @@
// Valid session, now create the individual sessions and stream the APKs
int success = EXIT_FAILURE;
- std::string individual_cmd =
- android::base::StringPrintf("%s install-create", install_cmd.c_str());
- std::string all_session_ids = "";
+ std::vector<std::string> individual_cmd_args = {install_cmd, "install-create"};
for (int i = 1; i < first_package; i++) {
- individual_cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ individual_cmd_args.push_back(argv[i]);
+ } else {
+ individual_cmd_args.push_back(escape_arg(argv[i]));
+ }
}
if (apex_found) {
- individual_cmd += " --staged";
+ individual_cmd_args.emplace_back("--staged");
}
- std::string individual_apex_cmd = individual_cmd + " --apex";
- std::string cmd = "";
+
+ std::vector<std::string> individual_apex_cmd_args;
+ if (apex_found) {
+ individual_apex_cmd_args = individual_cmd_args;
+ individual_apex_cmd_args.emplace_back("--apex");
+ }
+
+ std::vector<std::string> add_session_cmd_args = {
+ install_cmd,
+ "install-add-session",
+ parent_session_id_str,
+ };
+
for (int i = first_package; i < argc; i++) {
const char* file = argv[i];
char buf[BUFSIZ];
@@ -761,9 +830,9 @@
unique_fd fd;
// Create individual install session
if (android::base::EndsWithIgnoreCase(file, ".apex")) {
- fd.reset(adb_connect(individual_apex_cmd, &error));
+ fd = send_command(individual_apex_cmd_args, &error);
} else {
- fd.reset(adb_connect(individual_cmd, &error));
+ fd = send_command(individual_cmd_args, &error);
}
if (fd < 0) {
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
@@ -786,6 +855,7 @@
fputs(buf, stderr);
goto finalize_multi_package_session;
}
+ const auto session_id_str = std::to_string(session_id);
fprintf(stdout, "Created child session ID %d.\n", session_id);
session_ids.push_back(session_id);
@@ -800,10 +870,15 @@
goto finalize_multi_package_session;
}
- std::string cmd = android::base::StringPrintf(
- "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
- static_cast<uint64_t>(sb.st_size), session_id, i,
- android::base::Basename(split).c_str());
+ std::vector<std::string> cmd_args = {
+ install_cmd,
+ "install-write",
+ "-S",
+ std::to_string(sb.st_size),
+ session_id_str,
+ android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+ "-",
+ };
unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
if (local_fd < 0) {
@@ -812,7 +887,7 @@
}
std::string error;
- unique_fd remote_fd(adb_connect(cmd, &error));
+ unique_fd remote_fd = send_command(cmd_args, &error);
if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
goto finalize_multi_package_session;
@@ -831,13 +906,11 @@
goto finalize_multi_package_session;
}
}
- all_session_ids += android::base::StringPrintf(" %d", session_id);
+ add_session_cmd_args.push_back(std::to_string(session_id));
}
- cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
- parent_session_id, all_session_ids.c_str());
{
- unique_fd fd(adb_connect(cmd, &error));
+ unique_fd fd = send_command(add_session_cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
goto finalize_multi_package_session;
@@ -846,7 +919,8 @@
}
if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+ fprintf(stderr, "adb: failed to link sessions (%s)\n",
+ android::base::Join(add_session_cmd_args, " ").c_str());
fputs(buf, stderr);
goto finalize_multi_package_session;
}
@@ -856,11 +930,14 @@
finalize_multi_package_session:
// Commit session if we streamed everything okay; otherwise abandon
- std::string service =
- android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
- success == 0 ? "commit" : "abandon", parent_session_id);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ success == 0 ? "install-commit" : "install-abandon",
+ parent_session_id_str,
+ };
+
{
- unique_fd fd(adb_connect(service, &error));
+ unique_fd fd = send_command(service_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
return EXIT_FAILURE;
@@ -881,10 +958,13 @@
session_ids.push_back(parent_session_id);
// try to abandon all remaining sessions
for (std::size_t i = 0; i < session_ids.size(); i++) {
- service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
- session_ids[i]);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ "install-abandon",
+ std::to_string(session_ids[i]),
+ };
fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
- unique_fd fd(adb_connect(service, &error));
+ unique_fd fd = send_command(service_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
continue;
diff --git a/adb/client/adb_wifi.cpp b/adb/client/adb_wifi.cpp
index fa71028..61a9a48 100644
--- a/adb/client/adb_wifi.cpp
+++ b/adb/client/adb_wifi.cpp
@@ -179,17 +179,21 @@
void adb_wifi_pair_device(const std::string& host, const std::string& password,
std::string& response) {
- // Check the address for a valid address and port.
- std::string parsed_host;
- std::string err;
- int port = -1;
- if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
- response = "Failed to parse address for pairing: " + err;
- return;
- }
- if (port <= 0 || port > 65535) {
- response = "Invalid port while parsing address [" + host + "]";
- return;
+ auto mdns_info = mdns_get_pairing_service_info(host);
+
+ if (!mdns_info.has_value()) {
+ // Check the address for a valid address and port.
+ std::string parsed_host;
+ std::string err;
+ int port = -1;
+ if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
+ response = "Failed to parse address for pairing: " + err;
+ return;
+ }
+ if (port <= 0 || port > 65535) {
+ response = "Invalid port while parsing address [" + host + "]";
+ return;
+ }
}
auto priv_key = adb_auth_get_user_privkey();
@@ -220,7 +224,11 @@
PairingResultWaiter waiter;
std::unique_lock<std::mutex> lock(waiter.mutex_);
- if (!client->Start(host, waiter.OnResult, &waiter)) {
+ if (!client->Start(mdns_info.has_value()
+ ? android::base::StringPrintf("%s:%d", mdns_info->addr.c_str(),
+ mdns_info->port)
+ : host,
+ waiter.OnResult, &waiter)) {
response = "Failed: Unable to start pairing client.";
return;
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index f0a287d..eaa32e5 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -104,7 +104,8 @@
" connect HOST[:PORT] connect to a device via TCP/IP [default port=5555]\n"
" disconnect [HOST[:PORT]]\n"
" disconnect from given TCP/IP device [default port=5555], or all\n"
- " pair HOST[:PORT] pair with a device for secure TCP/IP communication\n"
+ " pair HOST[:PORT] [PAIRING CODE]\n"
+ " pair with a device for secure TCP/IP communication\n"
" forward --list list all forward socket connections\n"
" forward [--no-rebind] LOCAL REMOTE\n"
" forward socket connection using:\n"
@@ -1712,14 +1713,21 @@
}
printf("List of devices attached\n");
return adb_query_command(query);
- }
- else if (!strcmp(argv[0], "connect")) {
+ } else if (!strcmp(argv[0], "transport-id")) {
+ TransportId transport_id;
+ std::string error;
+ unique_fd fd(adb_connect(&transport_id, "host:features", &error, true));
+ if (fd == -1) {
+ error_exit("%s", error.c_str());
+ }
+ printf("%" PRIu64 "\n", transport_id);
+ return 0;
+ } else if (!strcmp(argv[0], "connect")) {
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")) {
+ } else if (!strcmp(argv[0], "disconnect")) {
if (argc > 2) error_exit("usage: adb disconnect [HOST[:PORT]]");
std::string query = android::base::StringPrintf("host:disconnect:%s",
@@ -1728,13 +1736,17 @@
} else if (!strcmp(argv[0], "abb")) {
return adb_abb(argc, argv);
} else if (!strcmp(argv[0], "pair")) {
- if (argc != 2) error_exit("usage: adb pair <host>[:<port>]");
+ if (argc < 2 || argc > 3) error_exit("usage: adb pair HOST[:PORT] [PAIRING CODE]");
std::string password;
- printf("Enter pairing code: ");
- fflush(stdout);
- if (!std::getline(std::cin, password) || password.empty()) {
- error_exit("No pairing code provided");
+ if (argc == 2) {
+ printf("Enter pairing code: ");
+ fflush(stdout);
+ if (!std::getline(std::cin, password) || password.empty()) {
+ error_exit("No pairing code provided");
+ }
+ } else {
+ password = argv[2];
}
std::string query =
android::base::StringPrintf("host:pair:%s:%s", password.c_str(), argv[1]);
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 2814932..a8b0ab3 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -93,12 +93,10 @@
// Send install-incremental to the device along with properly configured file descriptors in
// streaming format. Once connection established, send all fs-verity tree bytes.
-static unique_fd start_install(const Files& files, bool silent) {
+static unique_fd start_install(const Files& files, const Args& passthrough_args, bool silent) {
std::vector<std::string> command_args{"package", "install-incremental"};
+ command_args.insert(command_args.end(), passthrough_args.begin(), passthrough_args.end());
- // fd's with positions at the beginning of fs-verity
- std::vector<unique_fd> signature_fds;
- signature_fds.reserve(files.size());
for (int i = 0, size = files.size(); i < size; ++i) {
const auto& file = files[i];
@@ -118,8 +116,6 @@
auto file_desc = StringPrintf("%s:%lld:%d:%s:1", android::base::Basename(file).c_str(),
(long long)st.st_size, i, signature.c_str());
command_args.push_back(std::move(file_desc));
-
- signature_fds.push_back(std::move(signature_fd));
}
std::string error;
@@ -150,8 +146,8 @@
return true;
}
-std::optional<Process> install(const Files& files, bool silent) {
- auto connection_fd = start_install(files, silent);
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent) {
+ auto connection_fd = start_install(files, passthrough_args, silent);
if (connection_fd < 0) {
if (!silent) {
fprintf(stderr, "adb: failed to initiate installation on device.\n");
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
index 1fb1e0b..40e928a 100644
--- a/adb/client/incremental.h
+++ b/adb/client/incremental.h
@@ -26,9 +26,10 @@
namespace incremental {
using Files = std::vector<std::string>;
+using Args = std::vector<std::string_view>;
bool can_install(const Files& files);
-std::optional<Process> install(const Files& files, bool silent);
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent);
enum class Result { Success, Failure, None };
Result wait_for_installation(int read_fd);
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 05e210f..a19bd6d 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -206,7 +206,7 @@
// We don't accept() client connections until this point: this way, clients
// can't see wonky state early in startup even if they're connecting directly
// to the server instead of going through the adb program.
- fdevent_run_on_main_thread([] { enable_daemon_sockets(); });
+ fdevent_run_on_main_thread([] { enable_server_sockets(); });
});
notify_thread.detach();
diff --git a/adb/client/mdns_utils.cpp b/adb/client/mdns_utils.cpp
new file mode 100644
index 0000000..8666b18
--- /dev/null
+++ b/adb/client/mdns_utils.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 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 "client/mdns_utils.h"
+
+#include <android-base/strings.h>
+
+namespace mdns {
+
+// <Instance>.<Service>.<Domain>
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name) {
+ CHECK(!name.empty());
+
+ // Return the whole name if it doesn't fall under <Instance>.<Service>.<Domain> or
+ // <Instance>.<Service>
+ bool has_local_suffix = false;
+ // Strip the local suffix, if any
+ {
+ std::string local_suffix = ".local";
+ local_suffix += android::base::EndsWith(name, ".") ? "." : "";
+
+ if (android::base::ConsumeSuffix(&name, local_suffix)) {
+ if (name.empty()) {
+ return std::nullopt;
+ }
+ has_local_suffix = true;
+ }
+ }
+
+ std::string transport;
+ // Strip the transport suffix, if any
+ {
+ std::string add_dot = (!has_local_suffix && android::base::EndsWith(name, ".")) ? "." : "";
+ std::array<std::string, 2> transport_suffixes{"._tcp", "._udp"};
+
+ for (const auto& t : transport_suffixes) {
+ if (android::base::ConsumeSuffix(&name, t + add_dot)) {
+ if (name.empty()) {
+ return std::nullopt;
+ }
+ transport = t.substr(1);
+ break;
+ }
+ }
+
+ if (has_local_suffix && transport.empty()) {
+ return std::nullopt;
+ }
+ }
+
+ if (!has_local_suffix && transport.empty()) {
+ return std::make_optional<MdnsInstance>(name, "", "");
+ }
+
+ // Split the service name from the instance name
+ auto pos = name.rfind(".");
+ if (pos == 0 || pos == std::string::npos || pos == name.size() - 1) {
+ return std::nullopt;
+ }
+
+ return std::make_optional<MdnsInstance>(name.substr(0, pos), name.substr(pos + 1), transport);
+}
+
+} // namespace mdns
diff --git a/adb/client/mdns_utils.h b/adb/client/mdns_utils.h
new file mode 100644
index 0000000..40d095d
--- /dev/null
+++ b/adb/client/mdns_utils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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 <optional>
+#include <string_view>
+
+#include "adb_wifi.h"
+
+namespace mdns {
+
+struct MdnsInstance {
+ std::string instance_name; // "my name"
+ std::string service_name; // "_adb-tls-connect"
+ std::string transport_type; // either "_tcp" or "_udp"
+
+ MdnsInstance(std::string_view inst, std::string_view serv, std::string_view trans)
+ : instance_name(inst), service_name(serv), transport_type(trans) {}
+};
+
+// This parser is based on https://tools.ietf.org/html/rfc6763#section-4.1 for
+// structured service instance names, where the whole name is in the format
+// <Instance>.<Service>.<Domain>.
+//
+// In our case, we ignore <Domain> portion of the name, which
+// we always assume to be ".local", or link-local mDNS.
+//
+// The string can be in one of the following forms:
+// - <Instance>.<Service>.<Domain>.?
+// - e.g. "instance._service._tcp.local" (or "...local.")
+// - <Instance>.<Service>.? (must contain either "_tcp" or "_udp" at the end)
+// - e.g. "instance._service._tcp" (or "..._tcp.)
+// - <Instance> (can contain dots '.')
+// - e.g. "myname", "name.", "my.name."
+//
+// Returns an MdnsInstance with the appropriate fields filled in (instance name is never empty),
+// otherwise returns std::nullopt.
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name);
+
+} // namespace mdns
diff --git a/adb/client/mdns_utils_test.cpp b/adb/client/mdns_utils_test.cpp
new file mode 100644
index 0000000..ec71529
--- /dev/null
+++ b/adb/client/mdns_utils_test.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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 "client/mdns_utils.h"
+
+#include <gtest/gtest.h>
+
+namespace mdns {
+
+TEST(mdns_utils, mdns_parse_instance_name) {
+ // Just the instance name
+ {
+ std::string str = ".";
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(str, res->instance_name);
+ EXPECT_TRUE(res->service_name.empty());
+ EXPECT_TRUE(res->transport_type.empty());
+ }
+ {
+ std::string str = "my.name";
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(str, res->instance_name);
+ EXPECT_TRUE(res->service_name.empty());
+ EXPECT_TRUE(res->transport_type.empty());
+ }
+ {
+ std::string str = "my.name.";
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(str, res->instance_name);
+ EXPECT_TRUE(res->service_name.empty());
+ EXPECT_TRUE(res->transport_type.empty());
+ }
+
+ // With "_tcp", "_udp" transport type
+ for (const std::string_view transport : {"._tcp", "._udp"}) {
+ {
+ std::string str = android::base::StringPrintf("%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf(".service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("service.%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("my.service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("my.service%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("my..service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my.");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("my.name.service%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my.name");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("name.service.%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+
+ // With ".local" domain
+ {
+ std::string str = ".local";
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = ".local.";
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = "name.local";
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("%s.local", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("service%s.local", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("name.service%s.local", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "name");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str =
+ android::base::StringPrintf("name.service%s.local.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "name");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str =
+ android::base::StringPrintf("name.service%s..local.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str =
+ android::base::StringPrintf("name.service.%s.local.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ }
+}
+
+} // namespace mdns
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 2b6aa7c..9db2453 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -38,6 +38,7 @@
#include "adb_trace.h"
#include "adb_utils.h"
#include "adb_wifi.h"
+#include "client/mdns_utils.h"
#include "fdevent/fdevent.h"
#include "sysdeps.h"
@@ -144,7 +145,7 @@
return initialized_;
}
- virtual ~AsyncServiceRef() {
+ void DestroyServiceRef() {
if (!initialized_) {
return;
}
@@ -152,9 +153,13 @@
// Order matters here! Must destroy the fdevent first since it has a
// reference to |sdRef_|.
fdevent_destroy(fde_);
+ D("DNSServiceRefDeallocate(sdRef=%p)", sdRef_);
DNSServiceRefDeallocate(sdRef_);
+ initialized_ = false;
}
+ virtual ~AsyncServiceRef() { DestroyServiceRef(); }
+
protected:
DNSServiceRef sdRef_;
@@ -203,6 +208,7 @@
if (ret != kDNSServiceErr_NoError) {
D("Got %d from DNSServiceGetAddrInfo.", ret);
} else {
+ D("DNSServiceGetAddrInfo(sdRef=%p, hosttarget=%s)", sdRef_, hosttarget);
Initialize();
}
@@ -216,14 +222,14 @@
}
std::string response;
- connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+ connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(), regType_.c_str()),
&response);
D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
ip_addr_, port_, response.c_str());
return true;
}
- void Connect(const sockaddr* address) {
+ bool AddToServiceRegistry(const sockaddr* address) {
sa_family_ = address->sa_family;
if (sa_family_ == AF_INET) {
@@ -234,26 +240,53 @@
addr_format_ = "[%s]:%hu";
} else { // Should be impossible
D("mDNS resolved non-IP address.");
- return;
+ return false;
}
// Winsock version requires the const cast Because Microsoft.
if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
D("Could not convert IP address to string.");
- return;
+ return false;
}
- // adb secure service needs to do something different from just
- // connecting here.
+ // Add to the service registry before trying to auto-connect, since socket_spec_connect will
+ // check these registries for the ip address when connecting via mdns instance name.
+ int adbSecureServiceType = serviceIndex();
+ ServiceRegistry* services = nullptr;
+ switch (adbSecureServiceType) {
+ case kADBTransportServiceRefIndex:
+ services = sAdbTransportServices;
+ break;
+ case kADBSecurePairingServiceRefIndex:
+ services = sAdbSecurePairingServices;
+ break;
+ case kADBSecureConnectServiceRefIndex:
+ services = sAdbSecureConnectServices;
+ break;
+ default:
+ LOG(WARNING) << "No registry available for reg_type=[" << regType_ << "]";
+ return false;
+ }
+
+ if (!services->empty()) {
+ // Remove the previous resolved service, if any.
+ services->erase(std::remove_if(services->begin(), services->end(),
+ [&](std::unique_ptr<ResolvedService>& service) {
+ return (serviceName_ == service->serviceName());
+ }));
+ }
+ services->push_back(std::unique_ptr<ResolvedService>(this));
+
if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
std::string response;
- D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(),
- regType_.c_str(), ip_addr_, port_);
+ D("Attempting to connect serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
+ serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
int index = adb_DNSServiceIndexByName(regType_.c_str());
if (index == kADBSecureConnectServiceRefIndex) {
ConnectSecureWifiDevice();
} else {
- connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+ connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(),
+ regType_.c_str()),
&response);
D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
ip_addr_, port_, response.c_str());
@@ -263,20 +296,7 @@
serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
}
- int adbSecureServiceType = serviceIndex();
- switch (adbSecureServiceType) {
- case kADBTransportServiceRefIndex:
- sAdbTransportServices->push_back(this);
- break;
- case kADBSecurePairingServiceRefIndex:
- sAdbSecurePairingServices->push_back(this);
- break;
- case kADBSecureConnectServiceRefIndex:
- sAdbSecureConnectServices->push_back(this);
- break;
- default:
- break;
- }
+ return true;
}
int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
@@ -291,7 +311,7 @@
uint16_t port() const { return port_; }
- using ServiceRegistry = std::vector<ResolvedService*>;
+ using ServiceRegistry = std::vector<std::unique_ptr<ResolvedService>>;
// unencrypted tcp connections
static ServiceRegistry* sAdbTransportServices;
@@ -301,7 +321,7 @@
static void initAdbServiceRegistries();
- static void forEachService(const ServiceRegistry& services, const std::string& hostname,
+ static void forEachService(const ServiceRegistry& services, std::string_view hostname,
adb_secure_foreach_service_callback cb);
static bool connectByServiceName(const ServiceRegistry& services,
@@ -321,13 +341,13 @@
};
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL;
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL;
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL;
// static
void ResolvedService::initAdbServiceRegistries() {
@@ -344,17 +364,17 @@
// static
void ResolvedService::forEachService(const ServiceRegistry& services,
- const std::string& wanted_service_name,
+ std::string_view wanted_service_name,
adb_secure_foreach_service_callback cb) {
initAdbServiceRegistries();
- for (auto service : services) {
+ for (const auto& service : services) {
auto service_name = service->serviceName();
auto reg_type = service->regType();
auto ip = service->ipAddress();
auto port = service->port();
- if (wanted_service_name == "") {
+ if (wanted_service_name.empty()) {
cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
} else if (service_name == wanted_service_name) {
cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
@@ -366,7 +386,7 @@
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
const std::string& service_name) {
initAdbServiceRegistries();
- for (auto service : services) {
+ for (const auto& service : services) {
if (service_name == service->serviceName()) {
D("Got service_name match [%s]", service->serviceName().c_str());
return service->ConnectSecureWifiDevice();
@@ -378,14 +398,12 @@
void adb_secure_foreach_pairing_service(const char* service_name,
adb_secure_foreach_service_callback cb) {
- ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
- service_name ? service_name : "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
}
void adb_secure_foreach_connect_service(const char* service_name,
adb_secure_foreach_service_callback cb) {
- ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
- service_name ? service_name : "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, service_name, cb);
}
bool adb_secure_connect_by_service_name(const char* service_name) {
@@ -393,23 +411,28 @@
service_name);
}
-static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
- DNSServiceFlags /*flags*/,
+static void DNSSD_API register_service_ip(DNSServiceRef sdRef, DNSServiceFlags flags,
uint32_t /*interfaceIndex*/,
- DNSServiceErrorType /*errorCode*/,
- const char* /*hostname*/,
- const sockaddr* address,
- uint32_t /*ttl*/,
- void* context) {
- D("Got IP for service.");
+ DNSServiceErrorType errorCode, const char* hostname,
+ const sockaddr* address, uint32_t ttl, void* context) {
+ D("%s: sdRef=%p flags=0x%08x errorCode=%u ttl=%u", __func__, sdRef, flags, errorCode, ttl);
std::unique_ptr<ResolvedService> data(
reinterpret_cast<ResolvedService*>(context));
- data->Connect(address);
+ // Only resolve the address once. If the address or port changes, we'll just get another
+ // registration.
+ data->DestroyServiceRef();
- // For ADB Secure services, keep those ResolvedService's around
- // for later processing with secure connection establishment.
- if (data->serviceIndex() != kADBTransportServiceRefIndex) {
- data.release();
+ if (errorCode != kDNSServiceErr_NoError) {
+ D("Got error while looking up ipaddr [%u]", errorCode);
+ return;
+ }
+
+ if (flags & kDNSServiceFlagsAdd) {
+ D("Resolved IP address for [%s]. Adding to service registry.", hostname);
+ auto* ptr = data.release();
+ if (!ptr->AddToServiceRegistry(address)) {
+ data.reset(ptr);
+ }
}
}
@@ -459,6 +482,7 @@
};
static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
+ D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
int index = adb_DNSServiceIndexByName(regType);
ResolvedService::ServiceRegistry* services;
switch (index) {
@@ -475,10 +499,15 @@
return;
}
+ if (services->empty()) {
+ return;
+ }
+
std::string sName(serviceName);
- services->erase(std::remove_if(
- services->begin(), services->end(),
- [&sName](ResolvedService* service) { return (sName == service->serviceName()); }));
+ services->erase(std::remove_if(services->begin(), services->end(),
+ [&sName](std::unique_ptr<ResolvedService>& service) {
+ return (sName == service->serviceName());
+ }));
}
// Returns the version the device wanted to advertise,
@@ -647,3 +676,79 @@
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
return result;
}
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name) {
+ CHECK(!name.empty());
+
+ auto mdns_instance = mdns::mdns_parse_instance_name(name);
+ if (!mdns_instance.has_value()) {
+ D("Failed to parse mDNS name [%s]", name.data());
+ return std::nullopt;
+ }
+
+ std::optional<MdnsInfo> info;
+ auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+ uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+ std::string reg_type;
+ if (!mdns_instance->service_name.empty()) {
+ reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
+ mdns_instance->transport_type.data());
+ int index = adb_DNSServiceIndexByName(reg_type);
+ switch (index) {
+ case kADBTransportServiceRefIndex:
+ ResolvedService::forEachService(*ResolvedService::sAdbTransportServices,
+ mdns_instance->instance_name, cb);
+ break;
+ case kADBSecureConnectServiceRefIndex:
+ ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
+ mdns_instance->instance_name, cb);
+ break;
+ default:
+ D("Unknown reg_type [%s]", reg_type.data());
+ return std::nullopt;
+ }
+ return info;
+ }
+
+ for (const auto& service :
+ {ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) {
+ ResolvedService::forEachService(*service, name, cb);
+ if (info.has_value()) {
+ return info;
+ }
+ }
+
+ return std::nullopt;
+}
+
+std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name) {
+ CHECK(!name.empty());
+
+ auto mdns_instance = mdns::mdns_parse_instance_name(name);
+ if (!mdns_instance.has_value()) {
+ D("Failed to parse mDNS pairing name [%s]", name.data());
+ return std::nullopt;
+ }
+
+ std::optional<MdnsInfo> info;
+ auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+ uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+ // Verify it's a pairing service if user explicitly inputs it.
+ if (!mdns_instance->service_name.empty()) {
+ auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
+ mdns_instance->transport_type.data());
+ int index = adb_DNSServiceIndexByName(reg_type);
+ switch (index) {
+ case kADBSecurePairingServiceRefIndex:
+ break;
+ default:
+ D("Not an adb pairing reg_type [%s]", reg_type.data());
+ return std::nullopt;
+ }
+ }
+
+ ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, name, cb);
+ return info;
+}
diff --git a/adb/coverage/gen_coverage.sh b/adb/coverage/gen_coverage.sh
index cced62a..43d45f0 100755
--- a/adb/coverage/gen_coverage.sh
+++ b/adb/coverage/gen_coverage.sh
@@ -17,10 +17,10 @@
# Check that we can connect to it.
adb disconnect
-adb tcpip $REMOTE_PORT
-# TODO: Add `adb transport-id` and wait-for-offline on it.
-sleep 5
+TRANSPORT_ID=$(adb transport-id)
+adb tcpip $REMOTE_PORT
+adb -t $TRANSPORT_ID wait-for-disconnect
adb connect $REMOTE
@@ -32,13 +32,16 @@
fi
# Back to USB, and make sure adbd is root.
+adb -s $REMOTE usb
adb disconnect $REMOTE
+adb wait-for-device root
adb root
-adb wait-for-device usb
+adb wait-for-device
-# TODO: Add `adb transport-id` and wait-for-offline on it.
-sleep 5
+TRANSPORT_ID=$(adb transport-id)
+adb usb
+adb -t $TRANSPORT_ID wait-for-disconnect
adb wait-for-device
@@ -61,10 +64,9 @@
adb shell setprop persist.adb.trace_mask 1
### Run test_device.py over USB.
+TRANSPORT_ID=$(adb transport-id)
adb shell killall adbd
-
-# TODO: Add `adb transport-id` and wait-for-offline on it.
-sleep 5
+adb -t $TRANSPORT_ID wait-for-disconnect
adb wait-for-device shell rm -rf "/data/misc/trace/*" /data/local/tmp/adb_coverage/
"$OUTPUT_DIR"/../test_device.py
@@ -80,13 +82,16 @@
sleep 5
# Restart adbd in tcp mode.
+TRANSPORT_ID=$(adb transport-id)
adb tcpip $REMOTE_PORT
-sleep 5
+adb -t $TRANSPORT_ID wait-for-disconnect
+
adb connect $REMOTE
adb -s $REMOTE wait-for-device
-# Run test_device.py again.
-ANDROID_SERIAL=$REMOTE "$OUTPUT_DIR"/../test_device.py
+# Instead of running test_device.py again, which takes forever, do some I/O back and forth instead.
+dd if=/dev/zero bs=1024 count=10240 | adb -s $REMOTE raw sink:10485760
+adb -s $REMOTE raw source:10485760 | dd of=/dev/null bs=1024 count=10240
# Dump traces again.
adb disconnect $REMOTE
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 55b7783..db8f07b 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -173,12 +173,6 @@
LOG(FATAL) << "Could not set SELinux context";
}
}
- std::string error;
- std::string local_name =
- android::base::StringPrintf("tcp:%d", server_port);
- if (install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)) {
- LOG(FATAL) << "Could not install *smartsocket* listener: " << error;
- }
}
}
#endif
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index fbfae1e..dbca4ad 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -646,15 +646,21 @@
}
// After handling all of the events we've received, check to see if any fds have died.
- if (stdinout_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ auto poll_finished = [](int events) {
+ // Don't return failure until we've read out all of the fd's incoming data.
+ return (events & POLLIN) == 0 &&
+ (events & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) != 0;
+ };
+
+ if (poll_finished(stdinout_pfd.revents)) {
return &stdinout_sfd_;
}
- if (stderr_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ if (poll_finished(stderr_pfd.revents)) {
return &stderr_sfd_;
}
- if (protocol_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ if (poll_finished(protocol_pfd.revents)) {
return &protocol_sfd_;
}
} // while (!dead_sfd)
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 7fff05a..a663871 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -231,7 +231,14 @@
offset += write_size;
}
}
- SubmitWrites();
+
+ // Wake up the worker thread to submit writes.
+ uint64_t notify = 1;
+ ssize_t rc = adb_write(worker_event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify worker eventfd to submit writes";
+ }
+
return true;
}
@@ -443,6 +450,9 @@
}
ReadEvents();
+
+ std::lock_guard<std::mutex> lock(write_mutex_);
+ SubmitWrites();
}
});
}
@@ -626,8 +636,6 @@
write_requests_.erase(it);
size_t outstanding_writes = --writes_submitted_;
LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
-
- SubmitWrites();
}
IoWriteBlock CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset, size_t len,
diff --git a/adb/fastdeploy/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
index d84c5a5..ed5056e 100644
--- a/adb/fastdeploy/proto/ApkEntry.proto
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -5,7 +5,6 @@
option java_package = "com.android.fastdeploy";
option java_outer_classname = "ApkEntryProto";
option java_multiple_files = true;
-option optimize_for = LITE_RUNTIME;
message APKDump {
string name = 1;
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 9fc3b2c..bb3af74 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -79,8 +79,8 @@
unique_fd Destroy(fdevent* fde);
protected:
- virtual void Register(fdevent*) {}
- virtual void Unregister(fdevent*) {}
+ virtual void Register(fdevent*) = 0;
+ virtual void Unregister(fdevent*) = 0;
public:
// Change which events should cause notifications.
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index ac86c08..21c1ba0 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -211,3 +211,7 @@
PLOG(FATAL) << "failed to write to fdevent interrupt fd";
}
}
+
+void fdevent_context_poll::Register(fdevent*) {}
+
+void fdevent_context_poll::Unregister(fdevent*) {}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
index 98abab2..8803e3e 100644
--- a/adb/fdevent/fdevent_poll.h
+++ b/adb/fdevent/fdevent_poll.h
@@ -48,6 +48,9 @@
fdevent_context_poll();
virtual ~fdevent_context_poll();
+ virtual void Register(fdevent* fde) final;
+ virtual void Unregister(fdevent* fde) final;
+
virtual void Set(fdevent* fde, unsigned events) final;
virtual void Loop() final;
diff --git a/adb/libs/adbconnection/include/adbconnection/process_info.h b/adb/libs/adbconnection/include/adbconnection/process_info.h
index 86d3259..d226699 100644
--- a/adb/libs/adbconnection/include/adbconnection/process_info.h
+++ b/adb/libs/adbconnection/include/adbconnection/process_info.h
@@ -21,7 +21,7 @@
#include <string>
struct ProcessInfo {
- const static size_t kMaxArchNameLength = 16;
+ static constexpr size_t kMaxArchNameLength = 16;
uint64_t pid;
bool debuggable;
diff --git a/adb/socket.h b/adb/socket.h
index 4276851..0623204 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -108,7 +108,10 @@
asocket *create_remote_socket(unsigned id, atransport *t);
void connect_to_remote(asocket* s, std::string_view destination);
+
+#if ADB_HOST
void connect_to_smartsocket(asocket *s);
+#endif
// Internal functions that are only made available here for testing purposes.
namespace internal {
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index d17036c..5cad70d 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -30,6 +30,7 @@
#include "adb.h"
#include "adb_utils.h"
+#include "adb_wifi.h"
#include "sysdeps.h"
using namespace std::string_literals;
@@ -103,12 +104,6 @@
if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
return false;
}
-
- if (port_value == -1) {
- *error = "missing port in specification: ";
- *error += spec;
- return false;
- }
}
if (hostname) {
@@ -201,7 +196,24 @@
fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
} else {
#if ADB_HOST
- fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+ // Check if the address is an mdns service we can connect to.
+ if (auto mdns_info = mdns_get_connect_service_info(address.substr(4));
+ mdns_info != std::nullopt) {
+ fd->reset(network_connect(mdns_info->addr, mdns_info->port, SOCK_STREAM, 0, error));
+ if (fd->get() != -1) {
+ // TODO(joshuaduong): We still show the ip address for the serial. Change it to
+ // use the mdns instance name, so we can adjust to address changes on
+ // reconnects.
+ port_value = mdns_info->port;
+ if (serial) {
+ *serial = android::base::StringPrintf("%s.%s",
+ mdns_info->service_name.c_str(),
+ mdns_info->service_type.c_str());
+ }
+ }
+ } else {
+ fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+ }
#else
// Disallow arbitrary connections in adbd.
*error = "adbd does not support arbitrary tcp connections";
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index e9d5270..e83c34c 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -24,6 +24,13 @@
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
+TEST(socket_spec, parse_tcp_socket_spec_failure) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_FALSE(parse_tcp_socket_spec("sneakernet:5037", &hostname, &port, &serial, &error));
+ EXPECT_TRUE(error.find("sneakernet") != std::string::npos);
+}
+
TEST(socket_spec, parse_tcp_socket_spec_just_port) {
std::string hostname, error, serial;
int port;
@@ -134,6 +141,19 @@
EXPECT_NE(client_fd.get(), -1);
}
+TEST(socket_spec, socket_spec_connect_failure) {
+ std::string error, serial;
+ int port;
+ unique_fd client_fd;
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "tcp:", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "acceptfd:", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:x", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5:x", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "sneakernet:", &port, &serial, &error));
+}
+
TEST(socket_spec, socket_spec_listen_connect_localfilesystem) {
std::string error, serial;
int port;
@@ -152,3 +172,16 @@
EXPECT_NE(client_fd.get(), -1);
}
}
+
+TEST(socket_spec, is_socket_spec) {
+ EXPECT_TRUE(is_socket_spec("tcp:blah"));
+ EXPECT_TRUE(is_socket_spec("acceptfd:blah"));
+ EXPECT_TRUE(is_socket_spec("local:blah"));
+ EXPECT_TRUE(is_socket_spec("localreserved:blah"));
+}
+
+TEST(socket_spec, is_local_socket_spec) {
+ EXPECT_TRUE(is_local_socket_spec("local:blah"));
+ EXPECT_TRUE(is_local_socket_spec("tcp:localhost"));
+ EXPECT_FALSE(is_local_socket_spec("tcp:www.google.com"));
+}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 423af67..13a4737 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -520,6 +520,7 @@
send_packet(p, s->transport);
}
+#if ADB_HOST
/* this is used by magic sockets to rig local sockets to
send the go-ahead message when they connect */
static void local_socket_ready_notify(asocket* s) {
@@ -584,8 +585,6 @@
return n;
}
-#if ADB_HOST
-
namespace internal {
// Parses a host service string of the following format:
@@ -714,15 +713,11 @@
} // namespace internal
-#endif // ADB_HOST
-
static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
-#if ADB_HOST
std::string_view service;
std::string_view serial;
TransportId transport_id = 0;
TransportType type = kTransportAny;
-#endif
D("SS(%d): enqueue %zu", s->id, data.size());
@@ -755,7 +750,6 @@
D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
-#if ADB_HOST
service = std::string_view(s->smart_socket_data).substr(4);
// TODO: These should be handled in handle_host_request.
@@ -841,16 +835,6 @@
s2->ready(s2);
return 0;
}
-#else /* !ADB_HOST */
- if (s->transport == nullptr) {
- std::string error_msg = "unknown failure";
- s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
- if (s->transport == nullptr) {
- SendFail(s->peer->fd, error_msg);
- goto fail;
- }
- }
-#endif
if (!s->transport) {
SendFail(s->peer->fd, "device offline (no transport)");
@@ -922,6 +906,7 @@
ss->peer = s;
s->ready(s);
}
+#endif
size_t asocket::get_max_payload() const {
size_t max_payload = MAX_PAYLOAD;
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 03bdcbd..4b99411 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -25,6 +25,7 @@
import random
import select
import socket
+import string
import struct
import subprocess
import sys
@@ -628,21 +629,49 @@
class MdnsTest(unittest.TestCase):
"""Tests for adb mdns."""
+ @staticmethod
+ def _mdns_services(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+ @staticmethod
+ def _devices(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+ @contextlib.contextmanager
+ def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect):
+ """Context manager for an ADB connection.
+
+ This automatically disconnects when done with the connection.
+ """
+
+ output = subprocess.check_output(["adb", "-P", str(server_port), "connect", mdns_instance])
+ if should_connect:
+ self.assertEqual(output.strip(), "connected to {}".format(serial).encode("utf8"))
+ else:
+ self.assertTrue(output.startswith("failed to resolve host: '{}'"
+ .format(mdns_instance).encode("utf8")))
+
+ try:
+ yield
+ finally:
+ # Perform best-effort disconnection. Discard the output.
+ subprocess.Popen(["adb", "disconnect", serial],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+
+
@unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
def test_mdns_services_register_unregister(self):
"""Ensure that `adb mdns services` correctly adds and removes a service
"""
from zeroconf import IPVersion, ServiceInfo
-
- def _mdns_services(port):
- output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
- return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
with adb_server() as server_port:
output = subprocess.check_output(["adb", "-P", str(server_port),
"mdns", "services"]).strip()
self.assertTrue(output.startswith(b"List of discovered mdns services"))
- print(f"services={_mdns_services(server_port)}")
"""TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
"""Register/Unregister a service"""
@@ -656,20 +685,52 @@
name=serv_instance + "." + serv_type + "local.",
addresses=[serv_ipaddr],
port=serv_port)
- print(f"Registering {serv_instance}.{serv_type} ...")
with zeroconf_register_service(zc, service_info) as info:
"""Give adb some time to register the service"""
- time.sleep(0.25)
- print(f"services={_mdns_services(server_port)}")
+ time.sleep(1)
self.assertTrue(any((serv_instance in line and serv_type in line)
- for line in _mdns_services(server_port)))
+ for line in MdnsTest._mdns_services(server_port)))
"""Give adb some time to unregister the service"""
- print("Unregistering mdns service...")
- time.sleep(0.25)
- print(f"services={_mdns_services(server_port)}")
+ time.sleep(1)
self.assertFalse(any((serv_instance in line and serv_type in line)
- for line in _mdns_services(server_port)))
+ for line in MdnsTest._mdns_services(server_port)))
+
+ @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+ def test_mdns_connect(self):
+ """Ensure that `adb connect` by mdns instance name works (for non-pairing services)
+ """
+ from zeroconf import IPVersion, ServiceInfo
+
+ with adb_server() as server_port:
+ with zeroconf_context(IPVersion.V4Only) as zc:
+ serv_instance = "fakeadbd-" + ''.join(
+ random.choice(string.ascii_letters) for i in range(4))
+ serv_type = "_" + self.service_name + "._tcp."
+ serv_ipaddr = socket.inet_aton("127.0.0.1")
+ should_connect = self.service_name != "adb-tls-pairing"
+ with fake_adbd() as (port, _):
+ service_info = ServiceInfo(
+ serv_type + "local.",
+ name=serv_instance + "." + serv_type + "local.",
+ addresses=[serv_ipaddr],
+ port=port)
+ with zeroconf_register_service(zc, service_info) as info:
+ """Give adb some time to register the service"""
+ time.sleep(1)
+ self.assertTrue(any((serv_instance in line and serv_type in line)
+ for line in MdnsTest._mdns_services(server_port)))
+ full_name = '.'.join([serv_instance, serv_type])
+ with self._adb_mdns_connect(server_port, serv_instance, full_name,
+ should_connect):
+ if should_connect:
+ self.assertEqual(MdnsTest._devices(server_port),
+ [[full_name, "device"]])
+
+ """Give adb some time to unregister the service"""
+ time.sleep(1)
+ self.assertFalse(any((serv_instance in line and serv_type in line)
+ for line in MdnsTest._mdns_services(server_port)))
def main():
"""Main entrypoint."""
diff --git a/adb/test_device.py b/adb/test_device.py
index f5e4cbb..9f1f403 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -82,10 +82,13 @@
class AbbTest(DeviceTest):
def test_smoke(self):
- result = subprocess.run(['adb', 'abb'], capture_output=True)
- self.assertEqual(1, result.returncode)
- expected_output = b"cmd: No service specified; use -l to list all services\n"
- self.assertEqual(expected_output, result.stderr)
+ abb = subprocess.run(['adb', 'abb'], capture_output=True)
+ cmd = subprocess.run(['adb', 'shell', 'cmd'], capture_output=True)
+
+ # abb squashes all failures to 1.
+ self.assertEqual(abb.returncode == 0, cmd.returncode == 0)
+ self.assertEqual(abb.stdout, cmd.stdout)
+ self.assertEqual(abb.stderr, cmd.stderr)
class ForwardReverseTest(DeviceTest):
def _test_no_rebind(self, description, direction_list, direction,
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 25ed366..1667011 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -928,6 +928,7 @@
remove_transport(t);
}
+#if ADB_HOST
static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
bool sanitize_qual) {
if (to_test.empty()) /* Return true if both the qual and to_test are empty strings. */
@@ -1083,10 +1084,13 @@
}
cv_.notify_one();
}
+#endif
atransport::~atransport() {
+#if ADB_HOST
// If the connection callback had not been run before, run it now.
SetConnectionEstablished(false);
+#endif
}
int atransport::Write(apacket* p) {
@@ -1240,6 +1244,7 @@
disconnects_.clear();
}
+#if ADB_HOST
bool atransport::MatchesTarget(const std::string& target) const {
if (!serial.empty()) {
if (target == serial) {
@@ -1283,8 +1288,6 @@
return reconnect_(this);
}
-#if ADB_HOST
-
// We use newline as our delimiter, make sure to never output it.
static std::string sanitize(std::string str, bool alphanumeric) {
auto pred = alphanumeric ? [](const char c) { return !isalnum(c); }
@@ -1366,7 +1369,7 @@
void close_usb_devices(bool reset) {
close_usb_devices([](const atransport*) { return true; }, reset);
}
-#endif // ADB_HOST
+#endif
bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
atransport::ReconnectCallback reconnect, bool use_tls, int* error) {
@@ -1406,7 +1409,9 @@
lock.unlock();
+#if ADB_HOST
auto waitable = t->connection_waitable();
+#endif
register_transport(t);
if (local == 1) {
@@ -1414,6 +1419,7 @@
return true;
}
+#if ADB_HOST
if (!waitable->WaitForConnection(std::chrono::seconds(10))) {
if (error) *error = ETIMEDOUT;
return false;
@@ -1423,6 +1429,7 @@
if (error) *error = EPERM;
return false;
}
+#endif
return true;
}
@@ -1453,14 +1460,9 @@
t->Kick();
}
}
-#if ADB_HOST
reconnect_handler.CheckForKicked();
-#endif
}
-#endif
-
-#if ADB_HOST
void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
unsigned writeable) {
atransport* t = new atransport(writeable ? kCsOffline : kCsNoPerm);
@@ -1482,9 +1484,7 @@
register_transport(t);
}
-#endif
-#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);
diff --git a/adb/transport.h b/adb/transport.h
index e93c31c..2ac21cf 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -262,9 +262,12 @@
: id(NextTransportId()),
kicked_(false),
connection_state_(state),
- connection_waitable_(std::make_shared<ConnectionWaitable>()),
connection_(nullptr),
reconnect_(std::move(reconnect)) {
+#if ADB_HOST
+ connection_waitable_ = std::make_shared<ConnectionWaitable>();
+#endif
+
// Initialize protocol to min version for compatibility with older versions.
// Version will be updated post-connect.
protocol_version = A_VERSION_MIN;
@@ -350,6 +353,7 @@
void RemoveDisconnect(adisconnect* disconnect);
void RunDisconnects();
+#if ADB_HOST
// Returns true if |target| matches this transport. A matching |target| can be any of:
// * <serial>
// * <devpath>
@@ -374,6 +378,7 @@
// Attempts to reconnect with the underlying Connection.
ReconnectResult Reconnect();
+#endif
private:
std::atomic<bool> kicked_;
@@ -392,9 +397,11 @@
std::deque<std::shared_ptr<RSA>> keys_;
#endif
+#if ADB_HOST
// A sharable object that can be used to wait for the atransport's
// connection to be established.
std::shared_ptr<ConnectionWaitable> connection_waitable_;
+#endif
// The underlying connection object.
std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
@@ -434,10 +441,17 @@
void init_transport_registration(void);
void init_mdns_transport_discovery(void);
std::string list_transports(bool long_listing);
+
+#if ADB_HOST
atransport* find_transport(const char* serial);
+
void kick_all_tcp_devices();
+#endif
+
void kick_all_transports();
+
void kick_all_tcp_tls_transports();
+
#if !ADB_HOST
void kick_all_transports_by_auth_key(std::string_view auth_key);
#endif
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index a9ada4a..8579ff4 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -127,6 +127,7 @@
ASSERT_EQ(std::string("baz"), t.device);
}
+#if ADB_HOST
TEST_F(TransportTest, test_matches_target) {
std::string serial = "foo";
std::string devpath = "/path/to/bar";
@@ -183,3 +184,4 @@
EXPECT_FALSE(t.MatchesTarget("abc:100.100.100.100"));
}
}
+#endif
diff --git a/base b/base
new file mode 120000
index 0000000..622c552
--- /dev/null
+++ b/base
@@ -0,0 +1 @@
+../libbase
\ No newline at end of file
diff --git a/base/.clang-format b/base/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/base/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/base/Android.bp b/base/Android.bp
deleted file mode 100644
index 61fbc3d..0000000
--- a/base/Android.bp
+++ /dev/null
@@ -1,242 +0,0 @@
-//
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_defaults {
- name: "libbase_cflags_defaults",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
- target: {
- android: {
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- ],
- },
- },
-}
-
-cc_library_headers {
- name: "libbase_headers",
- vendor_available: true,
- ramdisk_available: true,
- recovery_available: true,
- host_supported: true,
- native_bridge_supported: true,
- export_include_dirs: ["include"],
-
- target: {
- linux_bionic: {
- enabled: true,
- },
- windows: {
- enabled: true,
- },
- },
- apex_available: [
- "//apex_available:anyapex",
- "//apex_available:platform",
- ],
- min_sdk_version: "29",
-}
-
-cc_defaults {
- name: "libbase_defaults",
- defaults: ["libbase_cflags_defaults"],
- srcs: [
- "abi_compatibility.cpp",
- "chrono_utils.cpp",
- "cmsg.cpp",
- "file.cpp",
- "liblog_symbols.cpp",
- "logging.cpp",
- "mapped_file.cpp",
- "parsebool.cpp",
- "parsenetaddress.cpp",
- "process.cpp",
- "properties.cpp",
- "stringprintf.cpp",
- "strings.cpp",
- "threads.cpp",
- "test_utils.cpp",
- ],
-
- cppflags: ["-Wexit-time-destructors"],
- shared_libs: ["liblog"],
- target: {
- android: {
- sanitize: {
- misc_undefined: ["integer"],
- },
-
- },
- linux: {
- srcs: [
- "errors_unix.cpp",
- ],
- },
- darwin: {
- srcs: [
- "errors_unix.cpp",
- ],
- },
- linux_bionic: {
- enabled: true,
- },
- windows: {
- srcs: [
- "errors_windows.cpp",
- "utf8.cpp",
- ],
- exclude_srcs: [
- "cmsg.cpp",
- ],
- enabled: true,
- },
- },
-}
-
-cc_library {
- name: "libbase",
- defaults: ["libbase_defaults"],
- vendor_available: true,
- ramdisk_available: true,
- recovery_available: true,
- host_supported: true,
- native_bridge_supported: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
- header_libs: [
- "libbase_headers",
- ],
- export_header_lib_headers: ["libbase_headers"],
- static_libs: ["fmtlib"],
- whole_static_libs: ["fmtlib"],
- export_static_lib_headers: ["fmtlib"],
- apex_available: [
- "//apex_available:anyapex",
- "//apex_available:platform",
- ],
- min_sdk_version: "29",
-}
-
-cc_library_static {
- name: "libbase_ndk",
- defaults: ["libbase_defaults"],
- sdk_version: "current",
- stl: "c++_static",
- export_include_dirs: ["include"],
- static_libs: ["fmtlib_ndk"],
- whole_static_libs: ["fmtlib_ndk"],
- export_static_lib_headers: ["fmtlib_ndk"],
-}
-
-// Tests
-// ------------------------------------------------------------------------------
-cc_test {
- name: "libbase_test",
- defaults: ["libbase_cflags_defaults"],
- host_supported: true,
- srcs: [
- "cmsg_test.cpp",
- "endian_test.cpp",
- "errors_test.cpp",
- "expected_test.cpp",
- "file_test.cpp",
- "logging_splitters_test.cpp",
- "logging_test.cpp",
- "macros_test.cpp",
- "mapped_file_test.cpp",
- "no_destructor_test.cpp",
- "parsedouble_test.cpp",
- "parsebool_test.cpp",
- "parseint_test.cpp",
- "parsenetaddress_test.cpp",
- "process_test.cpp",
- "properties_test.cpp",
- "result_test.cpp",
- "scopeguard_test.cpp",
- "stringprintf_test.cpp",
- "strings_test.cpp",
- "test_main.cpp",
- "test_utils_test.cpp",
- ],
- target: {
- android: {
- sanitize: {
- misc_undefined: ["integer"],
- },
- },
- linux: {
- srcs: ["chrono_utils_test.cpp"],
- },
- windows: {
- srcs: ["utf8_test.cpp"],
- cflags: ["-Wno-unused-parameter"],
- enabled: true,
- },
- },
- local_include_dirs: ["."],
- shared_libs: ["libbase"],
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
- test_suites: ["device-tests"],
-}
-
-cc_test {
- name: "libbase_tidy_test",
- defaults: ["libbase_cflags_defaults"],
- host_supported: true,
-
- tidy: true,
- tidy_checks_as_errors: ["bugprone-use-after-move"],
-
- srcs: [
- "tidy/unique_fd_test.cpp",
- "tidy/unique_fd_test2.cpp",
- ],
-
- shared_libs: ["libbase"],
- test_suites: ["device_tests"],
-}
-
-cc_benchmark {
- name: "libbase_benchmark",
- defaults: ["libbase_cflags_defaults"],
-
- srcs: ["format_benchmark.cpp"],
- shared_libs: ["libbase"],
-
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-}
diff --git a/base/CPPLINT.cfg b/base/CPPLINT.cfg
deleted file mode 100644
index d94a89c..0000000
--- a/base/CPPLINT.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-set noparent
-filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators
diff --git a/base/OWNERS b/base/OWNERS
deleted file mode 100644
index 97777f7..0000000
--- a/base/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-enh@google.com
-jmgao@google.com
-tomcherry@google.com
diff --git a/base/README.md b/base/README.md
deleted file mode 100644
index 2ef5c10..0000000
--- a/base/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# libbase
-
-## Who is this library for?
-
-This library is a collection of convenience functions to make common tasks
-easier and less error-prone.
-
-In this context, "error-prone" covers both "hard to do correctly" and
-"hard to do with good performance", but as a general purpose library,
-libbase's primary focus is on making it easier to do things easily and
-correctly when a compromise has to be made between "simplest API" on the
-one hand and "fastest implementation" on the other. Though obviously
-the ideal is to have both.
-
-## Should my routine be added?
-
-The intention is to cover the 80% use cases, not be all things to all users.
-
-If you have a routine that's really useful in your project,
-congratulations. But that doesn't mean it should be here rather than
-just in your project.
-
-The question for libbase is "should everyone be doing this?"/"does this
-make everyone's code cleaner/safer?". Historically we've considered the
-bar for inclusion to be "are there at least three *unrelated* projects
-that would be cleaned up by doing so".
-
-If your routine is actually something from a future C++ standard (that
-isn't yet in libc++), or it's widely used in another library, that helps
-show that there's precedent. Being able to say "so-and-so has used this
-API for n years" is a good way to reduce concerns about API choices.
-
-## Any other restrictions?
-
-Unlike most Android code, code in libbase has to build for Mac and
-Windows too.
-
-Code here is also expected to have good test coverage.
-
-By its nature, it's difficult to change libbase API. It's often best
-to start using your routine just in your project, and let it "graduate"
-after you're certain that the API is solid.
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
deleted file mode 100644
index 06a7801..0000000
--- a/base/abi_compatibility.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <memory>
-
-#include "android-base/cmsg.h"
-#include "android-base/file.h"
-#include "android-base/mapped_file.h"
-#include "android-base/unique_fd.h"
-
-namespace android {
-namespace base {
-
-// These ABI-compatibility shims are in a separate file for two reasons:
-// 1. If they were in the file with the actual functions, it prevents calls to
-// those functions by other functions in the file, due to ambiguity.
-// 2. We will hopefully be able to delete these quickly.
-
-#if !defined(_WIN32)
-ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
- const std::vector<int>& fds) {
- return SendFileDescriptorVector(borrowed_fd(sockfd), data, len, fds);
-}
-
-ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
- std::vector<unique_fd>* fds) {
- return ReceiveFileDescriptorVector(borrowed_fd(sockfd), data, len, max_fds, fds);
-}
-#endif
-
-bool ReadFdToString(int fd, std::string* content) {
- return ReadFdToString(borrowed_fd(fd), content);
-}
-
-bool WriteStringToFd(const std::string& content, int fd) {
- return WriteStringToFd(content, borrowed_fd(fd));
-}
-
-bool ReadFully(int fd, void* data, size_t byte_count) {
- return ReadFully(borrowed_fd(fd), data, byte_count);
-}
-
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
- return ReadFullyAtOffset(borrowed_fd(fd), data, byte_count, offset);
-}
-
-bool WriteFully(int fd, const void* data, size_t byte_count) {
- return WriteFully(borrowed_fd(fd), data, byte_count);
-}
-
-#if defined(__LP64__)
-#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
-#else
-#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEixmi
-#endif
-
-#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
-extern "C" std::unique_ptr<MappedFile> MAPPEDFILE_FROMFD(int fd, off64_t offset, size_t length,
- int prot) {
- return MappedFile::FromFd(fd, offset, length, prot);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
deleted file mode 100644
index 19080a5..0000000
--- a/base/chrono_utils.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/chrono_utils.h"
-
-#include <time.h>
-
-namespace android {
-namespace base {
-
-boot_clock::time_point boot_clock::now() {
-#ifdef __linux__
- timespec ts;
- clock_gettime(CLOCK_BOOTTIME, &ts);
- return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
- std::chrono::nanoseconds(ts.tv_nsec));
-#else
- // Darwin and Windows do not support clock_gettime.
- return boot_clock::time_point();
-#endif // __linux__
-}
-
-std::ostream& operator<<(std::ostream& os, const Timer& t) {
- os << t.duration().count() << "ms";
- return os;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
deleted file mode 100644
index da442f4..0000000
--- a/base/chrono_utils_test.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/chrono_utils.h"
-
-#include <time.h>
-
-#include <chrono>
-#include <sstream>
-#include <string>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace base {
-
-std::chrono::seconds GetBootTimeSeconds() {
- struct timespec now;
- clock_gettime(CLOCK_BOOTTIME, &now);
-
- auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
- std::chrono::nanoseconds(now.tv_nsec));
- return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
-}
-
-// Tests (at least) the seconds accuracy of the boot_clock::now() method.
-TEST(ChronoUtilsTest, BootClockNowSeconds) {
- auto now = GetBootTimeSeconds();
- auto boot_seconds =
- std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
- EXPECT_EQ(now, boot_seconds);
-}
-
-template <typename T>
-void ExpectAboutEqual(T expected, T actual) {
- auto expected_upper_bound = expected * 1.05f;
- auto expected_lower_bound = expected * .95;
- EXPECT_GT(expected_upper_bound, actual);
- EXPECT_LT(expected_lower_bound, actual);
-}
-
-TEST(ChronoUtilsTest, TimerDurationIsSane) {
- auto start = boot_clock::now();
- Timer t;
- std::this_thread::sleep_for(50ms);
- auto stop = boot_clock::now();
- auto stop_timer = t.duration();
-
- auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
- ExpectAboutEqual(expected, stop_timer);
-}
-
-TEST(ChronoUtilsTest, TimerOstream) {
- Timer t;
- std::this_thread::sleep_for(50ms);
- auto stop_timer = t.duration().count();
- std::stringstream os;
- os << t;
- decltype(stop_timer) stop_timer_from_stream;
- os >> stop_timer_from_stream;
- EXPECT_NE(0, stop_timer);
- ExpectAboutEqual(stop_timer, stop_timer_from_stream);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
deleted file mode 100644
index 1fa873c..0000000
--- a/base/cmsg.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/cmsg.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/user.h>
-
-#include <memory>
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace base {
-
-ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
- const std::vector<int>& fds) {
- size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
- size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
- if (cmsg_space >= PAGE_SIZE) {
- errno = ENOMEM;
- return -1;
- }
-
- alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
- iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
- msghdr msg = {
- .msg_name = nullptr,
- .msg_namelen = 0,
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cmsg_buf,
- // We can't cast to the actual type of the field, because it's different across platforms.
- .msg_controllen = static_cast<unsigned int>(cmsg_space),
- .msg_flags = 0,
- };
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = cmsg_len;
-
- int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
- for (size_t i = 0; i < fds.size(); ++i) {
- cmsg_fds[i] = fds[i];
- }
-
-#if defined(__linux__)
- int flags = MSG_NOSIGNAL;
-#else
- int flags = 0;
-#endif
-
- return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
-}
-
-ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
- std::vector<unique_fd>* fds) {
- fds->clear();
-
- size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
- if (cmsg_space >= PAGE_SIZE) {
- errno = ENOMEM;
- return -1;
- }
-
- alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
- iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
- msghdr msg = {
- .msg_name = nullptr,
- .msg_namelen = 0,
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cmsg_buf,
- // We can't cast to the actual type of the field, because it's different across platforms.
- .msg_controllen = static_cast<unsigned int>(cmsg_space),
- .msg_flags = 0,
- };
-
- int flags = MSG_TRUNC | MSG_CTRUNC;
-#if defined(__linux__)
- flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
-#endif
-
- ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
-
- if (rc == -1) {
- return -1;
- }
-
- int error = 0;
- if ((msg.msg_flags & MSG_TRUNC)) {
- LOG(ERROR) << "message was truncated when receiving file descriptors";
- error = EMSGSIZE;
- } else if ((msg.msg_flags & MSG_CTRUNC)) {
- LOG(ERROR) << "control message was truncated when receiving file descriptors";
- error = EMSGSIZE;
- }
-
- std::vector<unique_fd> received_fds;
- struct cmsghdr* cmsg;
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
- << "]";
- error = EBADMSG;
- continue;
- }
-
- // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
- // some asserts to ensure that CMSG_LEN behaves as we expect.
-#if defined(__linux__)
-#define CMSG_ASSERT static_assert
-#else
-// CMSG_LEN is somehow not constexpr on darwin.
-#define CMSG_ASSERT CHECK
-#endif
- CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
- CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
- CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
- CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
-
- if (cmsg->cmsg_len % sizeof(int) != 0) {
- LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
- } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
- LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
- }
-
- int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
- size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
- for (size_t i = 0; i < cmsg_fdcount; ++i) {
-#if !defined(__linux__)
- // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
- fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
-#endif
- received_fds.emplace_back(cmsg_fds[i]);
- }
- }
-
- if (error != 0) {
- errno = error;
- return -1;
- }
-
- if (received_fds.size() > max_fds) {
- LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
- << received_fds.size();
- errno = EMSGSIZE;
- return -1;
- }
-
- *fds = std::move(received_fds);
- return rc;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
deleted file mode 100644
index 9ee5c82..0000000
--- a/base/cmsg_test.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/cmsg.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-
-#if !defined(_WIN32)
-
-using android::base::ReceiveFileDescriptors;
-using android::base::SendFileDescriptors;
-using android::base::unique_fd;
-
-static ino_t GetInode(int fd) {
- struct stat st;
- if (fstat(fd, &st) != 0) {
- PLOG(FATAL) << "fstat failed";
- }
-
- return st.st_ino;
-}
-
-struct CmsgTest : ::testing::TestWithParam<bool> {
- bool Seqpacket() { return GetParam(); }
-
- void SetUp() override {
- ASSERT_TRUE(
- android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
- int dup1 = dup(tmp1.fd);
- ASSERT_NE(-1, dup1);
- int dup2 = dup(tmp2.fd);
- ASSERT_NE(-1, dup2);
-
- fd1.reset(dup1);
- fd2.reset(dup2);
-
- ino1 = GetInode(dup1);
- ino2 = GetInode(dup2);
- }
-
- unique_fd send;
- unique_fd recv;
-
- TemporaryFile tmp1;
- TemporaryFile tmp2;
-
- unique_fd fd1;
- unique_fd fd2;
-
- ino_t ino1;
- ino_t ino2;
-};
-
-TEST_P(CmsgTest, smoke) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
-
- char buf[2];
- unique_fd received;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
- ASSERT_EQ('x', buf[0]);
- ASSERT_NE(-1, received.get());
-
- ASSERT_EQ(ino1, GetInode(received.get()));
-}
-
-TEST_P(CmsgTest, msg_trunc) {
- ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
-
- char buf[2];
- unique_fd received1, received2;
-
- ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
- if (Seqpacket()) {
- ASSERT_EQ(-1, rc);
- ASSERT_EQ(EMSGSIZE, errno);
- ASSERT_EQ(-1, received1.get());
- ASSERT_EQ(-1, received2.get());
- } else {
- ASSERT_EQ(1, rc);
- ASSERT_NE(-1, received1.get());
- ASSERT_NE(-1, received2.get());
- ASSERT_EQ(ino1, GetInode(received1.get()));
- ASSERT_EQ(ino2, GetInode(received2.get()));
- ASSERT_EQ(1, read(recv.get(), buf, 2));
- }
-}
-
-TEST_P(CmsgTest, msg_ctrunc) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
-
- char buf[2];
- unique_fd received;
- ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
- ASSERT_EQ(EMSGSIZE, errno);
- ASSERT_EQ(-1, received.get());
-}
-
-TEST_P(CmsgTest, peek) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
-
- char buf[2];
- ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
- ASSERT_EQ('a', buf[0]);
-
- unique_fd received;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
- ASSERT_EQ(ino1, GetInode(received.get()));
-}
-
-TEST_P(CmsgTest, stream_fd_association) {
- if (Seqpacket()) {
- return;
- }
-
- // fds are associated with the first byte of the write.
- ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
- ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
- char buf[2];
- ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
- ASSERT_EQ(0, memcmp(buf, "ab", 2));
-
- std::vector<unique_fd> received1;
- ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
- ASSERT_EQ(1, rc);
- ASSERT_EQ('c', buf[0]);
- ASSERT_TRUE(received1.empty());
-
- unique_fd received2;
- rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
- ASSERT_EQ(1, rc);
- ASSERT_EQ('d', buf[0]);
- ASSERT_EQ(ino2, GetInode(received2.get()));
-}
-
-TEST_P(CmsgTest, multiple_fd_ordering) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
-
- char buf[2];
- unique_fd received1, received2;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
-
- ASSERT_NE(-1, received1.get());
- ASSERT_NE(-1, received2.get());
-
- ASSERT_EQ(ino1, GetInode(received1.get()));
- ASSERT_EQ(ino2, GetInode(received2.get()));
-}
-
-TEST_P(CmsgTest, separate_fd_ordering) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
-
- char buf[2];
- unique_fd received1, received2;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
-
- ASSERT_NE(-1, received1.get());
- ASSERT_NE(-1, received2.get());
-
- ASSERT_EQ(ino1, GetInode(received1.get()));
- ASSERT_EQ(ino2, GetInode(received2.get()));
-}
-
-TEST_P(CmsgTest, separate_fds_no_coalescing) {
- unique_fd sent1(dup(tmp1.fd));
- unique_fd sent2(dup(tmp2.fd));
-
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
-
- char buf[2];
- std::vector<unique_fd> received;
- ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
- ASSERT_EQ(1U, received.size());
- ASSERT_EQ(ino1, GetInode(received[0].get()));
-
- ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
- ASSERT_EQ(1U, received.size());
- ASSERT_EQ(ino2, GetInode(received[0].get()));
-}
-
-INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
-
-#endif
diff --git a/base/endian_test.cpp b/base/endian_test.cpp
deleted file mode 100644
index 963ab13..0000000
--- a/base/endian_test.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/endian.h"
-
-#include <gtest/gtest.h>
-
-TEST(endian, constants) {
- ASSERT_TRUE(__LITTLE_ENDIAN == LITTLE_ENDIAN);
- ASSERT_TRUE(__BIG_ENDIAN == BIG_ENDIAN);
- ASSERT_TRUE(__BYTE_ORDER == BYTE_ORDER);
-
- ASSERT_EQ(__LITTLE_ENDIAN, __BYTE_ORDER);
-}
-
-TEST(endian, smoke) {
- static constexpr uint16_t le16 = 0x1234;
- static constexpr uint32_t le32 = 0x12345678;
- static constexpr uint64_t le64 = 0x123456789abcdef0;
-
- static constexpr uint16_t be16 = 0x3412;
- static constexpr uint32_t be32 = 0x78563412;
- static constexpr uint64_t be64 = 0xf0debc9a78563412;
-
- ASSERT_EQ(be16, htons(le16));
- ASSERT_EQ(be32, htonl(le32));
- ASSERT_EQ(be64, htonq(le64));
-
- ASSERT_EQ(le16, ntohs(be16));
- ASSERT_EQ(le32, ntohl(be32));
- ASSERT_EQ(le64, ntohq(be64));
-
- ASSERT_EQ(be16, htobe16(le16));
- ASSERT_EQ(be32, htobe32(le32));
- ASSERT_EQ(be64, htobe64(le64));
-
- ASSERT_EQ(le16, betoh16(be16));
- ASSERT_EQ(le32, betoh32(be32));
- ASSERT_EQ(le64, betoh64(be64));
-
- ASSERT_EQ(le16, htole16(le16));
- ASSERT_EQ(le32, htole32(le32));
- ASSERT_EQ(le64, htole64(le64));
-
- ASSERT_EQ(le16, letoh16(le16));
- ASSERT_EQ(le32, letoh32(le32));
- ASSERT_EQ(le64, letoh64(le64));
-
- ASSERT_EQ(le16, be16toh(be16));
- ASSERT_EQ(le32, be32toh(be32));
- ASSERT_EQ(le64, be64toh(be64));
-
- ASSERT_EQ(le16, le16toh(le16));
- ASSERT_EQ(le32, le32toh(le32));
- ASSERT_EQ(le64, le64toh(le64));
-}
diff --git a/base/errors_test.cpp b/base/errors_test.cpp
deleted file mode 100644
index 8e7cdd1..0000000
--- a/base/errors_test.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 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/errors.h"
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace base {
-
-// Error strings aren't consistent enough across systems to test the output,
-// just make sure we can compile correctly and nothing crashes even if we send
-// it possibly bogus error codes.
-TEST(ErrorsTest, TestSystemErrorString) {
- SystemErrorCodeToString(-1);
- SystemErrorCodeToString(0);
- SystemErrorCodeToString(1);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/errors_unix.cpp b/base/errors_unix.cpp
deleted file mode 100644
index 48269b6..0000000
--- a/base/errors_unix.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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/errors.h"
-
-#include <errno.h>
-#include <string.h>
-
-namespace android {
-namespace base {
-
-std::string SystemErrorCodeToString(int error_code) {
- return strerror(error_code);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/errors_windows.cpp b/base/errors_windows.cpp
deleted file mode 100644
index a5ff511..0000000
--- a/base/errors_windows.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 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/errors.h"
-
-#include <windows.h>
-
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
-#include "android-base/utf8.h"
-
-// A Windows error code is a DWORD. It's simpler to use an int error code for
-// both Unix and Windows if possible, but if this fails we'll need a different
-// function signature for each.
-static_assert(sizeof(int) >= sizeof(DWORD),
- "Windows system error codes are too large to fit in an int.");
-
-namespace android {
-namespace base {
-
-static constexpr DWORD kErrorMessageBufferSize = 256;
-
-std::string SystemErrorCodeToString(int int_error_code) {
- WCHAR msgbuf[kErrorMessageBufferSize];
- DWORD error_code = int_error_code;
- DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
- DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
- kErrorMessageBufferSize, nullptr);
- if (len == 0) {
- return android::base::StringPrintf(
- "Error %lu while retrieving message for error %lu", GetLastError(),
- error_code);
- }
-
- // Convert UTF-16 to UTF-8.
- std::string msg;
- if (!android::base::WideToUTF8(msgbuf, &msg)) {
- return android::base::StringPrintf(
- "Error %lu while converting message for error %lu from UTF-16 to UTF-8",
- GetLastError(), error_code);
- }
-
- // Messages returned by the system end with line breaks.
- msg = android::base::Trim(msg);
-
- // There are many Windows error messages compared to POSIX, so include the
- // numeric error code for easier, quicker, accurate identification. Use
- // decimal instead of hex because there are decimal ranges like 10000-11999
- // for Winsock.
- android::base::StringAppendF(&msg, " (%lu)", error_code);
- return msg;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
deleted file mode 100644
index 47e396a..0000000
--- a/base/expected_test.cpp
+++ /dev/null
@@ -1,876 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/expected.h"
-
-#include <cstdio>
-#include <memory>
-#include <string>
-
-#include <gtest/gtest.h>
-
-using android::base::expected;
-using android::base::unexpected;
-
-typedef expected<int, int> exp_int;
-typedef expected<double, double> exp_double;
-typedef expected<std::string, std::string> exp_string;
-typedef expected<std::pair<std::string, int>, int> exp_pair;
-typedef expected<void, int> exp_void;
-
-struct T {
- int a;
- int b;
- T() = default;
- T(int a, int b) noexcept : a(a), b(b) {}
-};
-bool operator==(const T& x, const T& y) {
- return x.a == y.a && x.b == y.b;
-}
-bool operator!=(const T& x, const T& y) {
- return x.a != y.a || x.b != y.b;
-}
-
-struct E {
- std::string message;
- int cause;
- E(const std::string& message, int cause) : message(message), cause(cause) {}
-};
-
-typedef expected<T,E> exp_complex;
-
-TEST(Expected, testDefaultConstructible) {
- exp_int e;
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ(0, e.value());
-
- exp_complex e2;
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(T(0,0), e2.value());
-
- exp_void e3;
- EXPECT_TRUE(e3.has_value());
-}
-
-TEST(Expected, testCopyConstructible) {
- exp_int e;
- exp_int e2 = e;
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(0, e.value());
- EXPECT_EQ(0, e2.value());
-
- exp_void e3;
- exp_void e4 = e3;
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testMoveConstructible) {
- exp_int e;
- exp_int e2 = std::move(e);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(0, e.value());
- EXPECT_EQ(0, e2.value());
-
- exp_string e3(std::string("hello"));
- exp_string e4 = std::move(e3);
-
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
- EXPECT_EQ("", e3.value()); // e3 is moved
- EXPECT_EQ("hello", e4.value());
-
- exp_void e5;
- exp_void e6 = std::move(e5);
- EXPECT_TRUE(e5.has_value());
- EXPECT_TRUE(e6.has_value());
-}
-
-TEST(Expected, testCopyConstructibleFromConvertibleType) {
- exp_double e = 3.3f;
- exp_int e2 = e;
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(3.3f, e.value());
- EXPECT_EQ(3, e2.value());
-}
-
-TEST(Expected, testMoveConstructibleFromConvertibleType) {
- exp_double e = 3.3f;
- exp_int e2 = std::move(e);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(3.3f, e.value());
- EXPECT_EQ(3, e2.value());
-}
-
-TEST(Expected, testConstructibleFromValue) {
- exp_int e = 3;
- exp_double e2 = 5.5f;
- exp_string e3 = std::string("hello");
- exp_complex e4 = T(10, 20);
- exp_void e5 = {};
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
- EXPECT_TRUE(e5.has_value());
- EXPECT_EQ(3, e.value());
- EXPECT_EQ(5.5f, e2.value());
- EXPECT_EQ("hello", e3.value());
- EXPECT_EQ(T(10,20), e4.value());
-}
-
-TEST(Expected, testConstructibleFromMovedValue) {
- std::string hello = "hello";
- exp_string e = std::move(hello);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ("hello", e.value());
- EXPECT_EQ("", hello);
-}
-
-TEST(Expected, testConstructibleFromConvertibleValue) {
- exp_int e = 3.3f; // double to int
- exp_string e2 = "hello"; // char* to std::string
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ(3, e.value());
-
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ("hello", e2.value());
-}
-
-TEST(Expected, testConstructibleFromUnexpected) {
- exp_int::unexpected_type unexp = unexpected(10);
- exp_int e = unexp;
-
- exp_double::unexpected_type unexp2 = unexpected(10.5f);
- exp_double e2 = unexp2;
-
- exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
- exp_string e3 = unexp3;
-
- exp_void::unexpected_type unexp4 = unexpected(10);
- exp_void e4 = unexp4;
-
- EXPECT_FALSE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_FALSE(e3.has_value());
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(10, e.error());
- EXPECT_EQ(10.5f, e2.error());
- EXPECT_EQ("error", e3.error());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testMoveConstructibleFromUnexpected) {
- exp_int e = unexpected(10);
- exp_double e2 = unexpected(10.5f);
- exp_string e3 = unexpected(std::string("error"));
- exp_void e4 = unexpected(10);
-
- EXPECT_FALSE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_FALSE(e3.has_value());
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(10, e.error());
- EXPECT_EQ(10.5f, e2.error());
- EXPECT_EQ("error", e3.error());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testConstructibleByForwarding) {
- exp_string e(std::in_place, 5, 'a');
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ("aaaaa", e.value());
-
- exp_string e2({'a', 'b', 'c'});
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ("abc", e2.value());
-
- exp_pair e3({"hello", 30});
- EXPECT_TRUE(e3.has_value());
- EXPECT_EQ("hello",e3->first);
- EXPECT_EQ(30,e3->second);
-
- exp_void e4({});
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testDestructible) {
- bool destroyed = false;
- struct T {
- bool* flag_;
- T(bool* flag) : flag_(flag) {}
- ~T() { *flag_ = true; }
- };
- {
- expected<T, int> exp = T(&destroyed);
- }
- EXPECT_TRUE(destroyed);
-}
-
-TEST(Expected, testAssignable) {
- exp_int e = 10;
- exp_int e2 = 20;
- e = e2;
-
- EXPECT_EQ(20, e.value());
- EXPECT_EQ(20, e2.value());
-
- exp_int e3 = 10;
- exp_int e4 = 20;
- e3 = std::move(e4);
-
- EXPECT_EQ(20, e3.value());
- EXPECT_EQ(20, e4.value());
-
- exp_void e5 = unexpected(10);
- ASSERT_FALSE(e5.has_value());
- exp_void e6;
- e5 = e6;
-
- EXPECT_TRUE(e5.has_value());
- EXPECT_TRUE(e6.has_value());
-}
-
-TEST(Expected, testAssignableFromValue) {
- exp_int e = 10;
- e = 20;
- EXPECT_EQ(20, e.value());
-
- exp_double e2 = 3.5f;
- e2 = 10.5f;
- EXPECT_EQ(10.5f, e2.value());
-
- exp_string e3 = "hello";
- e3 = "world";
- EXPECT_EQ("world", e3.value());
-
- exp_void e4 = unexpected(10);
- ASSERT_FALSE(e4.has_value());
- e4 = {};
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testAssignableFromUnexpected) {
- exp_int e = 10;
- e = unexpected(30);
- EXPECT_FALSE(e.has_value());
- EXPECT_EQ(30, e.error());
-
- exp_double e2 = 3.5f;
- e2 = unexpected(10.5f);
- EXPECT_FALSE(e2.has_value());
- EXPECT_EQ(10.5f, e2.error());
-
- exp_string e3 = "hello";
- e3 = unexpected("world");
- EXPECT_FALSE(e3.has_value());
- EXPECT_EQ("world", e3.error());
-
- exp_void e4 = {};
- e4 = unexpected(10);
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testAssignableFromMovedValue) {
- std::string world = "world";
- exp_string e = "hello";
- e = std::move(world);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ("world", e.value());
- EXPECT_EQ("", world);
-}
-
-TEST(Expected, testAssignableFromMovedUnexpected) {
- std::string world = "world";
- exp_string e = "hello";
- e = unexpected(std::move(world));
-
- EXPECT_FALSE(e.has_value());
- EXPECT_EQ("world", e.error());
- EXPECT_EQ("", world);
-}
-
-TEST(Expected, testEmplace) {
- struct T {
- int a;
- double b;
- T() {}
- T(int a, double b) noexcept : a(a), b(b) {}
- };
- expected<T, int> exp;
- T& t = exp.emplace(3, 10.5f);
-
- EXPECT_TRUE(exp.has_value());
- EXPECT_EQ(3, t.a);
- EXPECT_EQ(10.5f, t.b);
- EXPECT_EQ(3, exp.value().a);
- EXPECT_EQ(10.5, exp.value().b);
-
- exp_void e = unexpected(10);
- ASSERT_FALSE(e.has_value());
- e.emplace();
- EXPECT_TRUE(e.has_value());
-}
-
-TEST(Expected, testSwapExpectedExpected) {
- exp_int e = 10;
- exp_int e2 = 20;
- e.swap(e2);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(20, e.value());
- EXPECT_EQ(10, e2.value());
-
- exp_void e3;
- exp_void e4;
- e3.swap(e4);
-
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testSwapUnexpectedUnexpected) {
- exp_int e = unexpected(10);
- exp_int e2 = unexpected(20);
- e.swap(e2);
- EXPECT_FALSE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_EQ(20, e.error());
- EXPECT_EQ(10, e2.error());
-
- exp_void e3 = unexpected(10);
- exp_void e4 = unexpected(20);
- e3.swap(e4);
- EXPECT_FALSE(e3.has_value());
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(20, e3.error());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testSwapExpectedUnepected) {
- exp_int e = 10;
- exp_int e2 = unexpected(30);
- e.swap(e2);
- EXPECT_FALSE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(30, e.error());
- EXPECT_EQ(10, e2.value());
-
- exp_void e3;
- exp_void e4 = unexpected(10);
- e3.swap(e4);
- EXPECT_FALSE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
- EXPECT_EQ(10, e3.error());
-}
-
-TEST(Expected, testDereference) {
- struct T {
- int a;
- double b;
- T() {}
- T(int a, double b) : a(a), b(b) {}
- };
- expected<T, int> exp = T(3, 10.5f);
-
- EXPECT_EQ(3, exp->a);
- EXPECT_EQ(10.5f, exp->b);
-
- EXPECT_EQ(3, (*exp).a);
- EXPECT_EQ(10.5f, (*exp).b);
-}
-
-TEST(Expected, testTest) {
- exp_int e = 10;
- EXPECT_TRUE(e.ok());
- EXPECT_TRUE(e.has_value());
-
- exp_int e2 = unexpected(10);
- EXPECT_FALSE(e2.ok());
- EXPECT_FALSE(e2.has_value());
-}
-
-TEST(Expected, testGetValue) {
- exp_int e = 10;
- EXPECT_EQ(10, e.value());
- EXPECT_EQ(10, e.value_or(20));
-
- exp_int e2 = unexpected(10);
- EXPECT_EQ(10, e2.error());
- EXPECT_EQ(20, e2.value_or(20));
-}
-
-TEST(Expected, testSameValues) {
- exp_int e = 10;
- exp_int e2 = 10;
- EXPECT_TRUE(e == e2);
- EXPECT_TRUE(e2 == e);
- EXPECT_FALSE(e != e2);
- EXPECT_FALSE(e2 != e);
-
- exp_void e3;
- exp_void e4;
- EXPECT_TRUE(e3 == e4);
- EXPECT_TRUE(e4 == e3);
- EXPECT_FALSE(e3 != e4);
- EXPECT_FALSE(e4 != e3);
-}
-
-TEST(Expected, testDifferentValues) {
- exp_int e = 10;
- exp_int e2 = 20;
- EXPECT_FALSE(e == e2);
- EXPECT_FALSE(e2 == e);
- EXPECT_TRUE(e != e2);
- EXPECT_TRUE(e2 != e);
-}
-
-TEST(Expected, testValueWithError) {
- exp_int e = 10;
- exp_int e2 = unexpected(10);
- EXPECT_FALSE(e == e2);
- EXPECT_FALSE(e2 == e);
- EXPECT_TRUE(e != e2);
- EXPECT_TRUE(e2 != e);
-
- exp_void e3;
- exp_void e4 = unexpected(10);
- EXPECT_FALSE(e3 == e4);
- EXPECT_FALSE(e4 == e3);
- EXPECT_TRUE(e3 != e4);
- EXPECT_TRUE(e4 != e3);
-}
-
-TEST(Expected, testSameErrors) {
- exp_int e = unexpected(10);
- exp_int e2 = unexpected(10);
- EXPECT_TRUE(e == e2);
- EXPECT_TRUE(e2 == e);
- EXPECT_FALSE(e != e2);
- EXPECT_FALSE(e2 != e);
-
- exp_void e3 = unexpected(10);
- exp_void e4 = unexpected(10);
- EXPECT_TRUE(e3 == e4);
- EXPECT_TRUE(e4 == e3);
- EXPECT_FALSE(e3 != e4);
- EXPECT_FALSE(e4 != e3);
-}
-
-TEST(Expected, testDifferentErrors) {
- exp_int e = unexpected(10);
- exp_int e2 = unexpected(20);
- EXPECT_FALSE(e == e2);
- EXPECT_FALSE(e2 == e);
- EXPECT_TRUE(e != e2);
- EXPECT_TRUE(e2 != e);
-
- exp_void e3 = unexpected(10);
- exp_void e4 = unexpected(20);
- EXPECT_FALSE(e3 == e4);
- EXPECT_FALSE(e4 == e3);
- EXPECT_TRUE(e3 != e4);
- EXPECT_TRUE(e4 != e3);
-}
-
-TEST(Expected, testCompareWithSameError) {
- exp_int e = unexpected(10);
- exp_int::unexpected_type error = 10;
- EXPECT_TRUE(e == error);
- EXPECT_TRUE(error == e);
- EXPECT_FALSE(e != error);
- EXPECT_FALSE(error != e);
-
- exp_void e2 = unexpected(10);
- exp_void::unexpected_type error2 = 10;
- EXPECT_TRUE(e2 == error2);
- EXPECT_TRUE(error2 == e2);
- EXPECT_FALSE(e2 != error2);
- EXPECT_FALSE(error2 != e2);
-}
-
-TEST(Expected, testCompareWithDifferentError) {
- exp_int e = unexpected(10);
- exp_int::unexpected_type error = 20;
- EXPECT_FALSE(e == error);
- EXPECT_FALSE(error == e);
- EXPECT_TRUE(e != error);
- EXPECT_TRUE(error != e);
-
- exp_void e2 = unexpected(10);
- exp_void::unexpected_type error2 = 20;
- EXPECT_FALSE(e2 == error2);
- EXPECT_FALSE(error2 == e2);
- EXPECT_TRUE(e2 != error2);
- EXPECT_TRUE(error2 != e2);
-}
-
-TEST(Expected, testCompareDifferentType) {
- expected<int,int> e = 10;
- expected<int32_t, int> e2 = 10;
- EXPECT_TRUE(e == e2);
- e2 = 20;
- EXPECT_FALSE(e == e2);
-
- expected<std::string_view,int> e3 = "hello";
- expected<std::string,int> e4 = "hello";
- EXPECT_TRUE(e3 == e4);
- e4 = "world";
- EXPECT_FALSE(e3 == e4);
-
- expected<void,int> e5;
- expected<int,int> e6 = 10;
- EXPECT_FALSE(e5 == e6);
- EXPECT_FALSE(e6 == e5);
-}
-
-TEST(Expected, testDivideExample) {
- struct QR {
- int quotient;
- int remainder;
- QR(int q, int r) noexcept : quotient(q), remainder(r) {}
- bool operator==(const QR& rhs) const {
- return quotient == rhs.quotient && remainder == rhs.remainder;
- }
- bool operator!=(const QR& rhs) const {
- return quotient != rhs.quotient || remainder == rhs.remainder;
- }
- };
-
- auto divide = [](int x, int y) -> expected<QR,E> {
- if (y == 0) {
- return unexpected(E("divide by zero", -1));
- } else {
- return QR(x / y, x % y);
- }
- };
-
- EXPECT_FALSE(divide(10, 0).ok());
- EXPECT_EQ("divide by zero", divide(10, 0).error().message);
- EXPECT_EQ(-1, divide(10, 0).error().cause);
-
- EXPECT_TRUE(divide(10, 3).ok());
- EXPECT_EQ(QR(3, 1), *divide(10, 3));
-}
-
-TEST(Expected, testPair) {
- auto test = [](bool yes) -> exp_pair {
- if (yes) {
- return exp_pair({"yes", 42});
- } else {
- return unexpected(42);
- }
- };
-
- auto r = test(true);
- EXPECT_TRUE(r.ok());
- EXPECT_EQ("yes", r->first);
-}
-
-TEST(Expected, testVoid) {
- auto test = [](bool ok) -> exp_void {
- if (ok) {
- return {};
- } else {
- return unexpected(10);
- }
- };
-
- auto r = test(true);
- EXPECT_TRUE(r.ok());
- r = test(false);
- EXPECT_FALSE(r.ok());
- EXPECT_EQ(10, r.error());
-}
-
-// copied from result_test.cpp
-struct ConstructorTracker {
- static size_t constructor_called;
- static size_t copy_constructor_called;
- static size_t move_constructor_called;
- static size_t copy_assignment_called;
- static size_t move_assignment_called;
-
- template <typename T,
- typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
- ConstructorTracker(T&& string) : string(string) {
- ++constructor_called;
- }
- ConstructorTracker(const ConstructorTracker& ct) {
- ++copy_constructor_called;
- string = ct.string;
- }
- ConstructorTracker(ConstructorTracker&& ct) noexcept {
- ++move_constructor_called;
- string = std::move(ct.string);
- }
- ConstructorTracker& operator=(const ConstructorTracker& ct) {
- ++copy_assignment_called;
- string = ct.string;
- return *this;
- }
- ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
- ++move_assignment_called;
- string = std::move(ct.string);
- return *this;
- }
- static void Reset() {
- constructor_called = 0;
- copy_constructor_called = 0;
- move_constructor_called = 0;
- copy_assignment_called = 0;
- move_assignment_called = 0;
- }
- std::string string;
-};
-
-size_t ConstructorTracker::constructor_called = 0;
-size_t ConstructorTracker::copy_constructor_called = 0;
-size_t ConstructorTracker::move_constructor_called = 0;
-size_t ConstructorTracker::copy_assignment_called = 0;
-size_t ConstructorTracker::move_assignment_called = 0;
-
-typedef expected<ConstructorTracker, int> exp_track;
-
-TEST(Expected, testNumberOfCopies) {
- // default constructor
- ConstructorTracker::Reset();
- exp_track e("hello");
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // copy constructor
- ConstructorTracker::Reset();
- exp_track e2 = e;
- EXPECT_EQ(0U, ConstructorTracker::constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // move constructor
- ConstructorTracker::Reset();
- exp_track e3 = std::move(e);
- EXPECT_EQ(0U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // construct from lvalue
- ConstructorTracker::Reset();
- ConstructorTracker ct = "hello";
- exp_track e4(ct);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // construct from rvalue
- ConstructorTracker::Reset();
- ConstructorTracker ct2 = "hello";
- exp_track e5(std::move(ct2));
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // copy assignment
- ConstructorTracker::Reset();
- exp_track e6 = "hello";
- exp_track e7 = "world";
- e7 = e6;
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // move assignment
- ConstructorTracker::Reset();
- exp_track e8 = "hello";
- exp_track e9 = "world";
- e9 = std::move(e8);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
-
- // swap
- ConstructorTracker::Reset();
- exp_track e10 = "hello";
- exp_track e11 = "world";
- std::swap(e10, e11);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
-}
-
-TEST(Expected, testNoCopyOnReturn) {
- auto test = [](const std::string& in) -> exp_track {
- if (in.empty()) {
- return "literal string";
- }
- if (in == "test2") {
- return ConstructorTracker(in + in + "2");
- }
- ConstructorTracker result(in + " " + in);
- return result;
- };
-
- ConstructorTracker::Reset();
- auto result1 = test("");
- ASSERT_TRUE(result1.ok());
- EXPECT_EQ("literal string", result1->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- ConstructorTracker::Reset();
- auto result2 = test("test2");
- ASSERT_TRUE(result2.ok());
- EXPECT_EQ("test2test22", result2->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- ConstructorTracker::Reset();
- auto result3 = test("test3");
- ASSERT_TRUE(result3.ok());
- EXPECT_EQ("test3 test3", result3->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-}
-
-TEST(Expected, testNested) {
- expected<exp_string, std::string> e = "hello";
-
- EXPECT_TRUE(e.ok());
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e.value().has_value());
- EXPECT_TRUE(e->ok());
- EXPECT_EQ("hello", e.value().value());
-
- expected<exp_string, std::string> e2 = unexpected("world");
- EXPECT_FALSE(e2.has_value());
- EXPECT_FALSE(e2.ok());
- EXPECT_EQ("world", e2.error());
-
- expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
- EXPECT_TRUE(e3.has_value());
- EXPECT_FALSE(e3.value().has_value());
- EXPECT_TRUE(e3.ok());
- EXPECT_FALSE(e3->ok());
- EXPECT_EQ("world", e3.value().error());
-}
-
-constexpr bool equals(const char* a, const char* b) {
- return (a == nullptr && b == nullptr) ||
- (a != nullptr && b != nullptr && *a == *b &&
- (*a == '\0' || equals(a + 1, b + 1)));
-}
-
-TEST(Expected, testConstexpr) {
- // Compliation error will occur if these expressions can't be
- // evaluated at compile time
- constexpr exp_int e(3);
- constexpr exp_int::unexpected_type err(3);
- constexpr int i = 4;
-
- // default constructor
- static_assert(exp_int().value() == 0);
- // copy constructor
- static_assert(exp_int(e).value() == 3);
- // move constructor
- static_assert(exp_int(exp_int(4)).value() == 4);
- // copy construct from value
- static_assert(exp_int(i).value() == 4);
- // copy construct from unexpected
- static_assert(exp_int(err).error() == 3);
- // move costruct from unexpected
- static_assert(exp_int(unexpected(3)).error() == 3);
- // observers
- static_assert(*exp_int(3) == 3);
- static_assert(exp_int(3).has_value() == true);
- static_assert(exp_int(3).value_or(4) == 3);
-
- typedef expected<const char*, int> exp_s;
- constexpr exp_s s("hello");
- constexpr const char* c = "hello";
- static_assert(equals(exp_s().value(), nullptr));
- static_assert(equals(exp_s(s).value(), "hello"));
- static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
- static_assert(equals(exp_s("hello").value(), "hello"));
- static_assert(equals(exp_s(c).value(), "hello"));
-}
-
-TEST(Expected, testWithNonConstructible) {
- struct AssertNotConstructed {
- AssertNotConstructed() = delete;
- };
-
- expected<int, AssertNotConstructed> v(42);
- EXPECT_TRUE(v.has_value());
- EXPECT_EQ(42, v.value());
-
- expected<AssertNotConstructed, int> e(unexpected(42));
- EXPECT_FALSE(e.has_value());
- EXPECT_EQ(42, e.error());
-}
-
-TEST(Expected, testWithMoveOnlyType) {
- typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
- exp_ptr e(std::make_unique<int>(3));
- exp_ptr e2(unexpected(std::make_unique<int>(4)));
-
- EXPECT_TRUE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_EQ(3, *(e.value()));
- EXPECT_EQ(4, *(e2.error()));
-
- e2 = std::move(e);
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(3, *(e2.value()));
-}
diff --git a/base/file.cpp b/base/file.cpp
deleted file mode 100644
index 97cc2b2..0000000
--- a/base/file.cpp
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/file.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <ftw.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <mutex>
-#include <string>
-#include <vector>
-
-#if defined(__APPLE__)
-#include <mach-o/dyld.h>
-#endif
-#if defined(_WIN32)
-#include <direct.h>
-#include <windows.h>
-#define O_NOFOLLOW 0
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
-#include "android-base/logging.h" // and must be after windows.h for ERROR
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-
-namespace {
-
-#ifdef _WIN32
-static int mkstemp(char* name_template, size_t size_in_chars) {
- std::wstring path;
- CHECK(android::base::UTF8ToWide(name_template, &path))
- << "path can't be converted to wchar: " << name_template;
- if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
- return -1;
- }
-
- // Use open() to match the close() that TemporaryFile's destructor does.
- // Use O_BINARY to match base file APIs.
- int fd = _wopen(path.c_str(), O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- return -1;
- }
-
- std::string path_utf8;
- CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
- CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
- << "utf8 path can't be assigned back to name_template";
-
- return fd;
-}
-
-static char* mkdtemp(char* name_template, size_t size_in_chars) {
- std::wstring path;
- CHECK(android::base::UTF8ToWide(name_template, &path))
- << "path can't be converted to wchar: " << name_template;
-
- if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
- return nullptr;
- }
-
- if (_wmkdir(path.c_str()) != 0) {
- return nullptr;
- }
-
- std::string path_utf8;
- CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
- CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
- << "utf8 path can't be assigned back to name_template";
-
- return name_template;
-}
-#endif
-
-std::string GetSystemTempDir() {
-#if defined(__ANDROID__)
- const auto* tmpdir = getenv("TMPDIR");
- if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
- if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
- return tmpdir;
- }
- // Tests running in app context can't access /data/local/tmp,
- // so try current directory if /data/local/tmp is not accessible.
- return ".";
-#elif defined(_WIN32)
- wchar_t tmp_dir_w[MAX_PATH];
- DWORD result = GetTempPathW(std::size(tmp_dir_w), tmp_dir_w); // checks TMP env
- CHECK_NE(result, 0ul) << "GetTempPathW failed, error: " << GetLastError();
- CHECK_LT(result, std::size(tmp_dir_w)) << "path truncated to: " << result;
-
- // GetTempPath() returns a path with a trailing slash, but init()
- // does not expect that, so remove it.
- if (tmp_dir_w[result - 1] == L'\\') {
- tmp_dir_w[result - 1] = L'\0';
- }
-
- std::string tmp_dir;
- CHECK(android::base::WideToUTF8(tmp_dir_w, &tmp_dir)) << "path can't be converted to utf8";
-
- return tmp_dir;
-#else
- const auto* tmpdir = getenv("TMPDIR");
- if (tmpdir == nullptr) tmpdir = "/tmp";
- return tmpdir;
-#endif
-}
-
-} // namespace
-
-TemporaryFile::TemporaryFile() {
- init(GetSystemTempDir());
-}
-
-TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
- init(tmp_dir);
-}
-
-TemporaryFile::~TemporaryFile() {
- if (fd != -1) {
- close(fd);
- }
- if (remove_file_) {
- unlink(path);
- }
-}
-
-int TemporaryFile::release() {
- int result = fd;
- fd = -1;
- return result;
-}
-
-void TemporaryFile::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
-#if defined(_WIN32)
- fd = mkstemp(path, sizeof(path));
-#else
- fd = mkstemp(path);
-#endif
-}
-
-TemporaryDir::TemporaryDir() {
- init(GetSystemTempDir());
-}
-
-TemporaryDir::~TemporaryDir() {
- if (!remove_dir_and_contents_) return;
-
- auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
- switch (file_type) {
- case FTW_D:
- case FTW_DP:
- case FTW_DNR:
- if (rmdir(child) == -1) {
- PLOG(ERROR) << "rmdir " << child;
- }
- break;
- case FTW_NS:
- default:
- if (rmdir(child) != -1) break;
- // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
- FALLTHROUGH_INTENDED;
- case FTW_F:
- case FTW_SL:
- case FTW_SLN:
- if (unlink(child) == -1) {
- PLOG(ERROR) << "unlink " << child;
- }
- break;
- }
- return 0;
- };
-
- nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
-}
-
-bool TemporaryDir::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
-#if defined(_WIN32)
- return (mkdtemp(path, sizeof(path)) != nullptr);
-#else
- return (mkdtemp(path) != nullptr);
-#endif
-}
-
-namespace android {
-namespace base {
-
-// Versions of standard library APIs that support UTF-8 strings.
-using namespace android::base::utf8;
-
-bool ReadFdToString(borrowed_fd fd, std::string* content) {
- content->clear();
-
- // Although original we had small files in mind, this code gets used for
- // very large files too, where the std::string growth heuristics might not
- // be suitable. https://code.google.com/p/android/issues/detail?id=258500.
- struct stat sb;
- if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
- content->reserve(sb.st_size);
- }
-
- char buf[BUFSIZ] __attribute__((__uninitialized__));
- ssize_t n;
- while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
- content->append(buf, n);
- }
- return (n == 0) ? true : false;
-}
-
-bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) {
- content->clear();
-
- int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
- if (fd == -1) {
- return false;
- }
- return ReadFdToString(fd, content);
-}
-
-bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
- const char* p = content.data();
- size_t left = content.size();
- while (left > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
- if (n == -1) {
- return false;
- }
- p += n;
- left -= n;
- }
- return true;
-}
-
-static bool CleanUpAfterFailedWrite(const std::string& path) {
- // Something went wrong. Let's not leave a corrupt file lying around.
- int saved_errno = errno;
- unlink(path.c_str());
- errno = saved_errno;
- return false;
-}
-
-#if !defined(_WIN32)
-bool WriteStringToFile(const std::string& content, const std::string& path,
- mode_t mode, uid_t owner, gid_t group,
- bool follow_symlinks) {
- int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
- (follow_symlinks ? 0 : O_NOFOLLOW);
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
- if (fd == -1) {
- PLOG(ERROR) << "android::WriteStringToFile open failed";
- return false;
- }
-
- // We do an explicit fchmod here because we assume that the caller really
- // meant what they said and doesn't want the umask-influenced mode.
- if (fchmod(fd, mode) == -1) {
- PLOG(ERROR) << "android::WriteStringToFile fchmod failed";
- return CleanUpAfterFailedWrite(path);
- }
- if (fchown(fd, owner, group) == -1) {
- PLOG(ERROR) << "android::WriteStringToFile fchown failed";
- return CleanUpAfterFailedWrite(path);
- }
- if (!WriteStringToFd(content, fd)) {
- PLOG(ERROR) << "android::WriteStringToFile write failed";
- return CleanUpAfterFailedWrite(path);
- }
- return true;
-}
-#endif
-
-bool WriteStringToFile(const std::string& content, const std::string& path,
- bool follow_symlinks) {
- int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
- (follow_symlinks ? 0 : O_NOFOLLOW);
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
- if (fd == -1) {
- return false;
- }
- return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
-}
-
-bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
- uint8_t* p = reinterpret_cast<uint8_t*>(data);
- size_t remaining = byte_count;
- while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
- if (n <= 0) return false;
- p += n;
- remaining -= n;
- }
- return true;
-}
-
-#if defined(_WIN32)
-// Windows implementation of pread. Note that this DOES move the file descriptors read position,
-// but it does so atomically.
-static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
- DWORD bytes_read;
- OVERLAPPED overlapped;
- memset(&overlapped, 0, sizeof(OVERLAPPED));
- overlapped.Offset = static_cast<DWORD>(offset);
- overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
- if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
- static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
- // In case someone tries to read errno (since this is masquerading as a POSIX call)
- errno = EIO;
- return -1;
- }
- return static_cast<ssize_t>(bytes_read);
-}
-#endif
-
-bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
- uint8_t* p = reinterpret_cast<uint8_t*>(data);
- while (byte_count > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
- if (n <= 0) return false;
- p += n;
- byte_count -= n;
- offset += n;
- }
- return true;
-}
-
-bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
- const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
- size_t remaining = byte_count;
- while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
- if (n == -1) return false;
- p += n;
- remaining -= n;
- }
- return true;
-}
-
-bool RemoveFileIfExists(const std::string& path, std::string* err) {
- struct stat st;
-#if defined(_WIN32)
- // TODO: Windows version can't handle symbolic links correctly.
- int result = stat(path.c_str(), &st);
- bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
-#else
- int result = lstat(path.c_str(), &st);
- bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
-#endif
- if (result == -1) {
- if (errno == ENOENT || errno == ENOTDIR) return true;
- if (err != nullptr) *err = strerror(errno);
- return false;
- }
-
- if (result == 0) {
- if (!file_type_removable) {
- if (err != nullptr) {
- *err = "is not a regular file or symbolic link";
- }
- return false;
- }
- if (unlink(path.c_str()) == -1) {
- if (err != nullptr) {
- *err = strerror(errno);
- }
- return false;
- }
- }
- return true;
-}
-
-#if !defined(_WIN32)
-bool Readlink(const std::string& path, std::string* result) {
- result->clear();
-
- // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
- // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
- // waste memory to just start there. We add 1 so that we can recognize
- // whether it actually fit (rather than being truncated to 4095).
- std::vector<char> buf(4095 + 1);
- while (true) {
- ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
- // Unrecoverable error?
- if (size == -1) return false;
- // It fit! (If size == buf.size(), it may have been truncated.)
- if (static_cast<size_t>(size) < buf.size()) {
- result->assign(&buf[0], size);
- return true;
- }
- // Double our buffer and try again.
- buf.resize(buf.size() * 2);
- }
-}
-#endif
-
-#if !defined(_WIN32)
-bool Realpath(const std::string& path, std::string* result) {
- result->clear();
-
- // realpath may exit with EINTR. Retry if so.
- char* realpath_buf = nullptr;
- do {
- realpath_buf = realpath(path.c_str(), nullptr);
- } while (realpath_buf == nullptr && errno == EINTR);
-
- if (realpath_buf == nullptr) {
- return false;
- }
- result->assign(realpath_buf);
- free(realpath_buf);
- return true;
-}
-#endif
-
-std::string GetExecutablePath() {
-#if defined(__linux__)
- std::string path;
- android::base::Readlink("/proc/self/exe", &path);
- return path;
-#elif defined(__APPLE__)
- char path[PATH_MAX + 1];
- uint32_t path_len = sizeof(path);
- int rc = _NSGetExecutablePath(path, &path_len);
- if (rc < 0) {
- std::unique_ptr<char> path_buf(new char[path_len]);
- _NSGetExecutablePath(path_buf.get(), &path_len);
- return path_buf.get();
- }
- return path;
-#elif defined(_WIN32)
- char path[PATH_MAX + 1];
- DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
- if (result == 0 || result == sizeof(path) - 1) return "";
- path[PATH_MAX - 1] = 0;
- return path;
-#else
-#error unknown OS
-#endif
-}
-
-std::string GetExecutableDirectory() {
- return Dirname(GetExecutablePath());
-}
-
-std::string Basename(const std::string& path) {
- // Copy path because basename may modify the string passed in.
- std::string result(path);
-
-#if !defined(__BIONIC__)
- // Use lock because basename() may write to a process global and return a
- // pointer to that. Note that this locking strategy only works if all other
- // callers to basename in the process also grab this same lock, but its
- // better than nothing. Bionic's basename returns a thread-local buffer.
- static std::mutex& basename_lock = *new std::mutex();
- std::lock_guard<std::mutex> lock(basename_lock);
-#endif
-
- // Note that if std::string uses copy-on-write strings, &str[0] will cause
- // the copy to be made, so there is no chance of us accidentally writing to
- // the storage for 'path'.
- char* name = basename(&result[0]);
-
- // In case basename returned a pointer to a process global, copy that string
- // before leaving the lock.
- result.assign(name);
-
- return result;
-}
-
-std::string Dirname(const std::string& path) {
- // Copy path because dirname may modify the string passed in.
- std::string result(path);
-
-#if !defined(__BIONIC__)
- // Use lock because dirname() may write to a process global and return a
- // pointer to that. Note that this locking strategy only works if all other
- // callers to dirname in the process also grab this same lock, but its
- // better than nothing. Bionic's dirname returns a thread-local buffer.
- static std::mutex& dirname_lock = *new std::mutex();
- std::lock_guard<std::mutex> lock(dirname_lock);
-#endif
-
- // Note that if std::string uses copy-on-write strings, &str[0] will cause
- // the copy to be made, so there is no chance of us accidentally writing to
- // the storage for 'path'.
- char* parent = dirname(&result[0]);
-
- // In case dirname returned a pointer to a process global, copy that string
- // before leaving the lock.
- result.assign(parent);
-
- return result;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
deleted file mode 100644
index 120228d..0000000
--- a/base/file_test.cpp
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * 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.
- */
-
-#include "android-base/file.h"
-
-#include "android-base/utf8.h"
-
-#include <gtest/gtest.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <wchar.h>
-
-#include <string>
-
-#if !defined(_WIN32)
-#include <pwd.h>
-#else
-#include <windows.h>
-#endif
-
-#include "android-base/logging.h" // and must be after windows.h for ERROR
-
-TEST(file, ReadFileToString_ENOENT) {
- std::string s("hello");
- errno = 0;
- ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s));
- EXPECT_EQ(ENOENT, errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
-TEST(file, ReadFileToString_WriteStringToFile) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
- << strerror(errno);
- std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
- << strerror(errno);
- EXPECT_EQ("abc", s);
-}
-
-// symlinks require elevated privileges on Windows.
-#if !defined(_WIN32)
-TEST(file, ReadFileToString_WriteStringToFile_symlink) {
- TemporaryFile target, link;
- ASSERT_EQ(0, unlink(link.path));
- ASSERT_EQ(0, symlink(target.path, link.path));
- ASSERT_FALSE(android::base::WriteStringToFile("foo", link.path, false));
- ASSERT_EQ(ELOOP, errno);
- ASSERT_TRUE(android::base::WriteStringToFile("foo", link.path, true));
-
- std::string s;
- ASSERT_FALSE(android::base::ReadFileToString(link.path, &s));
- ASSERT_EQ(ELOOP, errno);
- ASSERT_TRUE(android::base::ReadFileToString(link.path, &s, true));
- ASSERT_EQ("foo", s);
-}
-#endif
-
-// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
-// sense on Windows.
-#if !defined(_WIN32)
-TEST(file, WriteStringToFile2) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
- getuid(), getgid()))
- << strerror(errno);
- struct stat sb;
- ASSERT_EQ(0, stat(tf.path, &sb));
- ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
- ASSERT_EQ(getuid(), sb.st_uid);
- ASSERT_EQ(getgid(), sb.st_gid);
- std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
- << strerror(errno);
- EXPECT_EQ("abc", s);
-}
-#endif
-
-#if defined(_WIN32)
-TEST(file, NonUnicodeCharsWindows) {
- constexpr auto kMaxEnvVariableValueSize = 32767;
- std::wstring old_tmp;
- old_tmp.resize(kMaxEnvVariableValueSize);
- old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
- if (old_tmp.empty()) {
- // Can't continue with empty TMP folder.
- return;
- }
-
- std::wstring new_tmp = old_tmp;
- if (new_tmp.back() != L'\\') {
- new_tmp.push_back(L'\\');
- }
-
- {
- auto path(new_tmp + L"锦绣成都\\");
- _wmkdir(path.c_str());
- ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
- }
- {
- auto path(new_tmp + L"директория с длинным именем\\");
- _wmkdir(path.c_str());
- ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
- }
- {
- auto path(new_tmp + L"äüöß weiß\\");
- _wmkdir(path.c_str());
- ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
- }
-
- SetEnvironmentVariableW(L"TMP", old_tmp.c_str());
-}
-
-TEST(file, RootDirectoryWindows) {
- constexpr auto kMaxEnvVariableValueSize = 32767;
- std::wstring old_tmp;
- bool tmp_is_empty = false;
- old_tmp.resize(kMaxEnvVariableValueSize);
- old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
- if (old_tmp.empty()) {
- tmp_is_empty = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
- }
- SetEnvironmentVariableW(L"TMP", L"C:");
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
-
- SetEnvironmentVariableW(L"TMP", tmp_is_empty ? nullptr : old_tmp.c_str());
-}
-#endif
-
-TEST(file, WriteStringToFd) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
-}
-
-TEST(file, WriteFully) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- s.resize(3);
- ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
- << strerror(errno);
- EXPECT_EQ("abc", s);
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- s.resize(1024);
- ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
-}
-
-TEST(file, RemoveFileIfExists) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- close(tf.fd);
- tf.fd = -1;
- std::string err;
- ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err)) << err;
- ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path));
- TemporaryDir td;
- ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
- ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
- ASSERT_EQ("is not a regular file or symbolic link", err);
-}
-
-TEST(file, RemoveFileIfExists_ENOTDIR) {
- TemporaryFile tf;
- close(tf.fd);
- tf.fd = -1;
- std::string err{"xxx"};
- ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
- ASSERT_EQ("xxx", err);
-}
-
-#if !defined(_WIN32)
-TEST(file, RemoveFileIfExists_EACCES) {
- // EACCES -- one of the directories in the path has no search permission
- // root can bypass permission restrictions, so drop root.
- if (getuid() == 0) {
- passwd* shell = getpwnam("shell");
- setgid(shell->pw_gid);
- setuid(shell->pw_uid);
- }
-
- TemporaryDir td;
- TemporaryFile tf(td.path);
- close(tf.fd);
- tf.fd = -1;
- std::string err{"xxx"};
- // Remove dir's search permission.
- ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
- ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
- ASSERT_EQ("Permission denied", err);
- // Set dir's search permission again.
- ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
- ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
-}
-#endif
-
-TEST(file, Readlink) {
-#if !defined(_WIN32)
- // Linux doesn't allow empty symbolic links.
- std::string min("x");
- // ext2 and ext4 both have PAGE_SIZE limits.
- // If file encryption is enabled, there's extra overhead to store the
- // size of the encrypted symlink target. There's also an off-by-one
- // in current kernels (and marlin/sailfish where we're seeing this
- // failure are still on 3.18, far from current). http://b/33306057.
- std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
-
- TemporaryDir td;
- std::string min_path{std::string(td.path) + "/" + "min"};
- std::string max_path{std::string(td.path) + "/" + "max"};
-
- ASSERT_EQ(0, symlink(min.c_str(), min_path.c_str()));
- ASSERT_EQ(0, symlink(max.c_str(), max_path.c_str()));
-
- std::string result;
-
- result = "wrong";
- ASSERT_TRUE(android::base::Readlink(min_path, &result));
- ASSERT_EQ(min, result);
-
- result = "wrong";
- ASSERT_TRUE(android::base::Readlink(max_path, &result));
- ASSERT_EQ(max, result);
-#endif
-}
-
-TEST(file, Realpath) {
-#if !defined(_WIN32)
- TemporaryDir td;
- std::string basename = android::base::Basename(td.path);
- std::string dir_name = android::base::Dirname(td.path);
- std::string base_dir_name = android::base::Basename(dir_name);
-
- {
- std::string path = dir_name + "/../" + base_dir_name + "/" + basename;
- std::string result;
- ASSERT_TRUE(android::base::Realpath(path, &result));
- ASSERT_EQ(td.path, result);
- }
-
- {
- std::string path = std::string(td.path) + "/..";
- std::string result;
- ASSERT_TRUE(android::base::Realpath(path, &result));
- ASSERT_EQ(dir_name, result);
- }
-
- {
- errno = 0;
- std::string path = std::string(td.path) + "/foo.noent";
- std::string result = "wrong";
- ASSERT_TRUE(!android::base::Realpath(path, &result));
- ASSERT_TRUE(result.empty());
- ASSERT_EQ(ENOENT, errno);
- }
-#endif
-}
-
-TEST(file, GetExecutableDirectory) {
- std::string path = android::base::GetExecutableDirectory();
- ASSERT_NE("", path);
- ASSERT_NE(android::base::GetExecutablePath(), path);
- ASSERT_EQ('/', path[0]);
- ASSERT_NE('/', path[path.size() - 1]);
-}
-
-TEST(file, GetExecutablePath) {
- ASSERT_NE("", android::base::GetExecutablePath());
-}
-
-TEST(file, Basename) {
- EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
- EXPECT_EQ("sh", android::base::Basename("sh"));
- EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
-}
-
-TEST(file, Dirname) {
- EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
- EXPECT_EQ(".", android::base::Dirname("sh"));
- EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
-}
-
-TEST(file, ReadFileToString_capacity) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
-
- // For a huge file, the overhead should still be small.
- std::string s;
- size_t size = 16 * 1024 * 1024;
- ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(size, s.size());
- EXPECT_LT(s.capacity(), size + 16);
-
- // Even for weird badly-aligned sizes.
- size += 12345;
- ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(size, s.size());
- EXPECT_LT(s.capacity(), size + 16);
-
- // We'll shrink an enormous string if you read a small file into it.
- size = 64;
- ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(size, s.size());
- EXPECT_LT(s.capacity(), size + 16);
-}
-
-TEST(file, ReadFileToString_capacity_0) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
-
- // Because /proc reports its files as zero-length, we don't actually trust
- // any file that claims to be zero-length. Rather than add increasingly
- // complex heuristics for shrinking the passed-in string in that case, we
- // currently leave it alone.
- std::string s;
- size_t initial_capacity = s.capacity();
- ASSERT_TRUE(android::base::WriteStringToFile("", tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(0U, s.size());
- EXPECT_EQ(initial_capacity, s.capacity());
-}
diff --git a/base/format_benchmark.cpp b/base/format_benchmark.cpp
deleted file mode 100644
index 9590b23..0000000
--- a/base/format_benchmark.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/format.h"
-
-#include <limits>
-
-#include <benchmark/benchmark.h>
-
-#include "android-base/stringprintf.h"
-
-using android::base::StringPrintf;
-
-static void BenchmarkFormatInt(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(fmt::format("{} {} {}", 42, std::numeric_limits<int>::min(),
- std::numeric_limits<int>::max()));
- }
-}
-
-BENCHMARK(BenchmarkFormatInt);
-
-static void BenchmarkStringPrintfInt(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(StringPrintf("%d %d %d", 42, std::numeric_limits<int>::min(),
- std::numeric_limits<int>::max()));
- }
-}
-
-BENCHMARK(BenchmarkStringPrintfInt);
-
-static void BenchmarkFormatFloat(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(fmt::format("{} {} {}", 42.42, std::numeric_limits<float>::min(),
- std::numeric_limits<float>::max()));
- }
-}
-
-BENCHMARK(BenchmarkFormatFloat);
-
-static void BenchmarkStringPrintfFloat(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(StringPrintf("%f %f %f", 42.42, std::numeric_limits<float>::min(),
- std::numeric_limits<float>::max()));
- }
-}
-
-BENCHMARK(BenchmarkStringPrintfFloat);
-
-static void BenchmarkFormatStrings(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(fmt::format("{} hello there {}", "hi,", "!!"));
- }
-}
-
-BENCHMARK(BenchmarkFormatStrings);
-
-static void BenchmarkStringPrintfStrings(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(StringPrintf("%s hello there %s", "hi,", "!!"));
- }
-}
-
-BENCHMARK(BenchmarkStringPrintfStrings);
-
-// Run the benchmark
-BENCHMARK_MAIN();
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
deleted file mode 100644
index 11fcf71..0000000
--- a/base/include/android-base/chrono_utils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <chrono>
-#include <sstream>
-
-#if __cplusplus > 201103L && !defined(__WIN32) // C++14
-using namespace std::chrono_literals;
-#endif
-
-namespace android {
-namespace base {
-
-// A std::chrono clock based on CLOCK_BOOTTIME.
-class boot_clock {
- public:
- typedef std::chrono::nanoseconds duration;
- typedef std::chrono::time_point<boot_clock, duration> time_point;
-
- static time_point now();
-};
-
-class Timer {
- public:
- Timer() : start_(boot_clock::now()) {}
-
- std::chrono::milliseconds duration() const {
- return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
- }
-
- private:
- boot_clock::time_point start_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Timer& t);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
deleted file mode 100644
index e4197b1..0000000
--- a/base/include/android-base/cmsg.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <type_traits>
-#include <vector>
-
-#include <android-base/collections.h>
-#include <android-base/macros.h>
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace base {
-
-#if !defined(_WIN32)
-
-// Helpers for sending and receiving file descriptors across Unix domain sockets.
-//
-// The cmsg(3) API is very hard to get right, with multiple landmines that can
-// lead to death. Almost all of the uses of cmsg in Android make at least one of
-// the following mistakes:
-//
-// - not aligning the cmsg buffer
-// - leaking fds if more fds are received than expected
-// - blindly dereferencing CMSG_DATA without checking the header
-// - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
-// - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
-// - using a length specified in number of fds instead of bytes
-//
-// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
-
-// Send file descriptors across a Unix domain socket.
-//
-// Note that the write can return short if the socket type is SOCK_STREAM. When
-// this happens, file descriptors are still sent to the other end, but with
-// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
-ssize_t SendFileDescriptorVector(borrowed_fd sock, const void* data, size_t len,
- const std::vector<int>& fds);
-
-// Receive file descriptors from a Unix domain socket.
-//
-// If more FDs (or bytes, for datagram sockets) are received than expected,
-// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
-ssize_t ReceiveFileDescriptorVector(borrowed_fd sock, void* data, size_t len, size_t max_fds,
- std::vector<android::base::unique_fd>* fds);
-
-// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
-// SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
-template <typename... Args>
-ssize_t SendFileDescriptors(borrowed_fd sock, const void* data, size_t len, Args&&... sent_fds) {
- // Do not allow implicit conversion to int: people might try to do something along the lines of:
- // SendFileDescriptors(..., std::move(a_unique_fd))
- // and be surprised when the unique_fd isn't closed afterwards.
- AssertType<int>(std::forward<Args>(sent_fds)...);
- std::vector<int> fds;
- Append(fds, std::forward<Args>(sent_fds)...);
- return SendFileDescriptorVector(sock, data, len, fds);
-}
-
-// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
-// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
-// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
-// In both cases, all arguments are cleared and any received FDs are thrown away.
-template <typename... Args>
-ssize_t ReceiveFileDescriptors(borrowed_fd sock, void* data, size_t len, Args&&... received_fds) {
- std::vector<unique_fd*> fds;
- Append(fds, std::forward<Args>(received_fds)...);
-
- std::vector<unique_fd> result;
- ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
- if (rc == -1 || result.size() != fds.size()) {
- int err = rc == -1 ? errno : ENOMSG;
- for (unique_fd* fd : fds) {
- fd->reset();
- }
- errno = err;
- return -1;
- }
-
- for (size_t i = 0; i < fds.size(); ++i) {
- *fds[i] = std::move(result[i]);
- }
- return rc;
-}
-
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
deleted file mode 100644
index be0683a..0000000
--- a/base/include/android-base/collections.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utility>
-
-namespace android {
-namespace base {
-
-// Helpers for converting a variadic template parameter pack to a homogeneous collection.
-// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
-//
-// Use as follows:
-//
-// template <typename... Args>
-// std::vector<int> CreateVector(Args&&... args) {
-// std::vector<int> result;
-// Append(result, std::forward<Args>(args)...);
-// return result;
-// }
-template <typename CollectionType, typename T>
-void Append(CollectionType& collection, T&& arg) {
- collection.push_back(std::forward<T>(arg));
-}
-
-template <typename CollectionType, typename T, typename... Args>
-void Append(CollectionType& collection, T&& arg, Args&&... args) {
- collection.push_back(std::forward<T>(arg));
- return Append(collection, std::forward<Args>(args)...);
-}
-
-// Assert that all of the arguments in a variadic template parameter pack are of a given type
-// after std::decay.
-template <typename T, typename Arg, typename... Args>
-void AssertType(Arg&&) {
- static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
-}
-
-template <typename T, typename Arg, typename... Args>
-void AssertType(Arg&&, Args&&... args) {
- static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
- AssertType<T>(std::forward<Args>(args)...);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
deleted file mode 100644
index 8fa6365..0000000
--- a/base/include/android-base/endian.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-/* A cross-platform equivalent of bionic's <sys/endian.h>. */
-
-/* For __BIONIC__ and __GLIBC__ */
-#include <sys/cdefs.h>
-
-#if defined(__BIONIC__)
-
-#include <sys/endian.h>
-
-#elif defined(__GLIBC__)
-
-/* glibc's <endian.h> is like bionic's <sys/endian.h>. */
-#include <endian.h>
-
-/* glibc keeps htons and htonl in <netinet/in.h>. */
-#include <netinet/in.h>
-
-/* glibc doesn't have the 64-bit variants. */
-#define htonq(x) htobe64(x)
-#define ntohq(x) be64toh(x)
-
-/* glibc has different names to BSD for these. */
-#define betoh16(x) be16toh(x)
-#define betoh32(x) be32toh(x)
-#define betoh64(x) be64toh(x)
-#define letoh16(x) le16toh(x)
-#define letoh32(x) le32toh(x)
-#define letoh64(x) le64toh(x)
-
-#else
-
-#if defined(__APPLE__)
-/* macOS has some of the basics. */
-#include <sys/_endian.h>
-#else
-/* Windows has some of the basics as well. */
-#include <sys/param.h>
-#include <winsock2.h>
-/* winsock2.h *must* be included before the following four macros. */
-#define htons(x) __builtin_bswap16(x)
-#define htonl(x) __builtin_bswap32(x)
-#define ntohs(x) __builtin_bswap16(x)
-#define ntohl(x) __builtin_bswap32(x)
-#endif
-
-/* Neither macOS nor Windows have the rest. */
-
-#define __LITTLE_ENDIAN 1234
-#define __BIG_ENDIAN 4321
-#define __BYTE_ORDER __LITTLE_ENDIAN
-
-#define htonq(x) __builtin_bswap64(x)
-
-#define ntohq(x) __builtin_bswap64(x)
-
-#define htobe16(x) __builtin_bswap16(x)
-#define htobe32(x) __builtin_bswap32(x)
-#define htobe64(x) __builtin_bswap64(x)
-
-#define betoh16(x) __builtin_bswap16(x)
-#define betoh32(x) __builtin_bswap32(x)
-#define betoh64(x) __builtin_bswap64(x)
-
-#define htole16(x) (x)
-#define htole32(x) (x)
-#define htole64(x) (x)
-
-#define letoh16(x) (x)
-#define letoh32(x) (x)
-#define letoh64(x) (x)
-
-#define be16toh(x) __builtin_bswap16(x)
-#define be32toh(x) __builtin_bswap32(x)
-#define be64toh(x) __builtin_bswap64(x)
-
-#define le16toh(x) (x)
-#define le32toh(x) (x)
-#define le64toh(x) (x)
-
-#endif
diff --git a/base/include/android-base/errno_restorer.h b/base/include/android-base/errno_restorer.h
deleted file mode 100644
index 1c8597c..0000000
--- a/base/include/android-base/errno_restorer.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2020 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 "errno.h"
-
-#include "android-base/macros.h"
-
-namespace android {
-namespace base {
-
-class ErrnoRestorer {
- public:
- ErrnoRestorer() : saved_errno_(errno) {}
-
- ~ErrnoRestorer() { errno = saved_errno_; }
-
- // Allow this object to be used as part of && operation.
- operator bool() const { return true; }
-
- private:
- const int saved_errno_;
-
- DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
deleted file mode 100644
index 06f29fc..0000000
--- a/base/include/android-base/errors.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-// Portable error handling functions. This is only necessary for host-side
-// code that needs to be cross-platform; code that is only run on Unix should
-// just use errno and strerror() for simplicity.
-//
-// There is some complexity since Windows has (at least) three different error
-// numbers, not all of which share the same type:
-// * errno: for C runtime errors.
-// * GetLastError(): Windows non-socket errors.
-// * WSAGetLastError(): Windows socket errors.
-// errno can be passed to strerror() on all platforms, but the other two require
-// special handling to get the error string. Refer to Microsoft documentation
-// to determine which error code to check for each function.
-
-#pragma once
-
-#include <string>
-
-namespace android {
-namespace base {
-
-// Returns a string describing the given system error code. |error_code| must
-// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing
-// errno on Windows has undefined behavior.
-std::string SystemErrorCodeToString(int error_code);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
deleted file mode 100644
index 9470344..0000000
--- a/base/include/android-base/expected.h
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <algorithm>
-#include <initializer_list>
-#include <type_traits>
-#include <utility>
-#include <variant>
-
-// android::base::expected is an Android implementation of the std::expected
-// proposal.
-// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
-//
-// Usage:
-// using android::base::expected;
-// using android::base::unexpected;
-//
-// expected<double,std::string> safe_divide(double i, double j) {
-// if (j == 0) return unexpected("divide by zero");
-// else return i / j;
-// }
-//
-// void test() {
-// auto q = safe_divide(10, 0);
-// if (q) { printf("%f\n", q.value()); }
-// else { printf("%s\n", q.error().c_str()); }
-// }
-//
-// When the proposal becomes part of the standard and is implemented by
-// libcxx, this will be removed and android::base::expected will be
-// type alias to std::expected.
-//
-
-namespace android {
-namespace base {
-
-// Synopsis
-template<class T, class E>
- class expected;
-
-template<class E>
- class unexpected;
-template<class E>
- unexpected(E) -> unexpected<E>;
-
-template<class E>
- class bad_expected_access;
-
-template<>
- class bad_expected_access<void>;
-
-struct unexpect_t {
- explicit unexpect_t() = default;
-};
-inline constexpr unexpect_t unexpect{};
-
-// macros for SFINAE
-#define _ENABLE_IF(...) \
- , std::enable_if_t<(__VA_ARGS__)>* = nullptr
-
-// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
-// ignored when used as a return value. This is off by default.
-#ifdef NODISCARD_EXPECTED
-#define _NODISCARD_ [[nodiscard]]
-#else
-#define _NODISCARD_
-#endif
-
-// Class expected
-template<class T, class E>
-class _NODISCARD_ expected {
- public:
- using value_type = T;
- using error_type = E;
- using unexpected_type = unexpected<E>;
-
- template<class U>
- using rebind = expected<U, error_type>;
-
- // constructors
- constexpr expected() = default;
- constexpr expected(const expected& rhs) = default;
- constexpr expected(expected&& rhs) noexcept = default;
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- !(!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const expected<U, G>& rhs) {
- if (rhs.has_value()) var_ = rhs.value();
- else var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- (!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* explicit */
- )>
- constexpr explicit expected(const expected<U, G>& rhs) {
- if (rhs.has_value()) var_ = rhs.value();
- else var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- !(!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(expected<U, G>&& rhs) {
- if (rhs.has_value()) var_ = std::move(rhs.value());
- else var_ = unexpected(std::move(rhs.error()));
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- (!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* explicit */
- )>
- constexpr explicit expected(expected<U, G>&& rhs) {
- if (rhs.has_value()) var_ = std::move(rhs.value());
- else var_ = unexpected(std::move(rhs.error()));
- }
-
- template <class U = T _ENABLE_IF(
- std::is_constructible_v<T, U&&> &&
- !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
- !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- std::is_convertible_v<U&&, T> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
- constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
-
- template <class U = T _ENABLE_IF(
- std::is_constructible_v<T, U&&> &&
- !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
- !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !std::is_convertible_v<U&&, T> /* explicit */
- )>
- // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
- constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- std::is_convertible_v<const G&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, e.value()) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- !std::is_convertible_v<const G&, E> /* explicit */
- )>
- constexpr explicit expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, E(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- std::is_convertible_v<G&&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, std::move(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- !std::is_convertible_v<G&&, E> /* explicit */
- )>
- constexpr explicit expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
-
- template<class... Args _ENABLE_IF(
- std::is_constructible_v<T, Args&&...>
- )>
- constexpr explicit expected(std::in_place_t, Args&&... args)
- : var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<T, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
- : var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
-
- template<class... Args _ENABLE_IF(
- std::is_constructible_v<E, Args...>
- )>
- constexpr explicit expected(unexpect_t, Args&&... args)
- : var_(unexpected_type(std::forward<Args>(args)...)) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<E, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
- : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
-
- // destructor
- ~expected() = default;
-
- // assignment
- // Note: SFNAIE doesn't work here because assignment operator should be
- // non-template. We could workaround this by defining a templated parent class
- // having the assignment operator. This incomplete implementation however
- // doesn't allow us to copy assign expected<T,E> even when T is non-copy
- // assignable. The copy assignment will fail by the underlying std::variant
- // anyway though the error message won't be clear.
- expected& operator=(const expected& rhs) = default;
-
- // Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) noexcept(
- std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
-
- template <class U = T _ENABLE_IF(
- !std::is_void_v<T> &&
- !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
- std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
- std::is_nothrow_move_constructible_v<E>)>
- expected& operator=(U&& rhs) {
- var_ = T(std::forward<U>(rhs));
- return *this;
- }
-
- template<class G = E>
- expected& operator=(const unexpected<G>& rhs) {
- var_ = rhs;
- return *this;
- }
-
- template<class G = E _ENABLE_IF(
- std::is_nothrow_move_constructible_v<G> &&
- std::is_move_assignable_v<G>
- )>
- expected& operator=(unexpected<G>&& rhs) {
- var_ = std::move(rhs);
- return *this;
- }
-
- // modifiers
- template<class... Args _ENABLE_IF(
- std::is_nothrow_constructible_v<T, Args...>
- )>
- T& emplace(Args&&... args) {
- expected(std::in_place, std::forward<Args>(args)...).swap(*this);
- return value();
- }
-
- template<class U, class... Args _ENABLE_IF(
- std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
- )>
- T& emplace(std::initializer_list<U> il, Args&&... args) {
- expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
- return value();
- }
-
- // swap
- template<typename U = T, typename = std::enable_if_t<(
- std::is_swappable_v<U> &&
- std::is_swappable_v<E> &&
- (std::is_move_constructible_v<U> ||
- std::is_move_constructible_v<E>))>>
- void swap(expected& rhs) noexcept(
- std::is_nothrow_move_constructible_v<T> &&
- std::is_nothrow_swappable_v<T> &&
- std::is_nothrow_move_constructible_v<E> &&
- std::is_nothrow_swappable_v<E>) {
- var_.swap(rhs.var_);
- }
-
- // observers
- constexpr const T* operator->() const { return std::addressof(value()); }
- constexpr T* operator->() { return std::addressof(value()); }
- constexpr const T& operator*() const& { return value(); }
- constexpr T& operator*() & { return value(); }
- constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
- constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
-
- constexpr explicit operator bool() const noexcept { return has_value(); }
- constexpr bool has_value() const noexcept { return var_.index() == 0; }
- constexpr bool ok() const noexcept { return has_value(); }
-
- constexpr const T& value() const& { return std::get<T>(var_); }
- constexpr T& value() & { return std::get<T>(var_); }
- constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
- constexpr T&& value() && { return std::move(std::get<T>(var_)); }
-
- constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
- constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
- constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
- constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
-
- template<class U _ENABLE_IF(
- std::is_copy_constructible_v<T> &&
- std::is_convertible_v<U, T>
- )>
- constexpr T value_or(U&& v) const& {
- if (has_value()) return value();
- else return static_cast<T>(std::forward<U>(v));
- }
-
- template<class U _ENABLE_IF(
- std::is_move_constructible_v<T> &&
- std::is_convertible_v<U, T>
- )>
- constexpr T value_or(U&& v) && {
- if (has_value()) return std::move(value());
- else return static_cast<T>(std::forward<U>(v));
- }
-
- // expected equality operators
- template<class T1, class E1, class T2, class E2>
- friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
- template<class T1, class E1, class T2, class E2>
- friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
-
- // Comparison with unexpected<E>
- template<class T1, class E1, class E2>
- friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
- template<class T1, class E1, class E2>
- friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
- template<class T1, class E1, class E2>
- friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
- template<class T1, class E1, class E2>
- friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
-
- // Specialized algorithms
- template<class T1, class E1>
- friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
-
- private:
- std::variant<value_type, unexpected_type> var_;
-};
-
-template<class T1, class E1, class T2, class E2>
-constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) return false;
- if (!x.has_value()) return x.error() == y.error();
- return *x == *y;
-}
-
-template<class T1, class E1, class T2, class E2>
-constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
- return !(x == y);
-}
-
-// Comparison with unexpected<E>
-template<class T1, class E1, class E2>
-constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
- return !x.has_value() && (x.error() == y.value());
-}
-template<class T1, class E1, class E2>
-constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
- return !y.has_value() && (x.value() == y.error());
-}
-template<class T1, class E1, class E2>
-constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
- return x.has_value() || (x.error() != y.value());
-}
-template<class T1, class E1, class E2>
-constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
- return y.has_value() || (x.value() != y.error());
-}
-
-template<class E>
-class _NODISCARD_ expected<void, E> {
- public:
- using value_type = void;
- using error_type = E;
- using unexpected_type = unexpected<E>;
-
- // constructors
- constexpr expected() = default;
- constexpr expected(const expected& rhs) = default;
- constexpr expected(expected&& rhs) noexcept = default;
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- std::is_convertible_v<const G&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const expected<U, G>& rhs) {
- if (!rhs.has_value()) var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- !std::is_convertible_v<const G&, E> /* explicit */
- )>
- constexpr explicit expected(const expected<U, G>& rhs) {
- if (!rhs.has_value()) var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- std::is_convertible_v<const G&&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(expected<U, G>&& rhs) {
- if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- !std::is_convertible_v<const G&&, E> /* explicit */
- )>
- constexpr explicit expected(expected<U, G>&& rhs) {
- if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
- }
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- std::is_convertible_v<const G&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, e.value()) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- !std::is_convertible_v<const G&, E> /* explicit */
- )>
- constexpr explicit expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, E(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- std::is_convertible_v<G&&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, std::move(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- !std::is_convertible_v<G&&, E> /* explicit */
- )>
- constexpr explicit expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
-
- template<class... Args _ENABLE_IF(
- sizeof...(Args) == 0
- )>
- constexpr explicit expected(std::in_place_t, Args&&...) {}
-
- template<class... Args _ENABLE_IF(
- std::is_constructible_v<E, Args...>
- )>
- constexpr explicit expected(unexpect_t, Args&&... args)
- : var_(unexpected_type(std::forward<Args>(args)...)) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<E, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
- : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
-
- // destructor
- ~expected() = default;
-
- // assignment
- // Note: SFNAIE doesn't work here because assignment operator should be
- // non-template. We could workaround this by defining a templated parent class
- // having the assignment operator. This incomplete implementation however
- // doesn't allow us to copy assign expected<T,E> even when T is non-copy
- // assignable. The copy assignment will fail by the underlying std::variant
- // anyway though the error message won't be clear.
- expected& operator=(const expected& rhs) = default;
-
- // Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
-
- template<class G = E>
- expected& operator=(const unexpected<G>& rhs) {
- var_ = rhs;
- return *this;
- }
-
- template<class G = E _ENABLE_IF(
- std::is_nothrow_move_constructible_v<G> &&
- std::is_move_assignable_v<G>
- )>
- expected& operator=(unexpected<G>&& rhs) {
- var_ = std::move(rhs);
- return *this;
- }
-
- // modifiers
- void emplace() {
- var_ = std::monostate();
- }
-
- // swap
- template<typename = std::enable_if_t<
- std::is_swappable_v<E>>
- >
- void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
- var_.swap(rhs.var_);
- }
-
- // observers
- constexpr explicit operator bool() const noexcept { return has_value(); }
- constexpr bool has_value() const noexcept { return var_.index() == 0; }
- constexpr bool ok() const noexcept { return has_value(); }
-
- constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
-
- constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
- constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
- constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
- constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
-
- // expected equality operators
- template<class E1, class E2>
- friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
-
- // Specialized algorithms
- template<class T1, class E1>
- friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
-
- private:
- std::variant<std::monostate, unexpected_type> var_;
-};
-
-template<class E1, class E2>
-constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) return false;
- if (!x.has_value()) return x.error() == y.error();
- return true;
-}
-
-template<class T1, class E1, class E2>
-constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) return false;
- if (!x.has_value()) return x.error() == y.error();
- return false;
-}
-
-template<class E1, class T2, class E2>
-constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) return false;
- if (!x.has_value()) return x.error() == y.error();
- return false;
-}
-
-template<class E>
-class unexpected {
- public:
- // constructors
- constexpr unexpected(const unexpected&) = default;
- constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
-
- template <class Err = E _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
- !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
- // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
- constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<E, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
- : val_(il, std::forward<Args>(args)...) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- std::is_convertible_v<Err, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr unexpected(const unexpected<Err>& rhs)
- : val_(rhs.value()) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- !std::is_convertible_v<Err, E> /* explicit */
- )>
- constexpr explicit unexpected(const unexpected<Err>& rhs)
- : val_(E(rhs.value())) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- std::is_convertible_v<Err, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr unexpected(unexpected<Err>&& rhs)
- : val_(std::move(rhs.value())) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- !std::is_convertible_v<Err, E> /* explicit */
- )>
- constexpr explicit unexpected(unexpected<Err>&& rhs)
- : val_(E(std::move(rhs.value()))) {}
-
- // assignment
- constexpr unexpected& operator=(const unexpected&) = default;
- constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
- default;
- template<class Err = E>
- constexpr unexpected& operator=(const unexpected<Err>& rhs) {
- val_ = rhs.value();
- return *this;
- }
- template<class Err = E>
- constexpr unexpected& operator=(unexpected<Err>&& rhs) {
- val_ = std::forward<E>(rhs.value());
- return *this;
- }
-
- // observer
- constexpr const E& value() const& noexcept { return val_; }
- constexpr E& value() & noexcept { return val_; }
- constexpr const E&& value() const&& noexcept { return std::move(val_); }
- constexpr E&& value() && noexcept { return std::move(val_); }
-
- void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
- std::swap(val_, other.val_);
- }
-
- template<class E1, class E2>
- friend constexpr bool
- operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
- template<class E1, class E2>
- friend constexpr bool
- operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
-
- template<class E1>
- friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
-
- private:
- E val_;
-};
-
-template<class E1, class E2>
-constexpr bool
-operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
- return e1.value() == e2.value();
-}
-
-template<class E1, class E2>
-constexpr bool
-operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
- return e1.value() != e2.value();
-}
-
-template<class E1>
-void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
- x.swap(y);
-}
-
-// TODO: bad_expected_access class
-
-#undef _ENABLE_IF
-#undef _NODISCARD_
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
deleted file mode 100644
index c622562..0000000
--- a/base/include/android-base/file.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <string>
-
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-#include "android-base/unique_fd.h"
-
-#if !defined(_WIN32) && !defined(O_BINARY)
-/** Windows needs O_BINARY, but Unix never mangles line endings. */
-#define O_BINARY 0
-#endif
-
-#if defined(_WIN32) && !defined(O_CLOEXEC)
-/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
-#define O_CLOEXEC O_NOINHERIT
-#endif
-
-class TemporaryFile {
- public:
- TemporaryFile();
- explicit TemporaryFile(const std::string& tmp_dir);
- ~TemporaryFile();
-
- // Release the ownership of fd, caller is reponsible for closing the
- // fd or stream properly.
- int release();
- // Don't remove the temporary file in the destructor.
- void DoNotRemove() { remove_file_ = false; }
-
- int fd;
- char path[1024];
-
- private:
- void init(const std::string& tmp_dir);
-
- bool remove_file_ = true;
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
-};
-
-class TemporaryDir {
- public:
- TemporaryDir();
- ~TemporaryDir();
- // Don't remove the temporary dir in the destructor.
- void DoNotRemove() { remove_dir_and_contents_ = false; }
-
- char path[1024];
-
- private:
- bool init(const std::string& tmp_dir);
-
- bool remove_dir_and_contents_ = true;
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
-};
-
-namespace android {
-namespace base {
-
-bool ReadFdToString(borrowed_fd fd, std::string* content);
-bool ReadFileToString(const std::string& path, std::string* content,
- bool follow_symlinks = false);
-
-bool WriteStringToFile(const std::string& content, const std::string& path,
- bool follow_symlinks = false);
-bool WriteStringToFd(const std::string& content, borrowed_fd fd);
-
-#if !defined(_WIN32)
-bool WriteStringToFile(const std::string& content, const std::string& path,
- mode_t mode, uid_t owner, gid_t group,
- bool follow_symlinks = false);
-#endif
-
-bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
-
-// Reads `byte_count` bytes from the file descriptor at the specified offset.
-// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
-//
-// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
-// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
-// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
-// same function, but concurrently seeking or reading incrementally can lead to unexpected
-// behavior.
-bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
-
-bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
-
-bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
-
-#if !defined(_WIN32)
-bool Realpath(const std::string& path, std::string* result);
-bool Readlink(const std::string& path, std::string* result);
-#endif
-
-std::string GetExecutablePath();
-std::string GetExecutableDirectory();
-
-// Like the regular basename and dirname, but thread-safe on all
-// platforms and capable of correctly handling exotic Windows paths.
-std::string Basename(const std::string& path);
-std::string Dirname(const std::string& path);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
deleted file mode 100644
index 330040d..0000000
--- a/base/include/android-base/format.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
-// It is accessed through its normal fmt:: namespace.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wshadow"
-#include <fmt/chrono.h>
-#pragma clang diagnostic pop
-#include <fmt/core.h>
-#include <fmt/format.h>
-#include <fmt/ostream.h>
-#include <fmt/printf.h>
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
deleted file mode 100644
index 26827fb..0000000
--- a/base/include/android-base/logging.h
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-//
-// Google-style C++ logging.
-//
-
-// This header provides a C++ stream interface to logging.
-//
-// To log:
-//
-// LOG(INFO) << "Some text; " << some_value;
-//
-// Replace `INFO` with any severity from `enum LogSeverity`.
-//
-// To log the result of a failed function and include the string
-// representation of `errno` at the end:
-//
-// PLOG(ERROR) << "Write failed";
-//
-// The output will be something like `Write failed: I/O error`.
-// Remember this as 'P' as in perror(3).
-//
-// To output your own types, simply implement operator<< as normal.
-//
-// By default, output goes to logcat on Android and stderr on the host.
-// A process can use `SetLogger` to decide where all logging goes.
-// Implementations are provided for logcat, stderr, and dmesg.
-//
-// By default, the process' name is used as the log tag.
-// Code can choose a specific log tag by defining LOG_TAG
-// before including this header.
-
-// This header also provides assertions:
-//
-// CHECK(must_be_true);
-// CHECK_EQ(a, b) << z_is_interesting_too;
-
-// NOTE: For Windows, you must include logging.h after windows.h to allow the
-// following code to suppress the evil ERROR macro:
-#ifdef _WIN32
-// windows.h includes wingdi.h which defines an evil macro ERROR.
-#ifdef ERROR
-#undef ERROR
-#endif
-#endif
-
-#include <functional>
-#include <memory>
-#include <ostream>
-
-#include "android-base/errno_restorer.h"
-#include "android-base/macros.h"
-
-// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
-#ifdef _LOG_TAG_INTERNAL
-#error "_LOG_TAG_INTERNAL must not be defined"
-#endif
-#ifdef LOG_TAG
-#define _LOG_TAG_INTERNAL LOG_TAG
-#else
-#define _LOG_TAG_INTERNAL nullptr
-#endif
-
-namespace android {
-namespace base {
-
-enum LogSeverity {
- VERBOSE,
- DEBUG,
- INFO,
- WARNING,
- ERROR,
- FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical to FATAL.
- FATAL,
-};
-
-enum LogId {
- DEFAULT,
- MAIN,
- SYSTEM,
- RADIO,
- CRASH,
-};
-
-using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
- unsigned int, const char*)>;
-using AbortFunction = std::function<void(const char*)>;
-
-// Loggers for use with InitLogging/SetLogger.
-
-// Log to the kernel log (dmesg).
-void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
-// Log to stderr in the full logcat format (with pid/tid/time/tag details).
-void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
-// Log just the message to stdout/stderr (without pid/tid/time/tag details).
-// The choice of stdout versus stderr is based on the severity.
-// Errors are also prefixed by the program name (as with err(3)/error(3)).
-// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
-void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
-
-void DefaultAborter(const char* abort_message);
-
-void SetDefaultTag(const std::string& tag);
-
-// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other
-// threads from writing to logd between sending each chunk, so other threads may interleave their
-// messages. If preventing interleaving is required, then a custom logger that takes a lock before
-// calling this logger should be provided.
-class LogdLogger {
- public:
- explicit LogdLogger(LogId default_log_id = android::base::MAIN);
-
- void operator()(LogId, LogSeverity, const char* tag, const char* file,
- unsigned int line, const char* message);
-
- private:
- LogId default_log_id_;
-};
-
-// Configure logging based on ANDROID_LOG_TAGS environment variable.
-// We need to parse a string that looks like
-//
-// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
-//
-// The tag (or '*' for the global level) comes first, followed by a colon and a
-// letter indicating the minimum priority level we're expected to log. This can
-// be used to reveal or conceal logs with specific tags.
-#ifdef __ANDROID__
-#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
-#else
-#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
-#endif
-void InitLogging(char* argv[],
- LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
- AbortFunction&& aborter = DefaultAborter);
-#undef INIT_LOGGING_DEFAULT_LOGGER
-
-// Replace the current logger.
-void SetLogger(LogFunction&& logger);
-
-// Replace the current aborter.
-void SetAborter(AbortFunction&& aborter);
-
-// A helper macro that produces an expression that accepts both a qualified name and an
-// unqualified name for a LogSeverity, and returns a LogSeverity value.
-// Note: DO NOT USE DIRECTLY. This is an implementation detail.
-#define SEVERITY_LAMBDA(severity) ([&]() { \
- using ::android::base::VERBOSE; \
- using ::android::base::DEBUG; \
- using ::android::base::INFO; \
- using ::android::base::WARNING; \
- using ::android::base::ERROR; \
- using ::android::base::FATAL_WITHOUT_ABORT; \
- using ::android::base::FATAL; \
- return (severity); }())
-
-#ifdef __clang_analyzer__
-// Clang's static analyzer does not see the conditional statement inside
-// LogMessage's destructor that will abort on FATAL severity.
-#define ABORT_AFTER_LOG_FATAL for (;; abort())
-
-struct LogAbortAfterFullExpr {
- ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
- explicit operator bool() const { return false; }
-};
-// Provides an expression that evaluates to the truthiness of `x`, automatically
-// aborting if `c` is true.
-#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
-// Note to the static analyzer that we always execute FATAL logs in practice.
-#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
-#else
-#define ABORT_AFTER_LOG_FATAL
-#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
-#define MUST_LOG_MESSAGE(severity) false
-#endif
-#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
-
-// Defines whether the given severity will be logged or silently swallowed.
-#define WOULD_LOG(severity) \
- (UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \
- MUST_LOG_MESSAGE(severity))
-
-// Get an ostream that can be used for logging at the given severity and to the default
-// destination.
-//
-// Notes:
-// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
-// usage manually.
-// 2) This does not save and restore errno.
-#define LOG_STREAM(severity) \
- ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, \
- -1) \
- .stream()
-
-// Logs a message to logcat on Android otherwise to stderr. If the severity is
-// FATAL it also causes an abort. For example:
-//
-// LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
-
-// Checks if we want to log something, and sets up appropriate RAII objects if
-// so.
-// Note: DO NOT USE DIRECTLY. This is an implementation detail.
-#define LOGGING_PREAMBLE(severity) \
- (WOULD_LOG(severity) && \
- ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
- ::android::base::ErrnoRestorer())
-
-// A variant of LOG that also logs the current errno value. To be used when
-// library calls fail.
-#define PLOG(severity) \
- LOGGING_PREAMBLE(severity) && \
- ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), \
- _LOG_TAG_INTERNAL, errno) \
- .stream()
-
-// Marker that code is yet to be implemented.
-#define UNIMPLEMENTED(level) \
- LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
-
-// Check whether condition x holds and LOG(FATAL) if not. The value of the
-// expression x is only evaluated once. Extra logging can be appended using <<
-// after. For example:
-//
-// CHECK(false == true) results in a log message of
-// "Check failed: false == true".
-#define CHECK(x) \
- LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, \
- -1) \
- .stream() \
- << "Check failed: " #x << " "
-
-// clang-format off
-// Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP) \
- for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
- UNLIKELY(!(_values.lhs OP _values.rhs)); \
- /* empty */) \
- ABORT_AFTER_LOG_FATAL \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
- .stream() \
- << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
- << ", " #RHS "=" << _values.rhs << ") "
-// clang-format on
-
-// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
-// of the expressions x and y is evaluated once. Extra logging can be appended
-// using << after. For example:
-//
-// CHECK_NE(0 == 1, false) results in
-// "Check failed: false != false (0==1=false, false=false) ".
-#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
-#define CHECK_NE(x, y) CHECK_OP(x, y, != )
-#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
-#define CHECK_LT(x, y) CHECK_OP(x, y, < )
-#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
-#define CHECK_GT(x, y) CHECK_OP(x, y, > )
-
-// clang-format off
-// Helper for CHECK_STRxx(s1,s2) macros.
-#define CHECK_STROP(s1, s2, sense) \
- while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
- ABORT_AFTER_LOG_FATAL \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, \
- _LOG_TAG_INTERNAL, -1) \
- .stream() \
- << "Check failed: " << "\"" << (s1) << "\"" \
- << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
-// clang-format on
-
-// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
-#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
-#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
-
-// Perform the pthread function call(args), LOG(FATAL) on error.
-#define CHECK_PTHREAD_CALL(call, args, what) \
- do { \
- int rc = call args; \
- if (rc != 0) { \
- errno = rc; \
- ABORT_AFTER_LOG_FATAL \
- PLOG(FATAL) << #call << " failed for " << (what); \
- } \
- } while (false)
-
-// CHECK that can be used in a constexpr function. For example:
-//
-// constexpr int half(int n) {
-// return
-// DCHECK_CONSTEXPR(n >= 0, , 0)
-// CHECK_CONSTEXPR((n & 1) == 0),
-// << "Extra debugging output: n = " << n, 0)
-// n / 2;
-// }
-#define CHECK_CONSTEXPR(x, out, dummy) \
- (UNLIKELY(!(x))) \
- ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
- :
-
-// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
-// CHECK should be used unless profiling identifies a CHECK as being in
-// performance critical code.
-#if defined(NDEBUG) && !defined(__clang_analyzer__)
-static constexpr bool kEnableDChecks = false;
-#else
-static constexpr bool kEnableDChecks = true;
-#endif
-
-#define DCHECK(x) \
- if (::android::base::kEnableDChecks) CHECK(x)
-#define DCHECK_EQ(x, y) \
- if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
-#define DCHECK_NE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_NE(x, y)
-#define DCHECK_LE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_LE(x, y)
-#define DCHECK_LT(x, y) \
- if (::android::base::kEnableDChecks) CHECK_LT(x, y)
-#define DCHECK_GE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_GE(x, y)
-#define DCHECK_GT(x, y) \
- if (::android::base::kEnableDChecks) CHECK_GT(x, y)
-#define DCHECK_STREQ(s1, s2) \
- if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
-#define DCHECK_STRNE(s1, s2) \
- if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG) && !defined(__clang_analyzer__)
-#define DCHECK_CONSTEXPR(x, out, dummy)
-#else
-#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
-#endif
-
-// Temporary class created to evaluate the LHS and RHS, used with
-// MakeEagerEvaluator to infer the types of LHS and RHS.
-template <typename LHS, typename RHS>
-struct EagerEvaluator {
- constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
- }
- LHS lhs;
- RHS rhs;
-};
-
-// Helper function for CHECK_xx.
-template <typename LHS, typename RHS>
-constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
- return EagerEvaluator<LHS, RHS>(lhs, rhs);
-}
-
-// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
-// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
-// signed/unsigned warnings to protect you against combinations not explicitly
-// listed below.
-#define EAGER_PTR_EVALUATOR(T1, T2) \
- template <> \
- struct EagerEvaluator<T1, T2> { \
- EagerEvaluator(T1 l, T2 r) \
- : lhs(reinterpret_cast<const void*>(l)), \
- rhs(reinterpret_cast<const void*>(r)) { \
- } \
- const void* lhs; \
- const void* rhs; \
- }
-EAGER_PTR_EVALUATOR(const char*, const char*);
-EAGER_PTR_EVALUATOR(const char*, char*);
-EAGER_PTR_EVALUATOR(char*, const char*);
-EAGER_PTR_EVALUATOR(char*, char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(const signed char*, signed char*);
-EAGER_PTR_EVALUATOR(signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(signed char*, signed char*);
-
-// Data for the log message, not stored in LogMessage to avoid increasing the
-// stack size.
-class LogMessageData;
-
-// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
-// of a CHECK. The destructor will abort if the severity is FATAL.
-class LogMessage {
- public:
- // LogId has been deprecated, but this constructor must exist for prebuilts.
- LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, const char* tag,
- int error);
- LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, int error);
-
- ~LogMessage();
-
- // Returns the stream associated with the message, the LogMessage performs
- // output when it goes out of scope.
- std::ostream& stream();
-
- // The routine that performs the actual logging.
- static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- const char* msg);
-
- private:
- const std::unique_ptr<LogMessageData> data_;
-
- DISALLOW_COPY_AND_ASSIGN(LogMessage);
-};
-
-// Get the minimum severity level for logging.
-LogSeverity GetMinimumLogSeverity();
-
-// Set the minimum severity level for logging, returning the old severity.
-LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
-
-// Return whether or not a log message with the associated tag should be logged.
-bool ShouldLog(LogSeverity severity, const char* tag);
-
-// Allows to temporarily change the minimum severity level for logging.
-class ScopedLogSeverity {
- public:
- explicit ScopedLogSeverity(LogSeverity level);
- ~ScopedLogSeverity();
-
- private:
- LogSeverity old_;
-};
-
-} // namespace base
-} // namespace android
-
-namespace std { // NOLINT(cert-dcl58-cpp)
-
-// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
-//
-// Note: for this to work, we need to have this in a namespace.
-// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
-// diagnose_if.
-// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
-// Note: a not-recommended alternative is to let Clang ignore the warning by adding
-// -Wno-user-defined-warnings to CPPFLAGS.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgcc-compat"
-#define OSTREAM_STRING_POINTER_USAGE_WARNING \
- __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
-inline OSTREAM_STRING_POINTER_USAGE_WARNING
-std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) {
- return stream << static_cast<const void*>(string_pointer);
-}
-#pragma clang diagnostic pop
-
-} // namespace std
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
deleted file mode 100644
index 546b2ec..0000000
--- a/base/include/android-base/macros.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stddef.h> // for size_t
-#include <unistd.h> // for TEMP_FAILURE_RETRY
-
-#include <utility>
-
-// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) \
- ({ \
- decltype(exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; \
- })
-#endif
-
-// A macro to disallow the copy constructor and operator= functions
-// This must be placed in the private: declarations for a class.
-//
-// For disallowing only assign or copy, delete the relevant operator or
-// constructor, for example:
-// void operator=(const TypeName&) = delete;
-// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
-// semantically, one should either use disallow both or neither. Try to
-// avoid these in new code.
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&) = delete; \
- void operator=(const TypeName&) = delete
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-//
-// This should be used in the private: declarations for a class
-// that wants to prevent anyone from instantiating it. This is
-// especially useful for classes containing only static methods.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName() = delete; \
- DISALLOW_COPY_AND_ASSIGN(TypeName)
-
-// The arraysize(arr) macro returns the # of elements in an array arr.
-// The expression is a compile-time constant, and therefore can be
-// used in defining new arrays, for example. If you use arraysize on
-// a pointer by mistake, you will get a compile-time error.
-//
-// One caveat is that arraysize() doesn't accept any array of an
-// anonymous type or a type defined inside a function. In these rare
-// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
-// due to a limitation in C++'s template system. The limitation might
-// eventually be removed, but it hasn't happened yet.
-
-// This template function declaration is used in defining arraysize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-template <typename T, size_t N>
-char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
-
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
-#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
-
-// Changing this definition will cause you a lot of pain. A majority of
-// vendor code defines LIKELY and UNLIKELY this way, and includes
-// this header through an indirect path.
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
-#define WARN_UNUSED __attribute__((warn_unused_result))
-
-// A deprecated function to call to create a false use of the parameter, for
-// example:
-// int foo(int x) { UNUSED(x); return 10; }
-// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
-template <typename... T>
-void UNUSED(const T&...) {
-}
-
-// An attribute to place on a parameter to a function, for example:
-// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
-// to avoid compiler warnings.
-#define ATTRIBUTE_UNUSED __attribute__((__unused__))
-
-// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
-// between switch labels:
-// switch (x) {
-// case 40:
-// case 41:
-// if (truth_is_out_there) {
-// ++x;
-// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
-// // comments.
-// } else {
-// return x;
-// }
-// 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.
-//
-// 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.
-//
-// 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
-
-// Current ABI string
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#endif
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
deleted file mode 100644
index 8c37f43..0000000
--- a/base/include/android-base/mapped_file.h
+++ /dev/null
@@ -1,93 +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.
- */
-
-#pragma once
-
-#include <sys/types.h>
-
-#include <memory>
-
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-#include "android-base/unique_fd.h"
-
-#if defined(_WIN32)
-#include <windows.h>
-#define PROT_READ 1
-#define PROT_WRITE 2
-using os_handle = HANDLE;
-#else
-#include <sys/mman.h>
-using os_handle = int;
-#endif
-
-namespace android {
-namespace base {
-
-/**
- * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
- */
-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(borrowed_fd fd, off64_t offset, size_t length,
- int prot);
-
- /**
- * Same thing, but using the raw OS file handle instead of a CRT wrapper.
- */
- static std::unique_ptr<MappedFile> FromOsHandle(os_handle h, off64_t offset, size_t length,
- int prot);
-
- /**
- * Removes the mapping.
- */
- ~MappedFile();
-
- /**
- * Not copyable but movable.
- */
- MappedFile(MappedFile&& other);
- MappedFile& operator=(MappedFile&& other);
-
- char* data() const { return base_ + offset_; }
- size_t size() const { return size_; }
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
-
- void Close();
-
- 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/base/include/android-base/memory.h b/base/include/android-base/memory.h
deleted file mode 100644
index 0277a03..0000000
--- a/base/include/android-base/memory.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-namespace android {
-namespace base {
-
-// Use memcpy for access to unaligned data on targets with alignment
-// restrictions. The compiler will generate appropriate code to access these
-// structures without generating alignment exceptions.
-template <typename T>
-static inline T get_unaligned(const void* address) {
- T result;
- memcpy(&result, address, sizeof(T));
- return result;
-}
-
-template <typename T>
-static inline void put_unaligned(void* address, T v) {
- memcpy(address, &v, sizeof(T));
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/no_destructor.h b/base/include/android-base/no_destructor.h
deleted file mode 100644
index ce0dc9f..0000000
--- a/base/include/android-base/no_destructor.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utility>
-
-#include "android-base/macros.h"
-
-namespace android {
-namespace base {
-
-// A wrapper that makes it easy to create an object of type T with static
-// storage duration that:
-// - is only constructed on first access
-// - never invokes the destructor
-// in order to satisfy the styleguide ban on global constructors and
-// destructors.
-//
-// Runtime constant example:
-// const std::string& GetLineSeparator() {
-// // Forwards to std::string(size_t, char, const Allocator&) constructor.
-// static const base::NoDestructor<std::string> s(5, '-');
-// return *s;
-// }
-//
-// More complex initialization with a lambda:
-// const std::string& GetSessionNonce() {
-// static const base::NoDestructor<std::string> nonce([] {
-// std::string s(16);
-// crypto::RandString(s.data(), s.size());
-// return s;
-// }());
-// return *nonce;
-// }
-//
-// NoDestructor<T> stores the object inline, so it also avoids a pointer
-// indirection and a malloc. Also note that since C++11 static local variable
-// initialization is thread-safe and so is this pattern. Code should prefer to
-// use NoDestructor<T> over:
-// - A function scoped static T* or T& that is dynamically initialized.
-// - A global base::LazyInstance<T>.
-//
-// Note that since the destructor is never run, this *will* leak memory if used
-// as a stack or member variable. Furthermore, a NoDestructor<T> should never
-// have global scope as that may require a static initializer.
-template <typename T>
-class NoDestructor {
- public:
- // Not constexpr; just write static constexpr T x = ...; if the value should
- // be a constexpr.
- template <typename... Args>
- explicit NoDestructor(Args&&... args) {
- new (storage_) T(std::forward<Args>(args)...);
- }
-
- // Allows copy and move construction of the contained type, to allow
- // construction from an initializer list, e.g. for std::vector.
- explicit NoDestructor(const T& x) { new (storage_) T(x); }
- explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
-
- NoDestructor(const NoDestructor&) = delete;
- NoDestructor& operator=(const NoDestructor&) = delete;
-
- ~NoDestructor() = default;
-
- const T& operator*() const { return *get(); }
- T& operator*() { return *get(); }
-
- const T* operator->() const { return get(); }
- T* operator->() { return get(); }
-
- const T* get() const { return reinterpret_cast<const T*>(storage_); }
- T* get() { return reinterpret_cast<T*>(storage_); }
-
- private:
- alignas(T) char storage_[sizeof(T)];
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/off64_t.h b/base/include/android-base/off64_t.h
deleted file mode 100644
index e6b71b8..0000000
--- a/base/include/android-base/off64_t.h
+++ /dev/null
@@ -1,22 +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.
- */
-
-#pragma once
-
-#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/parsebool.h b/base/include/android-base/parsebool.h
deleted file mode 100644
index b2bd021..0000000
--- a/base/include/android-base/parsebool.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string_view>
-
-namespace android {
-namespace base {
-
-// Parse the given string as yes or no inactivation of some sort. Return one of the
-// ParseBoolResult enumeration values.
-//
-// The following values parse as true:
-//
-// 1
-// on
-// true
-// y
-// yes
-//
-//
-// The following values parse as false:
-//
-// 0
-// false
-// n
-// no
-// off
-//
-// Anything else is a parse error.
-//
-// The purpose of this function is to have a single canonical parser for yes-or-no indications
-// throughout the system.
-
-enum class ParseBoolResult {
- kError,
- kFalse,
- kTrue,
-};
-
-ParseBoolResult ParseBool(std::string_view s);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
deleted file mode 100644
index ccffba2..0000000
--- a/base/include/android-base/parsedouble.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 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 <errno.h>
-#include <stdlib.h>
-
-#include <limits>
-#include <string>
-
-namespace android {
-namespace base {
-
-// Parse floating value in the string 's' and sets 'out' to that value if it exists.
-// Optionally allows the caller to define a 'min' and 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success.
-template <typename T, T (*strtox)(const char* str, char** endptr)>
-static inline bool ParseFloatingPoint(const char* s, T* out, T min, T max) {
- errno = 0;
- char* end;
- T result = strtox(s, &end);
- if (errno != 0 || s == end || *end != '\0') {
- return false;
- }
- if (result < min || max < result) {
- return false;
- }
- if (out != nullptr) {
- *out = result;
- }
- return true;
-}
-
-// Parse double value in the string 's' and sets 'out' to that value if it exists.
-// Optionally allows the caller to define a 'min' and 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success.
-static inline bool ParseDouble(const char* s, double* out,
- double min = std::numeric_limits<double>::lowest(),
- double max = std::numeric_limits<double>::max()) {
- return ParseFloatingPoint<double, strtod>(s, out, min, max);
-}
-static inline bool ParseDouble(const std::string& s, double* out,
- double min = std::numeric_limits<double>::lowest(),
- double max = std::numeric_limits<double>::max()) {
- return ParseFloatingPoint<double, strtod>(s.c_str(), out, min, max);
-}
-
-// Parse float value in the string 's' and sets 'out' to that value if it exists.
-// Optionally allows the caller to define a 'min' and 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success.
-static inline bool ParseFloat(const char* s, float* out,
- float min = std::numeric_limits<float>::lowest(),
- float max = std::numeric_limits<float>::max()) {
- return ParseFloatingPoint<float, strtof>(s, out, min, max);
-}
-static inline bool ParseFloat(const std::string& s, float* out,
- float min = std::numeric_limits<float>::lowest(),
- float max = std::numeric_limits<float>::max()) {
- return ParseFloatingPoint<float, strtof>(s.c_str(), out, min, max);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
deleted file mode 100644
index be8b97b..0000000
--- a/base/include/android-base/parseint.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <limits>
-#include <string>
-#include <type_traits>
-
-namespace android {
-namespace base {
-
-// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
-// 'out' to that value if it is specified. Optionally allows the caller to define
-// a 'max' beyond which otherwise valid values will be rejected. Returns boolean
-// success; 'out' is untouched if parsing fails.
-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++;
- }
-
- if (s[0] == '-') {
- errno = EINVAL;
- return false;
- }
-
- int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
- errno = 0;
- char* end;
- unsigned long long int result = strtoull(s, &end, base);
- if (errno != 0) return false;
- if (end == s) {
- errno = EINVAL;
- return false;
- }
- if (*end != '\0') {
- const char* suffixes = "bkmgtpe";
- const char* suffix;
- if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) ||
- __builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {
- errno = EINVAL;
- return false;
- }
- }
- if (max < result) {
- errno = ERANGE;
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<T>(result);
- }
- return true;
-}
-
-// TODO: string_view
-template <typename T>
-bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits<T>::max(),
- bool allow_suffixes = false) {
- return ParseUint(s.c_str(), out, max, allow_suffixes);
-}
-
-template <typename T>
-bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits<T>::max()) {
- return ParseUint(s, out, max, true);
-}
-
-// TODO: string_view
-template <typename T>
-bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits<T>::max()) {
- return ParseByteCount(s.c_str(), out, max);
-}
-
-// Parses the signed decimal or hexadecimal integer in the string 's' and sets
-// 'out' to that value if it is specified. Optionally allows the caller to define
-// a 'min' and 'max' beyond which otherwise valid values will be rejected. Returns
-// boolean success; 'out' is untouched if parsing fails.
-template <typename T>
-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++;
- }
-
- int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
- errno = 0;
- char* end;
- long long int result = strtoll(s, &end, base);
- if (errno != 0) {
- return false;
- }
- if (s == end || *end != '\0') {
- errno = EINVAL;
- return false;
- }
- if (result < min || max < result) {
- errno = ERANGE;
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<T>(result);
- }
- return true;
-}
-
-// TODO: string_view
-template <typename T>
-bool ParseInt(const std::string& s, T* out,
- T min = std::numeric_limits<T>::min(),
- T max = std::numeric_limits<T>::max()) {
- return ParseInt(s.c_str(), out, min, max);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
deleted file mode 100644
index 47f8b5f..0000000
--- a/base/include/android-base/parsenetaddress.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-namespace android {
-namespace base {
-
-// Parses |address| into |host| and |port|.
-//
-// If |address| doesn't contain a port number, the default value is taken from
-// |port|. If |canonical_address| is non-null it will be set to "host:port" or
-// "[host]:port" as appropriate.
-//
-// On failure, returns false and fills |error|.
-bool ParseNetAddress(const std::string& address, std::string* host, int* port,
- std::string* canonical_address, std::string* error);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/process.h b/base/include/android-base/process.h
deleted file mode 100644
index 69ed3fb..0000000
--- a/base/include/android-base/process.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <dirent.h>
-#include <sys/types.h>
-
-#include <iterator>
-#include <memory>
-#include <vector>
-
-namespace android {
-namespace base {
-
-class AllPids {
- class PidIterator {
- public:
- PidIterator(DIR* dir) : dir_(dir, closedir) { Increment(); }
- PidIterator& operator++() {
- Increment();
- return *this;
- }
- bool operator==(const PidIterator& other) const { return pid_ == other.pid_; }
- bool operator!=(const PidIterator& other) const { return !(*this == other); }
- long operator*() const { return pid_; }
- // iterator traits
- using difference_type = pid_t;
- using value_type = pid_t;
- using pointer = const pid_t*;
- using reference = const pid_t&;
- using iterator_category = std::input_iterator_tag;
-
- private:
- void Increment();
-
- pid_t pid_ = -1;
- std::unique_ptr<DIR, decltype(&closedir)> dir_;
- };
-
- public:
- PidIterator begin() { return opendir("/proc"); }
- PidIterator end() { return nullptr; }
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
deleted file mode 100644
index 49f1f31..0000000
--- a/base/include/android-base/properties.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/cdefs.h>
-
-#include <chrono>
-#include <limits>
-#include <optional>
-#include <string>
-
-struct prop_info;
-
-namespace android {
-namespace base {
-
-// Returns the current value of the system property `key`,
-// or `default_value` if the property is empty or doesn't exist.
-std::string GetProperty(const std::string& key, const std::string& default_value);
-
-// Returns true if the system property `key` has the value "1", "y", "yes", "on", or "true",
-// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
-bool GetBoolProperty(const std::string& key, bool default_value);
-
-// Returns the signed integer corresponding to the system property `key`.
-// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
-// the optional bounds, returns `default_value`.
-template <typename T> T GetIntProperty(const std::string& key,
- T default_value,
- T min = std::numeric_limits<T>::min(),
- T max = std::numeric_limits<T>::max());
-
-// Returns the unsigned integer corresponding to the system property `key`.
-// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
-// the optional bound, returns `default_value`.
-template <typename T> T GetUintProperty(const std::string& key,
- T default_value,
- T max = std::numeric_limits<T>::max());
-
-// Sets the system property `key` to `value`.
-bool SetProperty(const std::string& key, const std::string& value);
-
-// Waits for the system property `key` to have the value `expected_value`.
-// Times out after `relative_timeout`.
-// Returns true on success, false on timeout.
-#if defined(__BIONIC__)
-bool WaitForProperty(const std::string& key, const std::string& expected_value,
- std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
-#endif
-
-// Waits for the system property `key` to be created.
-// Times out after `relative_timeout`.
-// Returns true on success, false on timeout.
-#if defined(__BIONIC__)
-bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
- std::chrono::milliseconds::max());
-#endif
-
-#if defined(__BIONIC__) && __cplusplus >= 201703L
-// Cached system property lookup. For code that needs to read the same property multiple times,
-// this class helps optimize those lookups.
-class CachedProperty {
- public:
- explicit CachedProperty(const char* property_name);
-
- // Returns the current value of the underlying system property as cheaply as possible.
- // The returned pointer is valid until the next call to Get. Because most callers are going
- // to want to parse the string returned here and cached that as well, this function performs
- // no locking, and is completely thread unsafe. It is the caller's responsibility to provide a
- // lock for thread-safety.
- //
- // Note: *changed can be set to true even if the contents of the property remain the same.
- const char* Get(bool* changed = nullptr);
-
- private:
- std::string property_name_;
- const prop_info* prop_info_;
- std::optional<uint32_t> cached_area_serial_;
- std::optional<uint32_t> cached_property_serial_;
- char cached_value_[92];
- bool is_read_only_;
- const char* read_only_property_;
-};
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
deleted file mode 100644
index 56a4f3e..0000000
--- a/base/include/android-base/result.h
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// This file contains classes for returning a successful result along with an optional
-// arbitrarily typed return value or for returning a failure result along with an optional string
-// indicating why the function failed.
-
-// There are 3 classes that implement this functionality and one additional helper type.
-//
-// Result<T> either contains a member of type T that can be accessed using similar semantics as
-// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
-// Result<T>::error().
-//
-// ResultError is a type that contains both a std::string describing the error and a copy of errno
-// from when the error occurred. ResultError can be used in an ostream directly to print its
-// string value.
-//
-// Result<void> is the correct return type for a function that either returns successfully or
-// returns an error value. Returning {} from a function that returns Result<void> is the
-// correct way to indicate that a function without a return type has completed successfully.
-//
-// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
-// to T or from the constructor arguments for T. This allows you to return a type T directly from
-// a function that returns Result<T>.
-//
-// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
-// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
-// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
-// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
-// value can be directly specified via the Error() constructor.
-//
-// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
-// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
-//
-// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
-// In this case, the string that the ResultError takes is passed through the stream normally, but
-// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
-// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
-// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
-// used, the errno of the last one is respected.
-//
-// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
-// function that return Result<T> but you have a Result<U> and want to return its error. In this
-// case, you can return the .error() from the Result<U> to construct the Result<T>.
-
-// An example of how to use these is below:
-// Result<U> CalculateResult(const T& input) {
-// U output;
-// if (!SomeOtherCppFunction(input, &output)) {
-// return Errorf("SomeOtherCppFunction {} failed", input);
-// }
-// if (!c_api_function(output)) {
-// return ErrnoErrorf("c_api_function {} failed", output);
-// }
-// return output;
-// }
-//
-// auto output = CalculateResult(input);
-// if (!output) return Error() << "CalculateResult failed: " << output.error();
-// UseOutput(*output);
-
-#pragma once
-
-#include <errno.h>
-
-#include <sstream>
-#include <string>
-
-#include "android-base/expected.h"
-#include "android-base/format.h"
-
-namespace android {
-namespace base {
-
-struct ResultError {
- template <typename T>
- ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
-
- template <typename T>
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator android::base::expected<T, ResultError>() {
- return android::base::unexpected(ResultError(message_, code_));
- }
-
- std::string message() const { return message_; }
- int code() const { return code_; }
-
- private:
- std::string message_;
- int code_;
-};
-
-inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
- return lhs.message() == rhs.message() && lhs.code() == rhs.code();
-}
-
-inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
- return !(lhs == rhs);
-}
-
-inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
- os << t.message();
- return os;
-}
-
-class Error {
- public:
- Error() : errno_(0), append_errno_(false) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
-
- template <typename T>
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator android::base::expected<T, ResultError>() {
- return android::base::unexpected(ResultError(str(), errno_));
- }
-
- template <typename T>
- Error& operator<<(T&& t) {
- // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
- if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
- errno_ = t.code();
- return (*this) << t.message();
- }
- int saved = errno;
- ss_ << t;
- errno = saved;
- return *this;
- }
-
- const std::string str() const {
- std::string str = ss_.str();
- if (append_errno_) {
- if (str.empty()) {
- return strerror(errno_);
- }
- return std::move(str) + ": " + strerror(errno_);
- }
- return str;
- }
-
- Error(const Error&) = delete;
- Error(Error&&) = delete;
- Error& operator=(const Error&) = delete;
- Error& operator=(Error&&) = delete;
-
- template <typename T, typename... Args>
- friend Error ErrorfImpl(const T&& fmt, const Args&... args);
-
- template <typename T, typename... Args>
- friend Error ErrnoErrorfImpl(const T&& fmt, const Args&... args);
-
- private:
- Error(bool append_errno, int errno_to_append, const std::string& message)
- : errno_(errno_to_append), append_errno_(append_errno) {
- (*this) << message;
- }
-
- std::stringstream ss_;
- int errno_;
- const bool append_errno_;
-};
-
-inline Error ErrnoError() {
- return Error(errno);
-}
-
-inline int ErrorCode(int code) {
- return code;
-}
-
-// Return the error code of the last ResultError object, if any.
-// Otherwise, return `code` as it is.
-template <typename T, typename... Args>
-inline int ErrorCode(int code, T&& t, const Args&... args) {
- if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
- return ErrorCode(t.code(), args...);
- }
- return ErrorCode(code, args...);
-}
-
-template <typename T, typename... Args>
-inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
- return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
-}
-
-template <typename T, typename... Args>
-inline Error ErrnoErrorfImpl(const T&& fmt, const Args&... args) {
- return Error(true, errno, fmt::format(fmt, args...));
-}
-
-#define Errorf(fmt, ...) android::base::ErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
-#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
-
-template <typename T>
-using Result = android::base::expected<T, ResultError>;
-
-// Macros for testing the results of functions that return android::base::Result.
-// These also work with base::android::expected.
-
-#define CHECK_RESULT_OK(stmt) \
- do { \
- const auto& tmp = (stmt); \
- CHECK(tmp.ok()) << tmp.error(); \
- } while (0)
-
-#define ASSERT_RESULT_OK(stmt) \
- do { \
- const auto& tmp = (stmt); \
- ASSERT_TRUE(tmp.ok()) << tmp.error(); \
- } while (0)
-
-#define EXPECT_RESULT_OK(stmt) \
- do { \
- auto tmp = (stmt); \
- EXPECT_TRUE(tmp.ok()) << tmp.error(); \
- } while (0)
-
-// TODO: Maybe add RETURN_IF_ERROR() and ASSIGN_OR_RETURN()
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
deleted file mode 100644
index 5a224d6..0000000
--- a/base/include/android-base/scopeguard.h
+++ /dev/null
@@ -1,67 +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.
- */
-
-#pragma once
-
-#include <utility> // for std::move, std::forward
-
-namespace android {
-namespace base {
-
-// ScopeGuard ensures that the specified functor is executed no matter how the
-// current scope exits.
-template <typename F>
-class ScopeGuard {
- public:
- ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
-
- ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
- that.active_ = false;
- }
-
- template <typename Functor>
- ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
- that.active_ = false;
- }
-
- ~ScopeGuard() {
- if (active_) f_();
- }
-
- ScopeGuard() = delete;
- ScopeGuard(const ScopeGuard&) = delete;
- void operator=(const ScopeGuard&) = delete;
- void operator=(ScopeGuard&& that) = delete;
-
- void Disable() { active_ = false; }
-
- bool active() const { return active_; }
-
- private:
- template <typename Functor>
- friend class ScopeGuard;
-
- F f_;
- bool active_;
-};
-
-template <typename F>
-ScopeGuard<F> make_scope_guard(F&& f) {
- return ScopeGuard<F>(std::forward<F>(f));
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
deleted file mode 100644
index 93c56af..0000000
--- a/base/include/android-base/stringprintf.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2011 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 <stdarg.h>
-#include <string>
-
-namespace android {
-namespace base {
-
-// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking.
-
-// Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendF(std::string* dst, const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap)
- __attribute__((__format__(__printf__, 2, 0)));
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
deleted file mode 100644
index 14d534a..0000000
--- a/base/include/android-base/strings.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sstream>
-#include <string>
-#include <string_view>
-#include <vector>
-
-namespace android {
-namespace base {
-
-// Splits a string into a vector of strings.
-//
-// The string is split at each occurrence of a character in delimiters.
-//
-// The empty string is not a valid delimiter list.
-std::vector<std::string> Split(const std::string& s,
- const std::string& delimiters);
-
-// Trims whitespace off both ends of the given string.
-std::string Trim(const std::string& s);
-
-// Joins a container of things into a single string, using the given separator.
-template <typename ContainerT, typename SeparatorT>
-std::string Join(const ContainerT& things, SeparatorT separator) {
- if (things.empty()) {
- return "";
- }
-
- std::ostringstream result;
- result << *things.begin();
- for (auto it = std::next(things.begin()); it != things.end(); ++it) {
- result << separator << *it;
- }
- return result.str();
-}
-
-// We instantiate the common cases in strings.cpp.
-extern template std::string Join(const std::vector<std::string>&, char);
-extern template std::string Join(const std::vector<const char*>&, char);
-extern template std::string Join(const std::vector<std::string>&, const std::string&);
-extern template std::string Join(const std::vector<const char*>&, const std::string&);
-
-// Tests whether 's' starts with 'prefix'.
-bool StartsWith(std::string_view s, std::string_view prefix);
-bool StartsWith(std::string_view s, char prefix);
-bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
-
-// Tests whether 's' ends with 'suffix'.
-bool EndsWith(std::string_view s, std::string_view suffix);
-bool EndsWith(std::string_view s, char suffix);
-bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
-
-// Tests whether 'lhs' equals 'rhs', ignoring case.
-bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
-
-// Removes `prefix` from the start of the given string and returns true (if
-// it was present), false otherwise.
-inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
- if (!StartsWith(*s, prefix)) return false;
- s->remove_prefix(prefix.size());
- return true;
-}
-
-// Removes `suffix` from the end of the given string and returns true (if
-// it was present), false otherwise.
-inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
- if (!EndsWith(*s, suffix)) return false;
- s->remove_suffix(suffix.size());
- return true;
-}
-
-// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
-// there are matches if `all == true`.
-[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
- std::string_view to, bool all);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
deleted file mode 100644
index f3d7cb0..0000000
--- a/base/include/android-base/test_utils.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <regex>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-
-class CapturedStdFd {
- public:
- CapturedStdFd(int std_fd);
- ~CapturedStdFd();
-
- std::string str();
-
- void Start();
- void Stop();
- void Reset();
-
- private:
- int fd() const;
-
- TemporaryFile temp_file_;
- int std_fd_;
- int old_fd_ = -1;
-
- DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
-};
-
-class CapturedStderr : public CapturedStdFd {
- public:
- CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
-};
-
-class CapturedStdout : public CapturedStdFd {
- public:
- CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
-};
-
-#define ASSERT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (!std::regex_search(__s, std::regex((pattern)))) { \
- FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
-
-#define ASSERT_NOT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (std::regex_search(__s, std::regex((pattern)))) { \
- FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
-
-#define EXPECT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (!std::regex_search(__s, std::regex((pattern)))) { \
- ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
-
-#define EXPECT_NOT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (std::regex_search(__s, std::regex((pattern)))) { \
- ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
deleted file mode 100644
index 53fe6da..0000000
--- a/base/include/android-base/thread_annotations.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2016 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 <mutex>
-
-#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
-
-#define CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
-
-#define SCOPED_CAPABILITY \
- THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
-
-#define SHARED_CAPABILITY(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__))
-
-#define GUARDED_BY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
-
-#define PT_GUARDED_BY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
-
-#define EXCLUSIVE_LOCKS_REQUIRED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
-
-#define SHARED_LOCKS_REQUIRED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
-
-#define ACQUIRED_BEFORE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
-
-#define ACQUIRED_AFTER(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
-
-#define REQUIRES(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
-
-#define REQUIRES_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
-
-#define ACQUIRE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
-
-#define ACQUIRE_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
-
-#define RELEASE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
-
-#define RELEASE_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
-
-#define TRY_ACQUIRE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
-
-#define TRY_ACQUIRE_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
-
-#define EXCLUDES(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
-
-#define ASSERT_CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
-
-#define ASSERT_SHARED_CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
-
-#define RETURN_CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
-
-#define EXCLUSIVE_LOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
-
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
-
-#define SHARED_LOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
-
-#define SHARED_TRYLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
-
-#define UNLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
-
-#define SCOPED_LOCKABLE \
- THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
-
-#define LOCK_RETURNED(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
-
-#define NO_THREAD_SAFETY_ANALYSIS \
- THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-namespace android {
-namespace base {
-
-// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
-//
-// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
-// like std::unique_lock can't be marked with thread safety annotations. This helper allows
-// for manual assertion of lock state in a scope.
-//
-// For example:
-//
-// std::mutex mutex;
-// std::condition_variable cv;
-// std::vector<int> vec GUARDED_BY(mutex);
-//
-// int pop() {
-// std::unique_lock lock(mutex);
-// ScopedLockAssertion lock_assertion(mutex);
-// cv.wait(lock, []() {
-// ScopedLockAssertion lock_assertion(mutex);
-// return !vec.empty();
-// });
-//
-// int result = vec.back();
-// vec.pop_back();
-// return result;
-// }
-class SCOPED_CAPABILITY ScopedLockAssertion {
- public:
- ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
- ~ScopedLockAssertion() RELEASE() {}
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
deleted file mode 100644
index dba1fc6..0000000
--- a/base/include/android-base/threads.h
+++ /dev/null
@@ -1,30 +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.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-namespace android {
-namespace base {
-uint64_t GetThreadId();
-}
-} // namespace android
-
-#if defined(__GLIBC__)
-// bionic has this Linux-specifix call, but glibc doesn't.
-extern "C" int tgkill(int tgid, int tid, int sig);
-#endif
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
deleted file mode 100644
index 9ceb5db..0000000
--- a/base/include/android-base/unique_fd.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#endif
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-// DO NOT INCLUDE OTHER LIBBASE HEADERS!
-// This file gets used in libbinder, and libbinder is used everywhere.
-// Including other headers from libbase frequently results in inclusion of
-// android-base/macros.h, which causes macro collisions.
-
-// Container for a file descriptor that automatically closes the descriptor as
-// it goes out of scope.
-//
-// unique_fd ufd(open("/some/path", "r"));
-// if (ufd.get() == -1) return error;
-//
-// // Do something useful, possibly including 'return'.
-//
-// return 0; // Descriptor is closed for you.
-//
-// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
-// you find this class if you're searching for one of those names.
-
-#if defined(__BIONIC__)
-#include <android/fdsan.h>
-#endif
-
-namespace android {
-namespace base {
-
-struct DefaultCloser {
-#if defined(__BIONIC__)
- static void Tag(int fd, void* old_addr, void* new_addr) {
- if (android_fdsan_exchange_owner_tag) {
- uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(old_addr));
- uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(new_addr));
- android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
- }
- }
- static void Close(int fd, void* addr) {
- if (android_fdsan_close_with_tag) {
- uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(addr));
- android_fdsan_close_with_tag(fd, tag);
- } else {
- close(fd);
- }
- }
-#else
- static void Close(int fd) {
- // Even if close(2) fails with EINTR, the fd will have been closed.
- // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
- // else's fd.
- // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
- ::close(fd);
- }
-#endif
-};
-
-template <typename Closer>
-class unique_fd_impl final {
- public:
- unique_fd_impl() {}
-
- explicit unique_fd_impl(int fd) { reset(fd); }
- ~unique_fd_impl() { reset(); }
-
- unique_fd_impl(const unique_fd_impl&) = delete;
- void operator=(const unique_fd_impl&) = delete;
- 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);
- return *this;
- }
-
- [[clang::reinitializes]] void reset(int new_value = -1) { reset(new_value, nullptr); }
-
- int get() const { return fd_; }
-
-#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
- // unique_fd's operator int is dangerous, but we have way too much code that
- // depends on it, so make this opt-in at first.
- operator int() const { return get(); } // NOLINT
-#endif
-
- bool operator>=(int rhs) const { return get() >= rhs; }
- bool operator<(int rhs) const { return get() < rhs; }
- bool operator==(int rhs) const { return get() == rhs; }
- bool operator!=(int rhs) const { return get() != rhs; }
- bool operator==(const unique_fd_impl& rhs) const { return get() == rhs.get(); }
- bool operator!=(const unique_fd_impl& rhs) const { return get() != rhs.get(); }
-
- // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
- bool operator!() const = delete;
-
- bool ok() const { return get() >= 0; }
-
- int release() __attribute__((warn_unused_result)) {
- tag(fd_, this, nullptr);
- int ret = fd_;
- fd_ = -1;
- return ret;
- }
-
- private:
- void reset(int new_value, void* previous_tag) {
- int previous_errno = errno;
-
- if (fd_ != -1) {
- close(fd_, this);
- }
-
- fd_ = new_value;
- if (new_value != -1) {
- tag(new_value, previous_tag, this);
- }
-
- errno = previous_errno;
- }
-
- int fd_ = -1;
-
- // Template magic to use Closer::Tag if available, and do nothing if not.
- // If Closer::Tag exists, this implementation is preferred, because int is a better match.
- // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
- template <typename T = Closer>
- static auto tag(int fd, void* old_tag, void* new_tag)
- -> decltype(T::Tag(fd, old_tag, new_tag), void()) {
- T::Tag(fd, old_tag, new_tag);
- }
-
- template <typename T = Closer>
- static void tag(long, void*, void*) {
- // No-op.
- }
-
- // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
- template <typename T = Closer>
- static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
- T::Close(fd, tag_value);
- }
-
- template <typename T = Closer>
- static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
- T::Close(fd);
- }
-};
-
-using unique_fd = unique_fd_impl<DefaultCloser>;
-
-#if !defined(_WIN32)
-
-// Inline functions, so that they can be used header-only.
-template <typename Closer>
-inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
- int flags = O_CLOEXEC) {
- int pipefd[2];
-
-#if defined(__linux__)
- if (pipe2(pipefd, flags) != 0) {
- return false;
- }
-#else // defined(__APPLE__)
- if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
- return false;
- }
- if (pipe(pipefd) != 0) {
- return false;
- }
-
- if (flags & O_CLOEXEC) {
- if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
- close(pipefd[0]);
- close(pipefd[1]);
- return false;
- }
- }
- if (flags & O_NONBLOCK) {
- if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
- close(pipefd[0]);
- close(pipefd[1]);
- return false;
- }
- }
-#endif
-
- read->reset(pipefd[0]);
- write->reset(pipefd[1]);
- return true;
-}
-
-template <typename Closer>
-inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
- unique_fd_impl<Closer>* right) {
- int sockfd[2];
- if (socketpair(domain, type, protocol, sockfd) != 0) {
- return false;
- }
- left->reset(sockfd[0]);
- right->reset(sockfd[1]);
- return true;
-}
-
-template <typename Closer>
-inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
- return Socketpair(AF_UNIX, type, 0, left, right);
-}
-
-// Using fdopen with unique_fd correctly is more annoying than it should be,
-// because fdopen doesn't close the file descriptor received upon failure.
-inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
- int fd = ufd.release();
- FILE* file = fdopen(fd, mode);
- if (!file) {
- close(fd);
- }
- return file;
-}
-
-// Using fdopendir with unique_fd correctly is more annoying than it should be,
-// because fdopen doesn't close the file descriptor received upon failure.
-inline DIR* Fdopendir(unique_fd&& ufd) {
- int fd = ufd.release();
- DIR* dir = fdopendir(fd);
- if (dir == nullptr) {
- close(fd);
- }
- return dir;
-}
-
-#endif // !defined(_WIN32)
-
-// A wrapper type that can be implicitly constructed from either int or unique_fd.
-struct borrowed_fd {
- /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
- template <typename T>
- /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {} // NOLINT
-
- int get() const { return fd_; }
-
- bool operator>=(int rhs) const { return get() >= rhs; }
- bool operator<(int rhs) const { return get() < rhs; }
- bool operator==(int rhs) const { return get() == rhs; }
- bool operator!=(int rhs) const { return get() != rhs; }
-
- private:
- int fd_ = -1;
-};
-} // namespace base
-} // namespace android
-
-template <typename T>
-int close(const android::base::unique_fd_impl<T>&)
- __attribute__((__unavailable__("close called on unique_fd")));
-
-template <typename T>
-FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
- __attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
- "unique_fd, or use android::base::Fdopen to pass ownership")));
-
-template <typename T>
-DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
- __unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
- "unique_fd, or use android::base::Fdopendir to pass ownership")));
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
deleted file mode 100644
index 1a414ec..0000000
--- a/base/include/android-base/utf8.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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.
-#include <fcntl.h> // open
-#include <stdio.h> // fopen
-#include <sys/stat.h> // mkdir
-#include <unistd.h> // unlink
-#endif
-
-namespace android {
-namespace base {
-
-// Only available on Windows because this is only needed on Windows.
-#ifdef _WIN32
-// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
-// conversion was done successfully.
-bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
-
-// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
-// whether the conversion was done successfully.
-bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
-
-// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
-// UTF-8. Returns whether the conversion was done successfully.
-bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
-
-// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
-// was done successfully.
-bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
-
-// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
-// whether the conversion was done successfully.
-bool UTF8ToWide(const char* utf8, std::wstring* utf16);
-
-// Convert a UTF-8 std::string (including any embedded NULL characters) to
-// UTF-16. Returns whether the conversion was done successfully.
-bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
-
-// Convert a file system path, represented as a NULL-terminated string of
-// UTF-8 characters, to a UTF-16 string representing the same file system
-// path using the Windows extended-lengh path representation.
-//
-// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
-// ```The Windows API has many functions that also have Unicode versions to
-// permit an extended-length path for a maximum total path length of 32,767
-// characters. To specify an extended-length path, use the "\\?\" prefix.
-// For example, "\\?\D:\very long path".```
-//
-// Returns whether the conversion was done successfully.
-bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
-#endif
-
-// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
-// are wrappers, for non-Windows these just expose existing APIs. To call these
-// functions, use:
-//
-// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
-// namespace {
-// // Import functions into anonymous namespace.
-// using namespace android::base::utf8;
-//
-// void SomeFunction(const char* name) {
-// int fd = open(name, ...); // Calls android::base::utf8::open().
-// ...
-// unlink(name); // Calls android::base::utf8::unlink().
-// }
-// }
-namespace utf8 {
-
-#ifdef _WIN32
-FILE* fopen(const char* name, const char* mode);
-int mkdir(const char* name, mode_t mode);
-int open(const char* name, int flags, ...);
-int unlink(const char* name);
-#else
-using ::fopen;
-using ::mkdir;
-using ::open;
-using ::unlink;
-#endif
-
-} // namespace utf8
-} // namespace base
-} // namespace android
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
deleted file mode 100644
index 1f4b69b..0000000
--- a/base/liblog_symbols.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2020 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 "liblog_symbols.h"
-
-#if defined(__ANDROID_SDK_VERSION__) && (__ANDROID_SDK_VERSION__ <= 29)
-#define USE_DLSYM
-#endif
-
-#ifdef USE_DLSYM
-#include <dlfcn.h>
-#endif
-
-namespace android {
-namespace base {
-
-#ifdef USE_DLSYM
-
-const std::optional<LibLogFunctions>& GetLibLogFunctions() {
- static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
- void* liblog_handle = dlopen("liblog.so", RTLD_NOW);
- if (liblog_handle == nullptr) {
- return {};
- }
-
- LibLogFunctions real_liblog_functions = {};
-
-#define DLSYM(name) \
- real_liblog_functions.name = \
- reinterpret_cast<decltype(LibLogFunctions::name)>(dlsym(liblog_handle, #name)); \
- if (real_liblog_functions.name == nullptr) { \
- return {}; \
- }
-
- DLSYM(__android_log_set_logger)
- DLSYM(__android_log_write_log_message)
- DLSYM(__android_log_logd_logger)
- DLSYM(__android_log_stderr_logger)
- DLSYM(__android_log_set_aborter)
- DLSYM(__android_log_call_aborter)
- DLSYM(__android_log_default_aborter)
- DLSYM(__android_log_set_minimum_priority);
- DLSYM(__android_log_get_minimum_priority);
- DLSYM(__android_log_set_default_tag);
-#undef DLSYM
-
- return real_liblog_functions;
- }();
-
- return liblog_functions;
-}
-
-#else
-
-const std::optional<LibLogFunctions>& GetLibLogFunctions() {
- static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
- return LibLogFunctions{
- .__android_log_set_logger = __android_log_set_logger,
- .__android_log_write_log_message = __android_log_write_log_message,
- .__android_log_logd_logger = __android_log_logd_logger,
- .__android_log_stderr_logger = __android_log_stderr_logger,
- .__android_log_set_aborter = __android_log_set_aborter,
- .__android_log_call_aborter = __android_log_call_aborter,
- .__android_log_default_aborter = __android_log_default_aborter,
- .__android_log_set_minimum_priority = __android_log_set_minimum_priority,
- .__android_log_get_minimum_priority = __android_log_get_minimum_priority,
- .__android_log_set_default_tag = __android_log_set_default_tag,
- };
- }();
- return liblog_functions;
-}
-
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h
deleted file mode 100644
index 2e6b47f..0000000
--- a/base/liblog_symbols.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 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 <optional>
-
-#include <android/log.h>
-
-namespace android {
-namespace base {
-
-struct LibLogFunctions {
- void (*__android_log_set_logger)(__android_logger_function logger);
- void (*__android_log_write_log_message)(struct __android_log_message* log_message);
-
- void (*__android_log_logd_logger)(const struct __android_log_message* log_message);
- void (*__android_log_stderr_logger)(const struct __android_log_message* log_message);
-
- void (*__android_log_set_aborter)(__android_aborter_function aborter);
- void (*__android_log_call_aborter)(const char* abort_message);
- void (*__android_log_default_aborter)(const char* abort_message);
- int32_t (*__android_log_set_minimum_priority)(int32_t priority);
- int32_t (*__android_log_get_minimum_priority)();
- void (*__android_log_set_default_tag)(const char* tag);
-};
-
-const std::optional<LibLogFunctions>& GetLibLogFunctions();
-
-} // namespace base
-} // namespace android
diff --git a/base/logging.cpp b/base/logging.cpp
deleted file mode 100644
index 5bd21da..0000000
--- a/base/logging.cpp
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(_WIN32)
-#include <windows.h>
-#endif
-
-#include "android-base/logging.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <time.h>
-
-// For getprogname(3) or program_invocation_short_name.
-#if defined(__ANDROID__) || defined(__APPLE__)
-#include <stdlib.h>
-#elif defined(__GLIBC__)
-#include <errno.h>
-#endif
-
-#if defined(__linux__)
-#include <sys/uio.h>
-#endif
-
-#include <atomic>
-#include <iostream>
-#include <limits>
-#include <mutex>
-#include <optional>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android/log.h>
-#ifdef __ANDROID__
-#include <android/set_abort_message.h>
-#else
-#include <sys/types.h>
-#include <unistd.h>
-#endif
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <android-base/threads.h>
-
-#include "liblog_symbols.h"
-#include "logging_splitters.h"
-
-namespace android {
-namespace base {
-
-// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
-#if defined(__GLIBC__) || defined(_WIN32)
-static const char* getprogname() {
-#if defined(__GLIBC__)
- return program_invocation_short_name;
-#elif defined(_WIN32)
- static bool first = true;
- static char progname[MAX_PATH] = {};
-
- if (first) {
- snprintf(progname, sizeof(progname), "%s",
- android::base::Basename(android::base::GetExecutablePath()).c_str());
- first = false;
- }
-
- return progname;
-#endif
-}
-#endif
-
-static const char* GetFileBasename(const char* file) {
- // We can't use basename(3) even on Unix because the Mac doesn't
- // have a non-modifying basename.
- const char* last_slash = strrchr(file, '/');
- if (last_slash != nullptr) {
- return last_slash + 1;
- }
-#if defined(_WIN32)
- const char* last_backslash = strrchr(file, '\\');
- if (last_backslash != nullptr) {
- return last_backslash + 1;
- }
-#endif
- return file;
-}
-
-#if defined(__linux__)
-static int OpenKmsg() {
-#if defined(__ANDROID__)
- // pick up 'file w /dev/kmsg' environment from daemon's init rc file
- const auto val = getenv("ANDROID_FILE__dev_kmsg");
- if (val != nullptr) {
- int fd;
- if (android::base::ParseInt(val, &fd, 0)) {
- auto flags = fcntl(fd, F_GETFL);
- if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd;
- }
- }
-#endif
- return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
-}
-#endif
-
-static LogId log_id_tToLogId(int32_t buffer_id) {
- switch (buffer_id) {
- case LOG_ID_MAIN:
- return MAIN;
- case LOG_ID_SYSTEM:
- return SYSTEM;
- case LOG_ID_RADIO:
- return RADIO;
- case LOG_ID_CRASH:
- return CRASH;
- case LOG_ID_DEFAULT:
- default:
- return DEFAULT;
- }
-}
-
-static int32_t LogIdTolog_id_t(LogId log_id) {
- switch (log_id) {
- case MAIN:
- return LOG_ID_MAIN;
- case SYSTEM:
- return LOG_ID_SYSTEM;
- case RADIO:
- return LOG_ID_RADIO;
- case CRASH:
- return LOG_ID_CRASH;
- case DEFAULT:
- default:
- return LOG_ID_DEFAULT;
- }
-}
-
-static LogSeverity PriorityToLogSeverity(int priority) {
- switch (priority) {
- case ANDROID_LOG_DEFAULT:
- return INFO;
- case ANDROID_LOG_VERBOSE:
- return VERBOSE;
- case ANDROID_LOG_DEBUG:
- return DEBUG;
- case ANDROID_LOG_INFO:
- return INFO;
- case ANDROID_LOG_WARN:
- return WARNING;
- case ANDROID_LOG_ERROR:
- return ERROR;
- case ANDROID_LOG_FATAL:
- return FATAL;
- default:
- return FATAL;
- }
-}
-
-static int32_t LogSeverityToPriority(LogSeverity severity) {
- switch (severity) {
- case VERBOSE:
- return ANDROID_LOG_VERBOSE;
- case DEBUG:
- return ANDROID_LOG_DEBUG;
- case INFO:
- return ANDROID_LOG_INFO;
- case WARNING:
- return ANDROID_LOG_WARN;
- case ERROR:
- return ANDROID_LOG_ERROR;
- case FATAL_WITHOUT_ABORT:
- case FATAL:
- default:
- return ANDROID_LOG_FATAL;
- }
-}
-
-static LogFunction& Logger() {
-#ifdef __ANDROID__
- static auto& logger = *new LogFunction(LogdLogger());
-#else
- static auto& logger = *new LogFunction(StderrLogger);
-#endif
- return logger;
-}
-
-static AbortFunction& Aborter() {
- static auto& aborter = *new AbortFunction(DefaultAborter);
- return aborter;
-}
-
-// Only used for Q fallback.
-static std::recursive_mutex& TagLock() {
- static auto& tag_lock = *new std::recursive_mutex();
- return tag_lock;
-}
-// Only used for Q fallback.
-static std::string* gDefaultTag;
-
-void SetDefaultTag(const std::string& tag) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- liblog_functions->__android_log_set_default_tag(tag.c_str());
- } else {
- std::lock_guard<std::recursive_mutex> lock(TagLock());
- if (gDefaultTag != nullptr) {
- delete gDefaultTag;
- gDefaultTag = nullptr;
- }
- if (!tag.empty()) {
- gDefaultTag = new std::string(tag);
- }
- }
-}
-
-static bool gInitialized = false;
-
-// Only used for Q fallback.
-static LogSeverity gMinimumLogSeverity = INFO;
-
-#if defined(__linux__)
-static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
- const char* tag) {
- // clang-format off
- static constexpr int kLogSeverityToKernelLogLevel[] = {
- [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
- // level)
- [android::base::DEBUG] = 7, // KERN_DEBUG
- [android::base::INFO] = 6, // KERN_INFO
- [android::base::WARNING] = 4, // KERN_WARNING
- [android::base::ERROR] = 3, // KERN_ERROR
- [android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
- [android::base::FATAL] = 2, // KERN_CRIT
- };
- // clang-format on
- static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
- "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
-
- static int klog_fd = OpenKmsg();
- if (klog_fd == -1) return;
-
- int level = kLogSeverityToKernelLogLevel[severity];
-
- // The kernel's printk buffer is only 1024 bytes.
- // TODO: should we automatically break up long lines into multiple lines?
- // Or we could log but with something like "..." at the end?
- char buf[1024] __attribute__((__uninitialized__));
- size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
- if (size > sizeof(buf)) {
- size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
- level, tag, size);
- }
-
- iovec iov[1];
- iov[0].iov_base = buf;
- iov[0].iov_len = size;
- TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
-}
-
-void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
- const char*, unsigned int, const char* full_message) {
- SplitByLines(full_message, KernelLogLine, severity, tag);
-}
-#endif
-
-void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
- const char* message) {
- struct tm now;
- time_t t = time(nullptr);
-
-#if defined(_WIN32)
- localtime_s(&now, &t);
-#else
- localtime_r(&t, &now);
-#endif
- auto output_string =
- StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message);
-
- fputs(output_string.c_str(), stderr);
-}
-
-void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
- unsigned int /*line*/, const char* message) {
- if (severity >= WARNING) {
- fflush(stdout);
- fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-void DefaultAborter(const char* abort_message) {
-#ifdef __ANDROID__
- android_set_abort_message(abort_message);
-#else
- UNUSED(abort_message);
-#endif
- abort();
-}
-
-static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
- int32_t lg_id = LogIdTolog_id_t(id);
- int32_t priority = LogSeverityToPriority(severity);
-
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- __android_log_message log_message = {sizeof(__android_log_message), lg_id, priority, tag,
- static_cast<const char*>(nullptr), 0, message};
- liblog_functions->__android_log_logd_logger(&log_message);
- } else {
- __android_log_buf_print(lg_id, priority, tag, "%s", message);
- }
-}
-
-LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
-
-void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
- unsigned int line, const char* message) {
- if (id == DEFAULT) {
- id = default_log_id_;
- }
-
- SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
-}
-
-void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
- SetLogger(std::forward<LogFunction>(logger));
- SetAborter(std::forward<AbortFunction>(aborter));
-
- if (gInitialized) {
- return;
- }
-
- gInitialized = true;
-
- // Stash the command line for later use. We can use /proc/self/cmdline on
- // Linux to recover this, but we don't have that luxury on the Mac/Windows,
- // and there are a couple of argv[0] variants that are commonly used.
- if (argv != nullptr) {
- SetDefaultTag(basename(argv[0]));
- }
-
- const char* tags = getenv("ANDROID_LOG_TAGS");
- if (tags == nullptr) {
- return;
- }
-
- std::vector<std::string> specs = Split(tags, " ");
- for (size_t i = 0; i < specs.size(); ++i) {
- // "tag-pattern:[vdiwefs]"
- std::string spec(specs[i]);
- if (spec.size() == 3 && StartsWith(spec, "*:")) {
- switch (spec[2]) {
- case 'v':
- SetMinimumLogSeverity(VERBOSE);
- continue;
- case 'd':
- SetMinimumLogSeverity(DEBUG);
- continue;
- case 'i':
- SetMinimumLogSeverity(INFO);
- continue;
- case 'w':
- SetMinimumLogSeverity(WARNING);
- continue;
- case 'e':
- SetMinimumLogSeverity(ERROR);
- continue;
- case 'f':
- SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
- continue;
- // liblog will even suppress FATAL if you say 's' for silent, but that's
- // crazy!
- case 's':
- SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
- continue;
- }
- }
- LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
- << ")";
- }
-}
-
-void SetLogger(LogFunction&& logger) {
- Logger() = std::move(logger);
-
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
- auto log_id = log_id_tToLogId(log_message->buffer_id);
- auto severity = PriorityToLogSeverity(log_message->priority);
-
- Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
- log_message->message);
- });
- }
-}
-
-void SetAborter(AbortFunction&& aborter) {
- Aborter() = std::move(aborter);
-
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- liblog_functions->__android_log_set_aborter(
- [](const char* abort_message) { Aborter()(abort_message); });
- }
-}
-
-// This indirection greatly reduces the stack impact of having lots of
-// checks/logging in a function.
-class LogMessageData {
- public:
- LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : file_(GetFileBasename(file)),
- line_number_(line),
- severity_(severity),
- tag_(tag),
- error_(error) {}
-
- const char* GetFile() const {
- return file_;
- }
-
- unsigned int GetLineNumber() const {
- return line_number_;
- }
-
- LogSeverity GetSeverity() const {
- return severity_;
- }
-
- const char* GetTag() const { return tag_; }
-
- int GetError() const {
- return error_;
- }
-
- std::ostream& GetBuffer() {
- return buffer_;
- }
-
- std::string ToString() const {
- return buffer_.str();
- }
-
- private:
- std::ostringstream buffer_;
- const char* const file_;
- const unsigned int line_number_;
- const LogSeverity severity_;
- const char* const tag_;
- const int error_;
-
- DISALLOW_COPY_AND_ASSIGN(LogMessageData);
-};
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
- const char* tag, int error)
- : LogMessage(file, line, severity, tag, error) {}
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : data_(new LogMessageData(file, line, severity, tag, error)) {}
-
-LogMessage::~LogMessage() {
- // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
- if (!WOULD_LOG(data_->GetSeverity())) {
- return;
- }
-
- // Finish constructing the message.
- if (data_->GetError() != -1) {
- data_->GetBuffer() << ": " << strerror(data_->GetError());
- }
- std::string msg(data_->ToString());
-
- if (data_->GetSeverity() == FATAL) {
-#ifdef __ANDROID__
- // Set the bionic abort message early to avoid liblog doing it
- // with the individual lines, so that we get the whole message.
- android_set_abort_message(msg.c_str());
-#endif
- }
-
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- msg.c_str());
-
- // Abort if necessary.
- if (data_->GetSeverity() == FATAL) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- liblog_functions->__android_log_call_aborter(msg.c_str());
- } else {
- Aborter()(msg.c_str());
- }
- }
-}
-
-std::ostream& LogMessage::stream() {
- return data_->GetBuffer();
-}
-
-void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- const char* message) {
- static auto& liblog_functions = GetLibLogFunctions();
- int32_t priority = LogSeverityToPriority(severity);
- if (liblog_functions) {
- __android_log_message log_message = {
- sizeof(__android_log_message), LOG_ID_DEFAULT, priority, tag, file, line, message};
- liblog_functions->__android_log_write_log_message(&log_message);
- } else {
- if (tag == nullptr) {
- std::lock_guard<std::recursive_mutex> lock(TagLock());
- if (gDefaultTag == nullptr) {
- gDefaultTag = new std::string(getprogname());
- }
-
- Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
- } else {
- Logger()(DEFAULT, severity, tag, file, line, message);
- }
- }
-}
-
-LogSeverity GetMinimumLogSeverity() {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority());
- } else {
- return gMinimumLogSeverity;
- }
-}
-
-bool ShouldLog(LogSeverity severity, const char* tag) {
- static auto& liblog_functions = GetLibLogFunctions();
- // Even though we're not using the R liblog functions in this function, if we're running on Q,
- // we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
- // take into consideration the value from SetMinimumLogSeverity().
- if (liblog_functions) {
- int32_t priority = LogSeverityToPriority(severity);
- return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
- } else {
- return severity >= gMinimumLogSeverity;
- }
-}
-
-LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- int32_t priority = LogSeverityToPriority(new_severity);
- return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
- } else {
- LogSeverity old_severity = gMinimumLogSeverity;
- gMinimumLogSeverity = new_severity;
- return old_severity;
- }
-}
-
-ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
- old_ = SetMinimumLogSeverity(new_severity);
-}
-
-ScopedLogSeverity::~ScopedLogSeverity() {
- SetMinimumLogSeverity(old_);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/logging_splitters.h b/base/logging_splitters.h
deleted file mode 100644
index 2ec2b20..0000000
--- a/base/logging_splitters.h
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2020 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 <inttypes.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-
-#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
-
-namespace android {
-namespace base {
-
-// This splits the message up line by line, by calling log_function with a pointer to the start of
-// each line and the size up to the newline character. It sends size = -1 for the final line.
-template <typename F, typename... Args>
-static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
- const char* newline = strchr(msg, '\n');
- while (newline != nullptr) {
- log_function(msg, newline - msg, args...);
- msg = newline + 1;
- newline = strchr(msg, '\n');
- }
-
- log_function(msg, -1, args...);
-}
-
-// This splits the message up into chunks that logs can process delimited by new lines. It calls
-// log_function with the exact null terminated message that should be sent to logd.
-// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
-// this function simply calls log_function with msg without any extra overhead.
-template <typename F>
-static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
- unsigned int line, const char* msg, const F& log_function) {
- // The maximum size of a payload, after the log header that logd will accept is
- // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
- // the string that we can log in each pass.
- // The protocol is documented in liblog/README.protocol.md.
- // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
- // and an additional byte for the null terminator on the payload. We subtract an additional 32
- // bytes for slack, similar to java/android/util/Log.java.
- ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
- if (max_size <= 0) {
- abort();
- }
- // If we're logging a fatal message, we'll append the file and line numbers.
- bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
-
- std::string file_header;
- if (add_file) {
- file_header = StringPrintf("%s:%u] ", file, line);
- }
- int file_header_size = file_header.size();
-
- __attribute__((uninitialized)) char logd_chunk[max_size + 1];
- ptrdiff_t chunk_position = 0;
-
- auto call_log_function = [&]() {
- log_function(log_id, severity, tag, logd_chunk);
- chunk_position = 0;
- };
-
- auto write_to_logd_chunk = [&](const char* message, int length) {
- int size_written = 0;
- const char* new_line = chunk_position > 0 ? "\n" : "";
- if (add_file) {
- size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
- "%s%s%.*s", new_line, file_header.c_str(), length, message);
- } else {
- size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
- "%s%.*s", new_line, length, message);
- }
-
- // This should never fail, if it does and we set size_written to 0, which will skip this line
- // and move to the next one.
- if (size_written < 0) {
- size_written = 0;
- }
- chunk_position += size_written;
- };
-
- const char* newline = strchr(msg, '\n');
- while (newline != nullptr) {
- // If we have data in the buffer and this next line doesn't fit, write the buffer.
- if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
- call_log_function();
- }
-
- // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
- // ever fit, in both cases, we add it to the buffer and continue.
- write_to_logd_chunk(msg, newline - msg);
-
- msg = newline + 1;
- newline = strchr(msg, '\n');
- }
-
- // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
- // then write the buffer.
- if (chunk_position != 0 &&
- chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
- write_to_logd_chunk(msg, -1);
- call_log_function();
- } else {
- // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
- if (chunk_position != 0) {
- call_log_function();
- }
- // Then write the rest of the msg.
- if (add_file) {
- snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
- log_function(log_id, severity, tag, logd_chunk);
- } else {
- log_function(log_id, severity, tag, msg);
- }
- }
-}
-
-static std::pair<int, int> CountSizeAndNewLines(const char* message) {
- int size = 0;
- int new_lines = 0;
- while (*message != '\0') {
- size++;
- if (*message == '\n') {
- ++new_lines;
- }
- ++message;
- }
- return {size, new_lines};
-}
-
-// This adds the log header to each line of message and returns it as a string intended to be
-// written to stderr.
-static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
- LogSeverity severity, const char* tag, const char* file,
- unsigned int line, const char* message) {
- char timestamp[32];
- strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
-
- static const char log_characters[] = "VDIWEFF";
- static_assert(arraysize(log_characters) - 1 == FATAL + 1,
- "Mismatch in size of log_characters and values in LogSeverity");
- char severity_char = log_characters[severity];
- std::string line_prefix;
- if (file != nullptr) {
- line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
- severity_char, timestamp, pid, tid, file, line);
- } else {
- line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
- timestamp, pid, tid);
- }
-
- auto [size, new_lines] = CountSizeAndNewLines(message);
- std::string output_string;
- output_string.reserve(size + new_lines * line_prefix.size() + 1);
-
- auto concat_lines = [&](const char* message, int size) {
- output_string.append(line_prefix);
- if (size == -1) {
- output_string.append(message);
- } else {
- output_string.append(message, size);
- }
- output_string.append("\n");
- };
- SplitByLines(message, concat_lines);
- return output_string;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/logging_splitters_test.cpp b/base/logging_splitters_test.cpp
deleted file mode 100644
index 679d19e..0000000
--- a/base/logging_splitters_test.cpp
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (C) 2020 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 "logging_splitters.h"
-
-#include <string>
-#include <vector>
-
-#include <android-base/strings.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace base {
-
-void TestNewlineSplitter(const std::string& input,
- const std::vector<std::string>& expected_output) {
- std::vector<std::string> output;
- auto logger_function = [&](const char* msg, int length) {
- if (length == -1) {
- output.push_back(msg);
- } else {
- output.push_back(std::string(msg, length));
- }
- };
- SplitByLines(input.c_str(), logger_function);
-
- EXPECT_EQ(expected_output, output);
-}
-
-TEST(logging_splitters, NewlineSplitter_EmptyString) {
- TestNewlineSplitter("", std::vector<std::string>{""});
-}
-
-TEST(logging_splitters, NewlineSplitter_BasicString) {
- TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
-}
-
-TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
- TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
-}
-
-TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
- TestNewlineSplitter("normal string\nsecond string\nthirdstring",
- std::vector<std::string>{"normal string", "second string", "thirdstring"});
-}
-
-TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
- TestNewlineSplitter(
- "normal string\nsecond string\nthirdstring\n",
- std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
-}
-
-TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
- TestNewlineSplitter(
- "normal string\n\n\nsecond string\n\nthirdstring\n",
- std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
-}
-
-void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
- const std::string& input,
- const std::vector<std::string>& expected_output) {
- std::vector<std::string> output;
- auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
- output.push_back(msg);
- };
-
- SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
- input.c_str(), logger_function);
-
- auto return_lengths = [&] {
- std::string sizes;
- sizes += "expected_output sizes:";
- for (const auto& string : expected_output) {
- sizes += " " + std::to_string(string.size());
- }
- sizes += "\noutput sizes:";
- for (const auto& string : output) {
- sizes += " " + std::to_string(string.size());
- }
- return sizes;
- };
-
- EXPECT_EQ(expected_output, output) << return_lengths();
-}
-
-TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
- TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
-}
-
-TEST(logging_splitters, LogdChunkSplitter_BasicString) {
- TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
-}
-
-TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
- TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
-}
-
-TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
- TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
- std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
-}
-
-TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
- TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
- std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
-}
-
-TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
- TestLogdChunkSplitter(
- "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
- std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
-}
-
-// This test should return the same string, the logd logger itself will truncate down to size.
-// This has historically been the behavior both in libbase and liblog.
-TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
- auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
- ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
-
- TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
-}
-
-std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
- return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
-}
-
-TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
- auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
- auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
- auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
-
- auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
-
- std::string tag = "tag";
- std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
- long_string_z};
-
- TestLogdChunkSplitter(tag, "", long_strings, expected);
-}
-
-// With a ~4k buffer, we should print 2 long strings per logger call.
-TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
- std::vector expected = {
- std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
- std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
- std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
- };
-
- auto long_strings = Join(expected, '\n');
-
- TestLogdChunkSplitter("tag", "", long_strings, expected);
-}
-
-TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
- const char* tag = "tag";
- ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
- auto long_string_a = std::string(max_size, 'a');
- auto long_string_b = std::string(max_size, 'b');
- auto long_string_c = std::string(max_size, 'c');
-
- auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
-
- TestLogdChunkSplitter(tag, "", long_strings,
- std::vector{long_string_a, long_string_b, long_string_c});
-}
-
-TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
- std::string tag = "tag";
- ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
-
- auto first_string_size = 1000;
- auto first_string = std::string(first_string_size, 'a');
- auto second_string_size = max_size - first_string_size - 1;
- auto second_string = std::string(second_string_size, 'b');
-
- auto exact_string = std::string(max_size, 'c');
-
- auto large_string = std::string(max_size + 50, 'd');
-
- auto final_string = std::string("final string!\n\nfinal \n \n final \n");
-
- std::vector expected = {first_string + '\n' + second_string, exact_string,
- ReduceToMaxSize(tag, large_string), final_string};
-
- std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
- final_string};
- auto long_strings = Join(input_strings, '\n');
-
- TestLogdChunkSplitter(tag, "", long_strings, expected);
-}
-
-TEST(logging_splitters, LogdChunkSplitter_WithFile) {
- std::string tag = "tag";
- std::string file = "/path/to/myfile.cpp";
- int line = 1000;
- auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
- ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
-
- auto first_string_size = 1000;
- auto first_string = std::string(first_string_size, 'a');
- auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
- auto second_string = std::string(second_string_size, 'b');
-
- auto exact_string = std::string(max_size - file_header.size(), 'c');
-
- auto large_string = std::string(max_size + 50, 'd');
-
- auto final_string = std::string("final string!");
-
- std::vector expected = {
- file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
- file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
-
- std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
- final_string};
- auto long_strings = Join(input_strings, '\n');
-
- TestLogdChunkSplitter(tag, file, long_strings, expected);
-}
-
-// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
-// We could recover from this, but it's certainly an error for someone to attempt to use a tag this
-// large, so we abort instead.
-TEST(logging_splitters, LogdChunkSplitter_TooLongTag) {
- auto long_tag = std::string(5000, 'x');
- auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
- ASSERT_DEATH(
- SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
-}
-
-// We do handle excessively large file names correctly however.
-TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
- auto long_file = std::string(5000, 'x');
- std::string tag = "tag";
-
- std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
-
- TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
-}
-
-void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
- const std::string& expected) {
- // All log messages will show "01-01 00:00:00"
- struct tm now = {
- .tm_sec = 0,
- .tm_min = 0,
- .tm_hour = 0,
- .tm_mday = 1,
- .tm_mon = 0,
- .tm_year = 1970,
- };
-
- int pid = 1234; // All log messages will have 1234 for their PID.
- uint64_t tid = 4321; // All log messages will have 4321 for their TID.
-
- auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message);
- EXPECT_EQ(expected, result);
-}
-
-TEST(logging_splitters, StderrOutputGenerator_Basic) {
- TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
- "nullptr E 01-01 00:00:00 1234 4321 simple message\n");
- TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
- "tag E 01-01 00:00:00 1234 4321 simple message\n");
- TestStderrOutputGenerator(
- "tag", "/path/to/some/file", 0, "simple message",
- "tag E 01-01 00:00:00 1234 4321 /path/to/some/file:0] simple message\n");
-}
-
-TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
- TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
- "tag\n\n E 01-01 00:00:00 1234 4321 simple message\n");
- TestStderrOutputGenerator(
- "tag", "/path/to/some/file\n\n", 0, "simple message",
- "tag E 01-01 00:00:00 1234 4321 /path/to/some/file\n\n:0] simple message\n");
-}
-
-TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
- TestStderrOutputGenerator(
- "tag", nullptr, 0, "simple message\n",
- "tag E 01-01 00:00:00 1234 4321 simple message\ntag E 01-01 00:00:00 1234 4321 \n");
-}
-
-TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
- const char* expected_result =
- "tag E 01-01 00:00:00 1234 4321 simple message\n"
- "tag E 01-01 00:00:00 1234 4321 \n"
- "tag E 01-01 00:00:00 1234 4321 \n"
- "tag E 01-01 00:00:00 1234 4321 another message \n"
- "tag E 01-01 00:00:00 1234 4321 \n"
- "tag E 01-01 00:00:00 1234 4321 final message \n"
- "tag E 01-01 00:00:00 1234 4321 \n"
- "tag E 01-01 00:00:00 1234 4321 \n"
- "tag E 01-01 00:00:00 1234 4321 \n";
-
- TestStderrOutputGenerator("tag", nullptr, 0,
- "simple message\n\n\nanother message \n\n final message \n\n\n",
- expected_result);
-}
-
-TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
- auto long_string_a = std::string(4000, 'a');
- auto long_string_b = std::string(4000, 'b');
-
- auto message = long_string_a + '\n' + long_string_b;
- auto expected_result = "tag E 01-01 00:00:00 1234 4321 " + long_string_a + '\n' +
- "tag E 01-01 00:00:00 1234 4321 " + long_string_b + '\n';
- TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
deleted file mode 100644
index 593e2c1..0000000
--- a/base/logging_test.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/logging.h"
-
-#include <libgen.h>
-
-#if defined(_WIN32)
-#include <signal.h>
-#endif
-
-#include <regex>
-#include <string>
-#include <thread>
-
-#include "android-base/file.h"
-#include "android-base/scopeguard.h"
-#include "android-base/stringprintf.h"
-#include "android-base/test_utils.h"
-
-#include <gtest/gtest.h>
-
-#ifdef __ANDROID__
-#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
-#else
-#define HOST_TEST(suite, name) TEST(suite, name)
-#endif
-
-#if defined(_WIN32)
-static void ExitSignalAbortHandler(int) {
- _exit(3);
-}
-#endif
-
-static void SuppressAbortUI() {
-#if defined(_WIN32)
- // We really just want to call _set_abort_behavior(0, _CALL_REPORTFAULT) to
- // suppress the Windows Error Reporting dialog box, but that API is not
- // available in the OS-supplied C Runtime, msvcrt.dll, that we currently
- // use (it is available in the Visual Studio C runtime).
- //
- // Instead, we setup a SIGABRT handler, which is called in abort() right
- // before calling Windows Error Reporting. In the handler, we exit the
- // process just like abort() does.
- ASSERT_NE(SIG_ERR, signal(SIGABRT, ExitSignalAbortHandler));
-#endif
-}
-
-TEST(logging, CHECK) {
- ASSERT_DEATH({SuppressAbortUI(); CHECK(false);}, "Check failed: false ");
- CHECK(true);
-
- ASSERT_DEATH({SuppressAbortUI(); CHECK_EQ(0, 1);}, "Check failed: 0 == 1 ");
- CHECK_EQ(0, 0);
-
- ASSERT_DEATH({SuppressAbortUI(); CHECK_STREQ("foo", "bar");},
- R"(Check failed: "foo" == "bar")");
- CHECK_STREQ("foo", "foo");
-
- // Test whether CHECK() and CHECK_STREQ() have a dangling if with no else.
- bool flag = false;
- if (true)
- CHECK(true);
- else
- flag = true;
- EXPECT_FALSE(flag) << "CHECK macro probably has a dangling if with no else";
-
- flag = false;
- if (true)
- CHECK_STREQ("foo", "foo");
- else
- flag = true;
- EXPECT_FALSE(flag) << "CHECK_STREQ probably has a dangling if with no else";
-}
-
-TEST(logging, DCHECK) {
- if (android::base::kEnableDChecks) {
- ASSERT_DEATH({SuppressAbortUI(); DCHECK(false);}, "DCheck failed: false ");
- }
- DCHECK(true);
-
- if (android::base::kEnableDChecks) {
- ASSERT_DEATH({SuppressAbortUI(); DCHECK_EQ(0, 1);}, "DCheck failed: 0 == 1 ");
- }
- DCHECK_EQ(0, 0);
-
- if (android::base::kEnableDChecks) {
- ASSERT_DEATH({SuppressAbortUI(); DCHECK_STREQ("foo", "bar");},
- R"(DCheck failed: "foo" == "bar")");
- }
- DCHECK_STREQ("foo", "foo");
-
- // No testing whether we have a dangling else, possibly. That's inherent to the if (constexpr)
- // setup we intentionally chose to force type-checks of debug code even in release builds (so
- // we don't get more bit-rot).
-}
-
-
-#define CHECK_WOULD_LOG_DISABLED(severity) \
- static_assert(android::base::severity < android::base::FATAL, "Bad input"); \
- for (size_t i = static_cast<size_t>(android::base::severity) + 1; \
- i <= static_cast<size_t>(android::base::FATAL); \
- ++i) { \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_FALSE(WOULD_LOG(severity)) << i; \
- } \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_FALSE(WOULD_LOG(::android::base::severity)) << i; \
- } \
- } \
-
-#define CHECK_WOULD_LOG_ENABLED(severity) \
- for (size_t i = static_cast<size_t>(android::base::VERBOSE); \
- i <= static_cast<size_t>(android::base::severity); \
- ++i) { \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_TRUE(WOULD_LOG(severity)) << i; \
- } \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_TRUE(WOULD_LOG(::android::base::severity)) << i; \
- } \
- } \
-
-TEST(logging, WOULD_LOG_FATAL) {
- CHECK_WOULD_LOG_ENABLED(FATAL);
-}
-
-TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
- CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
-}
-
-TEST(logging, WOULD_LOG_ERROR_disabled) {
- CHECK_WOULD_LOG_DISABLED(ERROR);
-}
-
-TEST(logging, WOULD_LOG_ERROR_enabled) {
- CHECK_WOULD_LOG_ENABLED(ERROR);
-}
-
-TEST(logging, WOULD_LOG_WARNING_disabled) {
- CHECK_WOULD_LOG_DISABLED(WARNING);
-}
-
-TEST(logging, WOULD_LOG_WARNING_enabled) {
- CHECK_WOULD_LOG_ENABLED(WARNING);
-}
-
-TEST(logging, WOULD_LOG_INFO_disabled) {
- CHECK_WOULD_LOG_DISABLED(INFO);
-}
-
-TEST(logging, WOULD_LOG_INFO_enabled) {
- CHECK_WOULD_LOG_ENABLED(INFO);
-}
-
-TEST(logging, WOULD_LOG_DEBUG_disabled) {
- CHECK_WOULD_LOG_DISABLED(DEBUG);
-}
-
-TEST(logging, WOULD_LOG_DEBUG_enabled) {
- CHECK_WOULD_LOG_ENABLED(DEBUG);
-}
-
-TEST(logging, WOULD_LOG_VERBOSE_disabled) {
- CHECK_WOULD_LOG_DISABLED(VERBOSE);
-}
-
-TEST(logging, WOULD_LOG_VERBOSE_enabled) {
- CHECK_WOULD_LOG_ENABLED(VERBOSE);
-}
-
-#undef CHECK_WOULD_LOG_DISABLED
-#undef CHECK_WOULD_LOG_ENABLED
-
-
-#if !defined(_WIN32)
-static std::string make_log_pattern(android::base::LogSeverity severity,
- const char* message) {
- static const char log_characters[] = "VDIWEFF";
- static_assert(arraysize(log_characters) - 1 == android::base::FATAL + 1,
- "Mismatch in size of log_characters and values in LogSeverity");
- char log_char = log_characters[severity];
- std::string holder(__FILE__);
- return android::base::StringPrintf(
- "%c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s:\\d+] %s",
- log_char, basename(&holder[0]), message);
-}
-#endif
-
-static void CheckMessage(const std::string& output, android::base::LogSeverity severity,
- const char* expected, const char* expected_tag = nullptr) {
- // We can't usefully check the output of any of these on Windows because we
- // don't have std::regex, but we can at least make sure we printed at least as
- // many characters are in the log message.
- ASSERT_GT(output.length(), strlen(expected));
- ASSERT_NE(nullptr, strstr(output.c_str(), expected)) << output;
- if (expected_tag != nullptr) {
- ASSERT_NE(nullptr, strstr(output.c_str(), expected_tag)) << output;
- }
-
-#if !defined(_WIN32)
- std::string regex_str;
- if (expected_tag != nullptr) {
- regex_str.append(expected_tag);
- regex_str.append(" ");
- }
- regex_str.append(make_log_pattern(severity, expected));
- std::regex message_regex(regex_str);
- ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
-#endif
-}
-
-static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
- const char* expected, const char* expected_tag = nullptr) {
- cap.Stop();
- std::string output = cap.str();
- return CheckMessage(output, severity, expected, expected_tag);
-}
-
-#define CHECK_LOG_STREAM_DISABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG_STREAM(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- } \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG_STREAM(::android::base::severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- }
-
-#define CHECK_LOG_STREAM_ENABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG_STREAM(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG_STREAM(::android::base::severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
-
-TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
-}
-
-TEST(logging, LOG_STREAM_ERROR_disabled) {
- CHECK_LOG_STREAM_DISABLED(ERROR);
-}
-
-TEST(logging, LOG_STREAM_ERROR_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(ERROR));
-}
-
-TEST(logging, LOG_STREAM_WARNING_disabled) {
- CHECK_LOG_STREAM_DISABLED(WARNING);
-}
-
-TEST(logging, LOG_STREAM_WARNING_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(WARNING));
-}
-
-TEST(logging, LOG_STREAM_INFO_disabled) {
- CHECK_LOG_STREAM_DISABLED(INFO);
-}
-
-TEST(logging, LOG_STREAM_INFO_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(INFO));
-}
-
-TEST(logging, LOG_STREAM_DEBUG_disabled) {
- CHECK_LOG_STREAM_DISABLED(DEBUG);
-}
-
-TEST(logging, LOG_STREAM_DEBUG_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(DEBUG));
-}
-
-TEST(logging, LOG_STREAM_VERBOSE_disabled) {
- CHECK_LOG_STREAM_DISABLED(VERBOSE);
-}
-
-TEST(logging, LOG_STREAM_VERBOSE_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(VERBOSE));
-}
-
-#undef CHECK_LOG_STREAM_DISABLED
-#undef CHECK_LOG_STREAM_ENABLED
-
-#define CHECK_LOG_DISABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- } \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG(::android::base::severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- }
-
-#define CHECK_LOG_ENABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG(::android::base::severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
-
-TEST(logging, LOG_FATAL) {
- ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
- ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
-}
-
-TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
-}
-
-TEST(logging, LOG_ERROR_disabled) {
- CHECK_LOG_DISABLED(ERROR);
-}
-
-TEST(logging, LOG_ERROR_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(ERROR));
-}
-
-TEST(logging, LOG_WARNING_disabled) {
- CHECK_LOG_DISABLED(WARNING);
-}
-
-TEST(logging, LOG_WARNING_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(WARNING));
-}
-
-TEST(logging, LOG_INFO_disabled) {
- CHECK_LOG_DISABLED(INFO);
-}
-
-TEST(logging, LOG_INFO_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(INFO));
-}
-
-TEST(logging, LOG_DEBUG_disabled) {
- CHECK_LOG_DISABLED(DEBUG);
-}
-
-TEST(logging, LOG_DEBUG_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(DEBUG));
-}
-
-TEST(logging, LOG_VERBOSE_disabled) {
- CHECK_LOG_DISABLED(VERBOSE);
-}
-
-TEST(logging, LOG_VERBOSE_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(VERBOSE));
-}
-
-#undef CHECK_LOG_DISABLED
-#undef CHECK_LOG_ENABLED
-
-TEST(logging, LOG_complex_param) {
-#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info) \
- { \
- android::base::ScopedLogSeverity sls( \
- (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING); \
- CapturedStderr cap; \
- LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING) \
- << "foobar"; \
- if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) { \
- ASSERT_NO_FATAL_FAILURE(CheckMessage( \
- cap, (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
- "foobar")); \
- } else { \
- cap.Stop(); \
- ASSERT_EQ("", cap.str()); \
- } \
- }
-
- CHECK_LOG_COMBINATION(false,false);
- CHECK_LOG_COMBINATION(false,true);
- CHECK_LOG_COMBINATION(true,false);
- CHECK_LOG_COMBINATION(true,true);
-
-#undef CHECK_LOG_COMBINATION
-}
-
-
-TEST(logging, LOG_does_not_clobber_errno) {
- CapturedStderr cap;
- errno = 12345;
- LOG(INFO) << (errno = 67890);
- EXPECT_EQ(12345, errno) << "errno was not restored";
-
- ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
-}
-
-TEST(logging, PLOG_does_not_clobber_errno) {
- CapturedStderr cap;
- errno = 12345;
- PLOG(INFO) << (errno = 67890);
- EXPECT_EQ(12345, errno) << "errno was not restored";
-
- ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
-}
-
-TEST(logging, LOG_does_not_have_dangling_if) {
- CapturedStderr cap; // So the logging below has no side-effects.
-
- // Do the test two ways: once where we hypothesize that LOG()'s if
- // will evaluate to true (when severity is high enough) and once when we
- // expect it to evaluate to false (when severity is not high enough).
- bool flag = false;
- if (true)
- LOG(INFO) << "foobar";
- else
- flag = true;
-
- EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
-
- flag = false;
- if (true)
- LOG(VERBOSE) << "foobar";
- else
- flag = true;
-
- EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
-}
-
-#define CHECK_PLOG_DISABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- PLOG(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- } \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- PLOG(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- }
-
-#define CHECK_PLOG_ENABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- errno = ENOENT; \
- PLOG(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
- } \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- errno = ENOENT; \
- PLOG(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
- } \
-
-TEST(logging, PLOG_FATAL) {
- ASSERT_DEATH({SuppressAbortUI(); PLOG(FATAL) << "foobar";}, "foobar");
- ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
-}
-
-TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
-}
-
-TEST(logging, PLOG_ERROR_disabled) {
- CHECK_PLOG_DISABLED(ERROR);
-}
-
-TEST(logging, PLOG_ERROR_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(ERROR));
-}
-
-TEST(logging, PLOG_WARNING_disabled) {
- CHECK_PLOG_DISABLED(WARNING);
-}
-
-TEST(logging, PLOG_WARNING_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(WARNING));
-}
-
-TEST(logging, PLOG_INFO_disabled) {
- CHECK_PLOG_DISABLED(INFO);
-}
-
-TEST(logging, PLOG_INFO_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(INFO));
-}
-
-TEST(logging, PLOG_DEBUG_disabled) {
- CHECK_PLOG_DISABLED(DEBUG);
-}
-
-TEST(logging, PLOG_DEBUG_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(DEBUG));
-}
-
-TEST(logging, PLOG_VERBOSE_disabled) {
- CHECK_PLOG_DISABLED(VERBOSE);
-}
-
-TEST(logging, PLOG_VERBOSE_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(VERBOSE));
-}
-
-#undef CHECK_PLOG_DISABLED
-#undef CHECK_PLOG_ENABLED
-
-
-TEST(logging, UNIMPLEMENTED) {
- std::string expected = android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
-
- CapturedStderr cap;
- errno = ENOENT;
- UNIMPLEMENTED(ERROR);
- ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::ERROR, expected.c_str()));
-}
-
-static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
- LOG(ERROR) << "called noop";
-}
-
-TEST(logging, LOG_FATAL_NOOP_ABORTER) {
- CapturedStderr cap;
- {
- android::base::SetAborter(NoopAborter);
-
- android::base::ScopedLogSeverity sls(android::base::ERROR);
- LOG(FATAL) << "foobar";
- cap.Stop();
-
- android::base::SetAborter(android::base::DefaultAborter);
- }
- std::string output = cap.str();
- ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::FATAL, "foobar"));
- ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::ERROR, "called noop"));
-
- ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
-}
-
-struct CountLineAborter {
- static void CountLineAborterFunction(const char* msg) {
- while (*msg != 0) {
- if (*msg == '\n') {
- newline_count++;
- }
- msg++;
- }
- }
- static size_t newline_count;
-};
-size_t CountLineAborter::newline_count = 0;
-
-TEST(logging, LOG_FATAL_ABORTER_MESSAGE) {
- CountLineAborter::newline_count = 0;
- android::base::SetAborter(CountLineAborter::CountLineAborterFunction);
-
- android::base::ScopedLogSeverity sls(android::base::ERROR);
- CapturedStderr cap;
- LOG(FATAL) << "foo\nbar";
-
- EXPECT_EQ(CountLineAborter::newline_count, 1U);
-}
-
-__attribute__((constructor)) void TestLoggingInConstructor() {
- LOG(ERROR) << "foobar";
-}
-
-TEST(logging, StdioLogger) {
- CapturedStderr cap_err;
- CapturedStdout cap_out;
- android::base::SetLogger(android::base::StdioLogger);
- LOG(INFO) << "out";
- LOG(ERROR) << "err";
- cap_err.Stop();
- cap_out.Stop();
-
- // For INFO we expect just the literal "out\n".
- ASSERT_EQ("out\n", cap_out.str());
- // Whereas ERROR logging includes the program name.
- ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
-}
-
-TEST(logging, ForkSafe) {
-#if !defined(_WIN32)
- using namespace android::base;
- SetLogger(
- [&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); });
-
- auto guard = make_scope_guard([&] {
-#ifdef __ANDROID__
- SetLogger(LogdLogger());
-#else
- SetLogger(StderrLogger);
-#endif
- });
-
- auto thread = std::thread([] {
- LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there "
- "is no intervention";
- });
- thread.detach();
-
- auto pid = fork();
- ASSERT_NE(-1, pid);
-
- if (pid == 0) {
- // Reset the logger, so the next message doesn't sleep().
- SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {});
- LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe.";
- _exit(EXIT_SUCCESS);
- }
-
- // Wait for up to 3 seconds for the child to exit.
- int tries = 3;
- bool found_child = false;
- while (tries-- > 0) {
- auto result = waitpid(pid, nullptr, WNOHANG);
- EXPECT_NE(-1, result);
- if (result == pid) {
- found_child = true;
- break;
- }
- sleep(1);
- }
-
- EXPECT_TRUE(found_child);
-
- // Kill the child if it did not exit.
- if (!found_child) {
- kill(pid, SIGKILL);
- }
-#endif
-}
diff --git a/base/macros_test.cpp b/base/macros_test.cpp
deleted file mode 100644
index 2b522db..0000000
--- a/base/macros_test.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/macros.h"
-
-#include <stdint.h>
-
-#include <gtest/gtest.h>
-
-TEST(macros, SIZEOF_MEMBER_macro) {
- struct S {
- int32_t i32;
- double d;
- };
- ASSERT_EQ(4U, SIZEOF_MEMBER(S, i32));
- ASSERT_EQ(8U, SIZEOF_MEMBER(S, d));
-}
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
deleted file mode 100644
index fff3453..0000000
--- a/base/mapped_file.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/mapped_file.h"
-
-#include <utility>
-
-#include <errno.h>
-
-namespace android {
-namespace base {
-
-static constexpr char kEmptyBuffer[] = {'0'};
-
-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(borrowed_fd fd, off64_t offset, size_t length,
- int prot) {
-#if defined(_WIN32)
- return FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
-#else
- return FromOsHandle(fd.get(), offset, length, prot);
-#endif
-}
-
-std::unique_ptr<MappedFile> MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length,
- int prot) {
- static const 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 = CreateFileMappingW(
- h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
- if (handle == nullptr) {
- // http://b/119818070 "app crashes when reading asset of zero length".
- // Return a MappedFile that's only valid for reading the size.
- if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
- return std::unique_ptr<MappedFile>(
- new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0, nullptr));
- }
- return nullptr;
- }
- void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
- file_offset, file_length);
- if (base == nullptr) {
- 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, h, file_offset);
- if (base == MAP_FAILED) {
- // http://b/119818070 "app crashes when reading asset of zero length".
- // mmap fails with EINVAL for a zero length region.
- if (errno == EINVAL && length == 0) {
- return std::unique_ptr<MappedFile>(new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0));
- }
- return nullptr;
- }
- return std::unique_ptr<MappedFile>(new MappedFile(static_cast<char*>(base), length, slop));
-#endif
-}
-
-MappedFile::MappedFile(MappedFile&& other)
- : base_(std::exchange(other.base_, nullptr)),
- size_(std::exchange(other.size_, 0)),
- offset_(std::exchange(other.offset_, 0))
-#ifdef _WIN32
- ,
- handle_(std::exchange(other.handle_, nullptr))
-#endif
-{
-}
-
-MappedFile& MappedFile::operator=(MappedFile&& other) {
- Close();
- base_ = std::exchange(other.base_, nullptr);
- size_ = std::exchange(other.size_, 0);
- offset_ = std::exchange(other.offset_, 0);
-#ifdef _WIN32
- handle_ = std::exchange(other.handle_, nullptr);
-#endif
- return *this;
-}
-
-MappedFile::~MappedFile() {
- Close();
-}
-
-void MappedFile::Close() {
-#if defined(_WIN32)
- if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
- if (handle_ != nullptr) CloseHandle(handle_);
- handle_ = nullptr;
-#else
- if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
-#endif
-
- base_ = nullptr;
- offset_ = size_ = 0;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
deleted file mode 100644
index d21703c..0000000
--- a/base/mapped_file_test.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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"
-
-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]);
-}
-
-TEST(mapped_file, zero_length_mapping) {
- // http://b/119818070 "app crashes when reading asset of zero length".
- // mmap fails with EINVAL for a zero length region.
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
-
- auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
- EXPECT_EQ(0u, m->size());
- EXPECT_NE(nullptr, m->data());
-}
diff --git a/base/no_destructor_test.cpp b/base/no_destructor_test.cpp
deleted file mode 100644
index f19468a..0000000
--- a/base/no_destructor_test.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/no_destructor.h"
-
-#include <gtest/gtest.h>
-
-struct __attribute__((packed)) Bomb {
- Bomb() : magic_(123) {}
-
- ~Bomb() { exit(42); }
-
- int get() const { return magic_; }
-
- private:
- [[maybe_unused]] char padding_;
- int magic_;
-};
-
-TEST(no_destructor, bomb) {
- ASSERT_EXIT(({
- {
- Bomb b;
- if (b.get() != 123) exit(1);
- }
-
- exit(0);
- }),
- ::testing::ExitedWithCode(42), "");
-}
-
-TEST(no_destructor, defused) {
- ASSERT_EXIT(({
- {
- android::base::NoDestructor<Bomb> b;
- if (b->get() != 123) exit(1);
- }
-
- exit(0);
- }),
- ::testing::ExitedWithCode(0), "");
-}
-
-TEST(no_destructor, operators) {
- android::base::NoDestructor<Bomb> b;
- const android::base::NoDestructor<Bomb>& c = b;
- ASSERT_EQ(123, b.get()->get());
- ASSERT_EQ(123, b->get());
- ASSERT_EQ(123, (*b).get());
- ASSERT_EQ(123, c.get()->get());
- ASSERT_EQ(123, c->get());
- ASSERT_EQ(123, (*c).get());
-}
diff --git a/base/parsebool.cpp b/base/parsebool.cpp
deleted file mode 100644
index ff96fe9..0000000
--- a/base/parsebool.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/parsebool.h"
-#include <errno.h>
-
-namespace android {
-namespace base {
-
-ParseBoolResult ParseBool(std::string_view s) {
- if (s == "1" || s == "y" || s == "yes" || s == "on" || s == "true") {
- return ParseBoolResult::kTrue;
- }
- if (s == "0" || s == "n" || s == "no" || s == "off" || s == "false") {
- return ParseBoolResult::kFalse;
- }
- return ParseBoolResult::kError;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/parsebool_test.cpp b/base/parsebool_test.cpp
deleted file mode 100644
index a081994..0000000
--- a/base/parsebool_test.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/parsebool.h"
-
-#include <errno.h>
-
-#include <gtest/gtest.h>
-#include <string_view>
-
-using android::base::ParseBool;
-using android::base::ParseBoolResult;
-
-TEST(parsebool, true_) {
- static const char* yes[] = {
- "1", "on", "true", "y", "yes",
- };
- for (const char* s : yes) {
- ASSERT_EQ(ParseBoolResult::kTrue, ParseBool(s));
- }
-}
-
-TEST(parsebool, false_) {
- static const char* no[] = {
- "0", "false", "n", "no", "off",
- };
- for (const char* s : no) {
- ASSERT_EQ(ParseBoolResult::kFalse, ParseBool(s));
- }
-}
-
-TEST(parsebool, invalid) {
- ASSERT_EQ(ParseBoolResult::kError, ParseBool("blarg"));
- ASSERT_EQ(ParseBoolResult::kError, ParseBool(""));
-}
diff --git a/base/parsedouble_test.cpp b/base/parsedouble_test.cpp
deleted file mode 100644
index ec3c10c..0000000
--- a/base/parsedouble_test.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2016 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/parsedouble.h"
-
-#include <gtest/gtest.h>
-
-TEST(parsedouble, double_smoke) {
- double d;
- ASSERT_FALSE(android::base::ParseDouble("", &d));
- ASSERT_FALSE(android::base::ParseDouble("x", &d));
- ASSERT_FALSE(android::base::ParseDouble("123.4x", &d));
-
- ASSERT_TRUE(android::base::ParseDouble("123.4", &d));
- ASSERT_DOUBLE_EQ(123.4, d);
- ASSERT_TRUE(android::base::ParseDouble("-123.4", &d));
- ASSERT_DOUBLE_EQ(-123.4, d);
-
- ASSERT_TRUE(android::base::ParseDouble("0", &d, 0.0));
- ASSERT_DOUBLE_EQ(0.0, d);
- ASSERT_FALSE(android::base::ParseDouble("0", &d, 1e-9));
- ASSERT_FALSE(android::base::ParseDouble("3.0", &d, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseDouble("1.0", &d, 0.0, 2.0));
- ASSERT_DOUBLE_EQ(1.0, d);
-
- ASSERT_FALSE(android::base::ParseDouble("123.4x", nullptr));
- ASSERT_TRUE(android::base::ParseDouble("-123.4", nullptr));
- ASSERT_FALSE(android::base::ParseDouble("3.0", nullptr, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseDouble("1.0", nullptr, 0.0, 2.0));
-}
-
-TEST(parsedouble, float_smoke) {
- float f;
- ASSERT_FALSE(android::base::ParseFloat("", &f));
- ASSERT_FALSE(android::base::ParseFloat("x", &f));
- ASSERT_FALSE(android::base::ParseFloat("123.4x", &f));
-
- ASSERT_TRUE(android::base::ParseFloat("123.4", &f));
- ASSERT_FLOAT_EQ(123.4, f);
- ASSERT_TRUE(android::base::ParseFloat("-123.4", &f));
- ASSERT_FLOAT_EQ(-123.4, f);
-
- ASSERT_TRUE(android::base::ParseFloat("0", &f, 0.0));
- ASSERT_FLOAT_EQ(0.0, f);
- ASSERT_FALSE(android::base::ParseFloat("0", &f, 1e-9));
- ASSERT_FALSE(android::base::ParseFloat("3.0", &f, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseFloat("1.0", &f, 0.0, 2.0));
- ASSERT_FLOAT_EQ(1.0, f);
-
- ASSERT_FALSE(android::base::ParseFloat("123.4x", nullptr));
- ASSERT_TRUE(android::base::ParseFloat("-123.4", nullptr));
- ASSERT_FALSE(android::base::ParseFloat("3.0", nullptr, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseFloat("1.0", nullptr, 0.0, 2.0));
-}
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
deleted file mode 100644
index e449c33..0000000
--- a/base/parseint_test.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/parseint.h"
-
-#include <errno.h>
-
-#include <gtest/gtest.h>
-
-TEST(parseint, signed_smoke) {
- errno = 0;
- int i = 0;
- ASSERT_FALSE(android::base::ParseInt("x", &i));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt("123x", &i));
- ASSERT_EQ(EINVAL, errno);
-
- ASSERT_TRUE(android::base::ParseInt("123", &i));
- ASSERT_EQ(123, i);
- ASSERT_EQ(0, errno);
- i = 0;
- EXPECT_TRUE(android::base::ParseInt(" 123", &i));
- EXPECT_EQ(123, i);
- ASSERT_TRUE(android::base::ParseInt("-123", &i));
- ASSERT_EQ(-123, i);
- i = 0;
- EXPECT_TRUE(android::base::ParseInt(" -123", &i));
- EXPECT_EQ(-123, i);
-
- short s = 0;
- ASSERT_TRUE(android::base::ParseInt("1234", &s));
- ASSERT_EQ(1234, s);
-
- ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
- ASSERT_EQ(12, i);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
- ASSERT_EQ(ERANGE, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
- ASSERT_EQ(ERANGE, errno);
-
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
-}
-
-TEST(parseint, unsigned_smoke) {
- errno = 0;
- unsigned int i = 0u;
- ASSERT_FALSE(android::base::ParseUint("x", &i));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("123x", &i));
- ASSERT_EQ(EINVAL, errno);
-
- ASSERT_TRUE(android::base::ParseUint("123", &i));
- ASSERT_EQ(123u, i);
- ASSERT_EQ(0, errno);
- i = 0u;
- EXPECT_TRUE(android::base::ParseUint(" 123", &i));
- EXPECT_EQ(123u, i);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("-123", &i));
- EXPECT_EQ(EINVAL, errno);
- errno = 0;
- EXPECT_FALSE(android::base::ParseUint(" -123", &i));
- EXPECT_EQ(EINVAL, errno);
-
- unsigned short s = 0u;
- ASSERT_TRUE(android::base::ParseUint("1234", &s));
- ASSERT_EQ(1234u, s);
-
- ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
- ASSERT_EQ(12u, i);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
- ASSERT_EQ(ERANGE, errno);
-
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
-
- errno = 0;
- unsigned long long int lli;
- EXPECT_FALSE(android::base::ParseUint("-123", &lli));
- EXPECT_EQ(EINVAL, errno);
- errno = 0;
- EXPECT_FALSE(android::base::ParseUint(" -123", &lli));
- EXPECT_EQ(EINVAL, errno);
-}
-
-TEST(parseint, no_implicit_octal) {
- int i = 0;
- ASSERT_TRUE(android::base::ParseInt("0123", &i));
- ASSERT_EQ(123, i);
-
- unsigned int u = 0u;
- ASSERT_TRUE(android::base::ParseUint("0123", &u));
- ASSERT_EQ(123u, u);
-}
-
-TEST(parseint, explicit_hex) {
- int i = 0;
- ASSERT_TRUE(android::base::ParseInt("0x123", &i));
- ASSERT_EQ(0x123, i);
- i = 0;
- EXPECT_TRUE(android::base::ParseInt(" 0x123", &i));
- EXPECT_EQ(0x123, i);
-
- unsigned int u = 0u;
- ASSERT_TRUE(android::base::ParseUint("0x123", &u));
- ASSERT_EQ(0x123u, u);
- u = 0u;
- EXPECT_TRUE(android::base::ParseUint(" 0x123", &u));
- EXPECT_EQ(0x123u, u);
-}
-
-TEST(parseint, string) {
- int i = 0;
- ASSERT_TRUE(android::base::ParseInt(std::string("123"), &i));
- ASSERT_EQ(123, i);
-
- unsigned int u = 0u;
- ASSERT_TRUE(android::base::ParseUint(std::string("123"), &u));
- ASSERT_EQ(123u, u);
-}
-
-TEST(parseint, untouched_on_failure) {
- int i = 123;
- ASSERT_FALSE(android::base::ParseInt("456x", &i));
- ASSERT_EQ(123, i);
-
- unsigned int u = 123u;
- ASSERT_FALSE(android::base::ParseUint("456x", &u));
- ASSERT_EQ(123u, u);
-}
-
-TEST(parseint, ParseByteCount) {
- uint64_t i = 0;
- ASSERT_TRUE(android::base::ParseByteCount("123b", &i));
- ASSERT_EQ(123ULL, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("8k", &i));
- ASSERT_EQ(8ULL * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("8M", &i));
- ASSERT_EQ(8ULL * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("6g", &i));
- ASSERT_EQ(6ULL * 1024 * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("1T", &i));
- ASSERT_EQ(1ULL * 1024 * 1024 * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("2p", &i));
- ASSERT_EQ(2ULL * 1024 * 1024 * 1024 * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("4e", &i));
- ASSERT_EQ(4ULL * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, i);
-}
-
-TEST(parseint, ParseByteCount_invalid_suffix) {
- unsigned u;
- ASSERT_FALSE(android::base::ParseByteCount("1x", &u));
-}
-
-TEST(parseint, ParseByteCount_overflow) {
- uint64_t u64;
- ASSERT_FALSE(android::base::ParseByteCount("4294967295E", &u64));
-
- uint16_t u16;
- ASSERT_TRUE(android::base::ParseByteCount("63k", &u16));
- ASSERT_EQ(63U * 1024, u16);
- ASSERT_TRUE(android::base::ParseByteCount("65535b", &u16));
- ASSERT_EQ(65535U, u16);
- ASSERT_FALSE(android::base::ParseByteCount("65k", &u16));
-}
diff --git a/base/parsenetaddress.cpp b/base/parsenetaddress.cpp
deleted file mode 100644
index dd80f6d..0000000
--- a/base/parsenetaddress.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2016 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/parsenetaddress.h"
-
-#include <algorithm>
-
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
-
-namespace android {
-namespace base {
-
-bool ParseNetAddress(const std::string& address, std::string* host, int* port,
- std::string* canonical_address, std::string* error) {
- host->clear();
-
- bool ipv6 = true;
- bool saw_port = false;
- size_t colons = std::count(address.begin(), address.end(), ':');
- size_t dots = std::count(address.begin(), address.end(), '.');
- std::string port_str;
- if (address[0] == '[') {
- // [::1]:123
- if (address.rfind("]:") == std::string::npos) {
- *error = StringPrintf("bad IPv6 address '%s'", address.c_str());
- return false;
- }
- *host = address.substr(1, (address.find("]:") - 1));
- port_str = address.substr(address.rfind("]:") + 2);
- saw_port = true;
- } else if (dots == 0 && colons >= 2 && colons <= 7) {
- // ::1
- *host = address;
- } else if (colons <= 1) {
- // 1.2.3.4 or some.accidental.domain.com
- ipv6 = false;
- std::vector<std::string> pieces = Split(address, ":");
- *host = pieces[0];
- if (pieces.size() > 1) {
- port_str = pieces[1];
- saw_port = true;
- }
- }
-
- if (host->empty()) {
- *error = StringPrintf("no host in '%s'", address.c_str());
- return false;
- }
-
- if (saw_port) {
- if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 ||
- *port > 65535) {
- *error = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(),
- address.c_str());
- return false;
- }
- }
-
- if (canonical_address != nullptr) {
- *canonical_address =
- StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
- }
-
- return true;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/parsenetaddress_test.cpp b/base/parsenetaddress_test.cpp
deleted file mode 100644
index a3bfac8..0000000
--- a/base/parsenetaddress_test.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2016 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/parsenetaddress.h"
-
-#include <gtest/gtest.h>
-
-using android::base::ParseNetAddress;
-
-TEST(ParseNetAddressTest, TestUrl) {
- std::string canonical, host, error;
- int port = 123;
-
- EXPECT_TRUE(
- ParseNetAddress("www.google.com", &host, &port, &canonical, &error));
- EXPECT_EQ("www.google.com:123", canonical);
- EXPECT_EQ("www.google.com", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(
- ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error));
- EXPECT_EQ("www.google.com:666", canonical);
- EXPECT_EQ("www.google.com", host);
- EXPECT_EQ(666, port);
-}
-
-TEST(ParseNetAddressTest, TestIpv4) {
- std::string canonical, host, error;
- int port = 123;
-
- EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error));
- EXPECT_EQ("1.2.3.4:123", canonical);
- EXPECT_EQ("1.2.3.4", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error));
- EXPECT_EQ("1.2.3.4:666", canonical);
- EXPECT_EQ("1.2.3.4", host);
- EXPECT_EQ(666, port);
-}
-
-TEST(ParseNetAddressTest, TestIpv6) {
- std::string canonical, host, error;
- int port = 123;
-
- EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error));
- EXPECT_EQ("[::1]:123", canonical);
- EXPECT_EQ("::1", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port,
- &canonical, &error));
- EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical);
- EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error));
- EXPECT_EQ("[::1]:666", canonical);
- EXPECT_EQ("::1", host);
- EXPECT_EQ(666, port);
-
- EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port,
- &canonical, &error));
- EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical);
- EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
- EXPECT_EQ(666, port);
-}
-
-TEST(ParseNetAddressTest, TestInvalidAddress) {
- std::string canonical, host;
- int port;
-
- std::string failure_cases[] = {
- // Invalid IPv4.
- "1.2.3.4:",
- "1.2.3.4::",
- ":123",
-
- // Invalid IPv6.
- ":1",
- "::::::::1",
- "[::1",
- "[::1]",
- "[::1]:",
- "[::1]::",
-
- // Invalid port.
- "1.2.3.4:-1",
- "1.2.3.4:0",
- "1.2.3.4:65536"
- "1.2.3.4:hello",
- "[::1]:-1",
- "[::1]:0",
- "[::1]:65536",
- "[::1]:hello",
- };
-
- for (const auto& address : failure_cases) {
- // Failure should give some non-empty error string.
- std::string error;
- EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error));
- EXPECT_NE("", error);
- }
-}
-
-// Null canonical address argument.
-TEST(ParseNetAddressTest, TestNullCanonicalAddress) {
- std::string host, error;
- int port = 42;
-
- EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error));
- EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error));
- EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error));
-}
diff --git a/base/process.cpp b/base/process.cpp
deleted file mode 100644
index b8cabf6..0000000
--- a/base/process.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/process.h"
-
-namespace android {
-namespace base {
-
-void AllPids::PidIterator::Increment() {
- if (!dir_) {
- return;
- }
-
- dirent* de;
- while ((de = readdir(dir_.get())) != nullptr) {
- pid_t pid = atoi(de->d_name);
- if (pid != 0) {
- pid_ = pid;
- return;
- }
- }
- pid_ = -1;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/process_test.cpp b/base/process_test.cpp
deleted file mode 100644
index 056f667..0000000
--- a/base/process_test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/process.h"
-
-#include <unistd.h>
-
-#include <gtest/gtest.h>
-
-TEST(process, find_ourselves) {
-#if defined(__linux__)
- bool found_our_pid = false;
- for (const auto& pid : android::base::AllPids{}) {
- if (pid == getpid()) {
- found_our_pid = true;
- }
- }
-
- EXPECT_TRUE(found_our_pid);
-
-#endif
-}
diff --git a/base/properties.cpp b/base/properties.cpp
deleted file mode 100644
index 5c9ec7e..0000000
--- a/base/properties.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/properties.h"
-
-#if defined(__BIONIC__)
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/system_properties.h>
-#include <sys/_system_properties.h>
-#endif
-
-#include <algorithm>
-#include <chrono>
-#include <limits>
-#include <map>
-#include <string>
-
-#include <android-base/parsebool.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-
-namespace android {
-namespace base {
-
-bool GetBoolProperty(const std::string& key, bool default_value) {
- switch (ParseBool(GetProperty(key, ""))) {
- case ParseBoolResult::kError:
- return default_value;
- case ParseBoolResult::kFalse:
- return false;
- case ParseBoolResult::kTrue:
- return true;
- }
- __builtin_unreachable();
-}
-
-template <typename T>
-T GetIntProperty(const std::string& key, T default_value, T min, T max) {
- T result;
- std::string value = GetProperty(key, "");
- if (!value.empty() && android::base::ParseInt(value, &result, min, max)) return result;
- return default_value;
-}
-
-template <typename T>
-T GetUintProperty(const std::string& key, T default_value, T max) {
- T result;
- std::string value = GetProperty(key, "");
- if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
- return default_value;
-}
-
-template int8_t GetIntProperty(const std::string&, int8_t, int8_t, int8_t);
-template int16_t GetIntProperty(const std::string&, int16_t, int16_t, int16_t);
-template int32_t GetIntProperty(const std::string&, int32_t, int32_t, int32_t);
-template int64_t GetIntProperty(const std::string&, int64_t, int64_t, int64_t);
-
-template uint8_t GetUintProperty(const std::string&, uint8_t, uint8_t);
-template uint16_t GetUintProperty(const std::string&, uint16_t, uint16_t);
-template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
-template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
-
-#if !defined(__BIONIC__)
-static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
-static int __system_property_set(const char* key, const char* value) {
- g_properties[key] = value;
- return 0;
-}
-#endif
-
-std::string GetProperty(const std::string& key, const std::string& default_value) {
- std::string property_value;
-#if defined(__BIONIC__)
- const prop_info* pi = __system_property_find(key.c_str());
- if (pi == nullptr) return default_value;
-
- __system_property_read_callback(pi,
- [](void* cookie, const char*, const char* value, unsigned) {
- auto property_value = reinterpret_cast<std::string*>(cookie);
- *property_value = value;
- },
- &property_value);
-#else
- auto it = g_properties.find(key);
- if (it == g_properties.end()) return default_value;
- property_value = it->second;
-#endif
- // If the property exists but is empty, also return the default value.
- // Since we can't remove system properties, "empty" is traditionally
- // the same as "missing" (this was true for cutils' property_get).
- return property_value.empty() ? default_value : property_value;
-}
-
-bool SetProperty(const std::string& key, const std::string& value) {
- return (__system_property_set(key.c_str(), value.c_str()) == 0);
-}
-
-#if defined(__BIONIC__)
-
-struct WaitForPropertyData {
- bool done;
- const std::string* expected_value;
- unsigned last_read_serial;
-};
-
-static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) {
- WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr);
- if (*data->expected_value == value) {
- data->done = true;
- } else {
- data->last_read_serial = serial;
- }
-}
-
-// TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
- auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
- auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
- ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max());
- ts.tv_nsec = ns.count();
-}
-
-using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
-
-static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
- const AbsTime& start_time) {
- auto now = std::chrono::steady_clock::now();
- auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- if (time_elapsed >= relative_timeout) {
- ts = { 0, 0 };
- } else {
- auto remaining_timeout = relative_timeout - time_elapsed;
- DurationToTimeSpec(ts, remaining_timeout);
- }
-}
-
-// Waits for the system property `key` to be created.
-// Times out after `relative_timeout`.
-// Sets absolute_timeout which represents absolute time for the timeout.
-// Returns nullptr on timeout.
-static const prop_info* WaitForPropertyCreation(const std::string& key,
- const std::chrono::milliseconds& relative_timeout,
- const AbsTime& start_time) {
- // Find the property's prop_info*.
- const prop_info* pi;
- unsigned global_serial = 0;
- while ((pi = __system_property_find(key.c_str())) == nullptr) {
- // The property doesn't even exist yet.
- // Wait for a global change and then look again.
- timespec ts;
- UpdateTimeSpec(ts, relative_timeout, start_time);
- if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
- }
- return pi;
-}
-
-bool WaitForProperty(const std::string& key, const std::string& expected_value,
- std::chrono::milliseconds relative_timeout) {
- auto start_time = std::chrono::steady_clock::now();
- const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
- if (pi == nullptr) return false;
-
- WaitForPropertyData data;
- data.expected_value = &expected_value;
- data.done = false;
- while (true) {
- timespec ts;
- // Check whether the property has the value we're looking for?
- __system_property_read_callback(pi, WaitForPropertyCallback, &data);
- if (data.done) return true;
-
- // It didn't, so wait for the property to change before checking again.
- UpdateTimeSpec(ts, relative_timeout, start_time);
- uint32_t unused;
- if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
- }
-}
-
-bool WaitForPropertyCreation(const std::string& key,
- std::chrono::milliseconds relative_timeout) {
- auto start_time = std::chrono::steady_clock::now();
- return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
-}
-
-CachedProperty::CachedProperty(const char* property_name)
- : property_name_(property_name),
- prop_info_(nullptr),
- cached_area_serial_(0),
- cached_property_serial_(0),
- is_read_only_(android::base::StartsWith(property_name, "ro.")),
- read_only_property_(nullptr) {
- static_assert(sizeof(cached_value_) == PROP_VALUE_MAX);
-}
-
-const char* CachedProperty::Get(bool* changed) {
- std::optional<uint32_t> initial_property_serial_ = cached_property_serial_;
-
- // Do we have a `struct prop_info` yet?
- if (prop_info_ == nullptr) {
- // `__system_property_find` is expensive, so only retry if a property
- // has been created since last time we checked.
- uint32_t property_area_serial = __system_property_area_serial();
- if (property_area_serial != cached_area_serial_) {
- prop_info_ = __system_property_find(property_name_.c_str());
- cached_area_serial_ = property_area_serial;
- }
- }
-
- if (prop_info_ != nullptr) {
- // Only bother re-reading the property if it's actually changed since last time.
- uint32_t property_serial = __system_property_serial(prop_info_);
- if (property_serial != cached_property_serial_) {
- __system_property_read_callback(
- prop_info_,
- [](void* data, const char*, const char* value, uint32_t serial) {
- CachedProperty* instance = reinterpret_cast<CachedProperty*>(data);
- instance->cached_property_serial_ = serial;
- // Read only properties can be larger than PROP_VALUE_MAX, but also never change value
- // or location, thus we return the pointer from the shared memory directly.
- if (instance->is_read_only_) {
- instance->read_only_property_ = value;
- } else {
- strlcpy(instance->cached_value_, value, PROP_VALUE_MAX);
- }
- },
- this);
- }
- }
-
- if (changed) {
- *changed = cached_property_serial_ != initial_property_serial_;
- }
-
- if (is_read_only_) {
- return read_only_property_;
- } else {
- return cached_value_;
- }
-}
-
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
deleted file mode 100644
index c30c41e..0000000
--- a/base/properties_test.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/properties.h"
-
-#include <gtest/gtest.h>
-
-#include <atomic>
-#include <chrono>
-#include <string>
-#include <thread>
-
-#if !defined(_WIN32)
-using namespace std::literals;
-#endif
-
-TEST(properties, smoke) {
- android::base::SetProperty("debug.libbase.property_test", "hello");
-
- std::string s = android::base::GetProperty("debug.libbase.property_test", "");
- ASSERT_EQ("hello", s);
-
- android::base::SetProperty("debug.libbase.property_test", "world");
- s = android::base::GetProperty("debug.libbase.property_test", "");
- ASSERT_EQ("world", s);
-
- s = android::base::GetProperty("this.property.does.not.exist", "");
- ASSERT_EQ("", s);
-
- s = android::base::GetProperty("this.property.does.not.exist", "default");
- ASSERT_EQ("default", s);
-}
-
-TEST(properties, empty) {
- // Because you can't delete a property, people "delete" them by
- // setting them to the empty string. In that case we'd want to
- // keep the default value (like cutils' property_get did).
- android::base::SetProperty("debug.libbase.property_test", "");
- std::string s = android::base::GetProperty("debug.libbase.property_test", "default");
- ASSERT_EQ("default", s);
-}
-
-static void CheckGetBoolProperty(bool expected, const std::string& value, bool default_value) {
- android::base::SetProperty("debug.libbase.property_test", value.c_str());
- ASSERT_EQ(expected, android::base::GetBoolProperty("debug.libbase.property_test", default_value));
-}
-
-TEST(properties, GetBoolProperty_true) {
- CheckGetBoolProperty(true, "1", false);
- CheckGetBoolProperty(true, "y", false);
- CheckGetBoolProperty(true, "yes", false);
- CheckGetBoolProperty(true, "on", false);
- CheckGetBoolProperty(true, "true", false);
-}
-
-TEST(properties, GetBoolProperty_false) {
- CheckGetBoolProperty(false, "0", true);
- CheckGetBoolProperty(false, "n", true);
- CheckGetBoolProperty(false, "no", true);
- CheckGetBoolProperty(false, "off", true);
- CheckGetBoolProperty(false, "false", true);
-}
-
-TEST(properties, GetBoolProperty_default) {
- CheckGetBoolProperty(true, "burp", true);
- CheckGetBoolProperty(false, "burp", false);
-}
-
-template <typename T> void CheckGetIntProperty() {
- // Positive and negative.
- android::base::SetProperty("debug.libbase.property_test", "-12");
- EXPECT_EQ(T(-12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
-
- // Default value.
- android::base::SetProperty("debug.libbase.property_test", "");
- EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
-
- // Bounds checks.
- android::base::SetProperty("debug.libbase.property_test", "0");
- EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
- android::base::SetProperty("debug.libbase.property_test", "1");
- EXPECT_EQ(T(1), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
- android::base::SetProperty("debug.libbase.property_test", "2");
- EXPECT_EQ(T(2), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
- android::base::SetProperty("debug.libbase.property_test", "3");
- EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
-}
-
-template <typename T> void CheckGetUintProperty() {
- // Positive.
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
-
- // Default value.
- android::base::SetProperty("debug.libbase.property_test", "");
- EXPECT_EQ(T(45), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
-
- // Bounds checks.
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 33, 22));
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(5), android::base::GetUintProperty<T>("debug.libbase.property_test", 5, 10));
-}
-
-TEST(properties, GetIntProperty_int8_t) { CheckGetIntProperty<int8_t>(); }
-TEST(properties, GetIntProperty_int16_t) { CheckGetIntProperty<int16_t>(); }
-TEST(properties, GetIntProperty_int32_t) { CheckGetIntProperty<int32_t>(); }
-TEST(properties, GetIntProperty_int64_t) { CheckGetIntProperty<int64_t>(); }
-
-TEST(properties, GetUintProperty_uint8_t) { CheckGetUintProperty<uint8_t>(); }
-TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
-TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
-TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
-
-TEST(properties, WaitForProperty) {
-#if defined(__BIONIC__)
- std::atomic<bool> flag{false};
- std::thread thread([&]() {
- std::this_thread::sleep_for(100ms);
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
- while (!flag) std::this_thread::yield();
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
- });
-
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
- flag = true;
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForProperty_timeout) {
-#if defined(__BIONIC__)
- auto t0 = std::chrono::steady_clock::now();
- ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
- 200ms));
- auto t1 = std::chrono::steady_clock::now();
-
- ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
- // Upper bounds on timing are inherently flaky, but let's try...
- ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForProperty_MaxTimeout) {
-#if defined(__BIONIC__)
- std::atomic<bool> flag{false};
- std::thread thread([&]() {
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
- while (!flag) std::this_thread::yield();
- std::this_thread::sleep_for(500ms);
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
- });
-
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
- flag = true;
- // Test that this does not immediately return false due to overflow issues with the timeout.
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForProperty_NegativeTimeout) {
-#if defined(__BIONIC__)
- std::atomic<bool> flag{false};
- std::thread thread([&]() {
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
- while (!flag) std::this_thread::yield();
- std::this_thread::sleep_for(500ms);
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
- });
-
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
- flag = true;
- // Assert that this immediately returns with a negative timeout
- ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForPropertyCreation) {
-#if defined(__BIONIC__)
- std::thread thread([&]() {
- std::this_thread::sleep_for(100ms);
- android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
- });
-
- ASSERT_TRUE(android::base::WaitForPropertyCreation(
- "debug.libbase.WaitForPropertyCreation_test", 1s));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForPropertyCreation_timeout) {
-#if defined(__BIONIC__)
- auto t0 = std::chrono::steady_clock::now();
- ASSERT_FALSE(android::base::WaitForPropertyCreation(
- "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
- auto t1 = std::chrono::steady_clock::now();
-
- ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
- // Upper bounds on timing are inherently flaky, but let's try...
- ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, CachedProperty) {
-#if defined(__BIONIC__)
- android::base::CachedProperty cached_property("debug.libbase.CachedProperty_test");
- bool changed;
- cached_property.Get(&changed);
-
- android::base::SetProperty("debug.libbase.CachedProperty_test", "foo");
- ASSERT_STREQ("foo", cached_property.Get(&changed));
- ASSERT_TRUE(changed);
-
- ASSERT_STREQ("foo", cached_property.Get(&changed));
- ASSERT_FALSE(changed);
-
- android::base::SetProperty("debug.libbase.CachedProperty_test", "bar");
- ASSERT_STREQ("bar", cached_property.Get(&changed));
- ASSERT_TRUE(changed);
-
- ASSERT_STREQ("bar", cached_property.Get(&changed));
- ASSERT_FALSE(changed);
-
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
diff --git a/base/result_test.cpp b/base/result_test.cpp
deleted file mode 100644
index c0ac0fd..0000000
--- a/base/result_test.cpp
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/result.h"
-
-#include "errno.h"
-
-#include <istream>
-#include <string>
-
-#include <gtest/gtest.h>
-
-using namespace std::string_literals;
-
-namespace android {
-namespace base {
-
-TEST(result, result_accessors) {
- Result<std::string> result = "success";
- ASSERT_RESULT_OK(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("success", *result);
- EXPECT_EQ("success", result.value());
-
- EXPECT_EQ('s', result->data()[0]);
-}
-
-TEST(result, result_accessors_rvalue) {
- ASSERT_TRUE(Result<std::string>("success").ok());
- ASSERT_TRUE(Result<std::string>("success").has_value());
-
- EXPECT_EQ("success", *Result<std::string>("success"));
- EXPECT_EQ("success", Result<std::string>("success").value());
-
- EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
-}
-
-TEST(result, result_void) {
- Result<void> ok = {};
- EXPECT_RESULT_OK(ok);
- ok.value(); // should not crash
- ASSERT_DEATH(ok.error(), "");
-
- Result<void> fail = Error() << "failure" << 1;
- EXPECT_FALSE(fail.ok());
- EXPECT_EQ("failure1", fail.error().message());
- EXPECT_EQ(0, fail.error().code());
- EXPECT_TRUE(ok != fail);
- ASSERT_DEATH(fail.value(), "");
-
- auto test = [](bool ok) -> Result<void> {
- if (ok) return {};
- else return Error() << "failure" << 1;
- };
- EXPECT_TRUE(test(true).ok());
- EXPECT_FALSE(test(false).ok());
- test(true).value(); // should not crash
- ASSERT_DEATH(test(true).error(), "");
- ASSERT_DEATH(test(false).value(), "");
- EXPECT_EQ("failure1", test(false).error().message());
-}
-
-TEST(result, result_error) {
- Result<void> result = Error() << "failure" << 1;
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error().code());
- EXPECT_EQ("failure1", result.error().message());
-}
-
-TEST(result, result_error_empty) {
- Result<void> result = Error();
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error().code());
- EXPECT_EQ("", result.error().message());
-}
-
-TEST(result, result_error_rvalue) {
- // Error() and ErrnoError() aren't actually used to create a Result<T> object.
- // Under the hood, they are an intermediate class that can be implicitly constructed into a
- // Result<T>. This is needed both to create the ostream and because Error() itself, by
- // definition will not know what the type, T, of the underlying Result<T> object that it would
- // create is.
-
- auto MakeRvalueErrorResult = []() -> Result<void> { return Error() << "failure" << 1; };
- ASSERT_FALSE(MakeRvalueErrorResult().ok());
- ASSERT_FALSE(MakeRvalueErrorResult().has_value());
-
- EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
- EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
-}
-
-TEST(result, result_errno_error) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<void> result = ErrnoError() << "failure" << 1;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error().code());
- EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
-}
-
-TEST(result, result_errno_error_no_text) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<void> result = ErrnoError();
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error().code());
- EXPECT_EQ(strerror(test_errno), result.error().message());
-}
-
-TEST(result, result_error_from_other_result) {
- auto error_text = "test error"s;
- Result<void> result = Error() << error_text;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = result.error();
-
- ASSERT_FALSE(result2.ok());
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result2.error().code());
- EXPECT_EQ(error_text, result2.error().message());
-}
-
-TEST(result, result_error_through_ostream) {
- auto error_text = "test error"s;
- Result<void> result = Error() << error_text;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2.ok());
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result2.error().code());
- EXPECT_EQ(error_text, result2.error().message());
-}
-
-TEST(result, result_errno_error_through_ostream) {
- auto error_text = "test error"s;
- constexpr int test_errno = 6;
- errno = 6;
- Result<void> result = ErrnoError() << error_text;
-
- errno = 0;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2.ok());
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(test_errno, result2.error().code());
- EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
-}
-
-TEST(result, constructor_forwarding) {
- auto result = Result<std::string>(std::in_place, 5, 'a');
-
- ASSERT_RESULT_OK(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("aaaaa", *result);
-}
-
-struct ConstructorTracker {
- static size_t constructor_called;
- static size_t copy_constructor_called;
- static size_t move_constructor_called;
- static size_t copy_assignment_called;
- static size_t move_assignment_called;
-
- template <typename T>
- ConstructorTracker(T&& string) : string(string) {
- ++constructor_called;
- }
-
- ConstructorTracker(const ConstructorTracker& ct) {
- ++copy_constructor_called;
- string = ct.string;
- }
- ConstructorTracker(ConstructorTracker&& ct) noexcept {
- ++move_constructor_called;
- string = std::move(ct.string);
- }
- ConstructorTracker& operator=(const ConstructorTracker& ct) {
- ++copy_assignment_called;
- string = ct.string;
- return *this;
- }
- ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
- ++move_assignment_called;
- string = std::move(ct.string);
- return *this;
- }
-
- std::string string;
-};
-
-size_t ConstructorTracker::constructor_called = 0;
-size_t ConstructorTracker::copy_constructor_called = 0;
-size_t ConstructorTracker::move_constructor_called = 0;
-size_t ConstructorTracker::copy_assignment_called = 0;
-size_t ConstructorTracker::move_assignment_called = 0;
-
-Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
- if (in.empty()) {
- return "literal string";
- }
- if (in == "test2") {
- return ConstructorTracker(in + in + "2");
- }
- ConstructorTracker result(in + " " + in);
- return result;
-};
-
-TEST(result, no_copy_on_return) {
- // If returning parameters that may be used to implicitly construct the type T of Result<T>,
- // then those parameters are forwarded to the construction of Result<T>.
-
- // If returning an prvalue or xvalue, it will be move constructed during the construction of
- // Result<T>.
-
- // This check ensures that that is the case, and particularly that no copy constructors
- // are called.
-
- auto result1 = ReturnConstructorTracker("");
- ASSERT_RESULT_OK(result1);
- EXPECT_EQ("literal string", result1->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result2 = ReturnConstructorTracker("test2");
- ASSERT_RESULT_OK(result2);
- EXPECT_EQ("test2test22", result2->string);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result3 = ReturnConstructorTracker("test3");
- ASSERT_RESULT_OK(result3);
- EXPECT_EQ("test3 test3", result3->string);
- EXPECT_EQ(3U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-}
-
-// Below two tests require that we do not hide the move constructor with our forwarding reference
-// constructor. This is done with by disabling the forwarding reference constructor if its first
-// and only type is Result<T>.
-TEST(result, result_result_with_success) {
- auto return_result_result_with_success = []() -> Result<Result<void>> { return Result<void>(); };
- auto result = return_result_result_with_success();
- ASSERT_RESULT_OK(result);
- ASSERT_RESULT_OK(*result);
-
- auto inner_result = result.value();
- ASSERT_RESULT_OK(inner_result);
-}
-
-TEST(result, result_result_with_failure) {
- auto return_result_result_with_error = []() -> Result<Result<void>> {
- return Result<void>(ResultError("failure string", 6));
- };
- auto result = return_result_result_with_error();
- ASSERT_RESULT_OK(result);
- ASSERT_FALSE(result->ok());
- EXPECT_EQ("failure string", (*result).error().message());
- EXPECT_EQ(6, (*result).error().code());
-}
-
-// This test requires that we disable the forwarding reference constructor if Result<T> is the
-// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
-// construct a Result<T>, then we still need the constructor.
-TEST(result, result_two_parameter_constructor_same_type) {
- struct TestStruct {
- TestStruct(int value) : value_(value) {}
- TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
- int value_;
- };
-
- auto return_test_struct = []() -> Result<TestStruct> {
- return Result<TestStruct>(std::in_place, Result<TestStruct>(std::in_place, 6), 6);
- };
-
- auto result = return_test_struct();
- ASSERT_RESULT_OK(result);
- EXPECT_EQ(36, result->value_);
-}
-
-TEST(result, die_on_access_failed_result) {
- Result<std::string> result = Error();
- ASSERT_DEATH(*result, "");
-}
-
-TEST(result, die_on_get_error_succesful_result) {
- Result<std::string> result = "success";
- ASSERT_DEATH(result.error(), "");
-}
-
-template <class CharT>
-std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
- errno = 2;
- return ss;
-}
-
-TEST(result, preserve_errno) {
- errno = 1;
- int old_errno = errno;
- Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
- ASSERT_FALSE(result.ok());
- EXPECT_EQ(old_errno, errno);
-
- errno = 1;
- old_errno = errno;
- Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
- ASSERT_FALSE(result2.ok());
- EXPECT_EQ(old_errno, errno);
- EXPECT_EQ(old_errno, result2.error().code());
-}
-
-TEST(result, error_with_fmt) {
- Result<int> result = Errorf("{} {}!", "hello", "world");
- EXPECT_EQ("hello world!", result.error().message());
-
- result = Errorf("{} {}!", std::string("hello"), std::string("world"));
- EXPECT_EQ("hello world!", result.error().message());
-
- result = Errorf("{1} {0}!", "world", "hello");
- EXPECT_EQ("hello world!", result.error().message());
-
- result = Errorf("hello world!");
- EXPECT_EQ("hello world!", result.error().message());
-
- Result<int> result2 = Errorf("error occurred with {}", result.error());
- EXPECT_EQ("error occurred with hello world!", result2.error().message());
-
- constexpr int test_errno = 6;
- errno = test_errno;
- result = ErrnoErrorf("{} {}!", "hello", "world");
- EXPECT_EQ(test_errno, result.error().code());
- EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
-}
-
-TEST(result, error_with_fmt_carries_errno) {
- constexpr int inner_errno = 6;
- errno = inner_errno;
- Result<int> inner_result = ErrnoErrorf("inner failure");
- errno = 0;
- EXPECT_EQ(inner_errno, inner_result.error().code());
-
- // outer_result is created with Errorf, but its error code is got from inner_result.
- Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
- EXPECT_EQ(inner_errno, outer_result.error().code());
- EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
- outer_result.error().message());
-
- // now both result objects are created with ErrnoErrorf. errno from the inner_result
- // is not passed to outer_result.
- constexpr int outer_errno = 10;
- errno = outer_errno;
- outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
- EXPECT_EQ(outer_errno, outer_result.error().code());
- EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
- strerror(outer_errno),
- outer_result.error().message());
-}
-
-TEST(result, errno_chaining_multiple) {
- constexpr int errno1 = 6;
- errno = errno1;
- Result<int> inner1 = ErrnoErrorf("error1");
-
- constexpr int errno2 = 10;
- errno = errno2;
- Result<int> inner2 = ErrnoErrorf("error2");
-
- // takes the error code of inner2 since its the last one.
- Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
- EXPECT_EQ(errno2, outer.error().code());
- EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
- outer.error().message());
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
deleted file mode 100644
index 9236d7b..0000000
--- a/base/scopeguard_test.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/scopeguard.h"
-
-#include <utility>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-TEST(scopeguard, normal) {
- bool guarded_var = true;
- {
- auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
- }
- ASSERT_FALSE(guarded_var);
-}
-
-TEST(scopeguard, disabled) {
- bool guarded_var = true;
- {
- auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
- scopeguard.Disable();
- }
- ASSERT_TRUE(guarded_var);
-}
-
-TEST(scopeguard, moved) {
- int guarded_var = true;
- auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
- { decltype(scopeguard) new_guard(std::move(scopeguard)); }
- EXPECT_FALSE(scopeguard.active());
- ASSERT_FALSE(guarded_var);
-}
-
-TEST(scopeguard, vector) {
- int guarded_var = 0;
- {
- std::vector<android::base::ScopeGuard<std::function<void()>>> scopeguards;
- scopeguards.emplace_back(android::base::make_scope_guard(
- std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
- scopeguards.emplace_back(android::base::make_scope_guard(
- std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
- }
- ASSERT_EQ(guarded_var, 2);
-}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
deleted file mode 100644
index e83ab13..0000000
--- a/base/stringprintf.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2011 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/stringprintf.h"
-
-#include <stdio.h>
-
-#include <string>
-
-namespace android {
-namespace base {
-
-void StringAppendV(std::string* dst, const char* format, va_list ap) {
- // First try with a small fixed size buffer
- char space[1024] __attribute__((__uninitialized__));
-
- // It's possible for methods that use a va_list to invalidate
- // the data in it upon use. The fix is to make a copy
- // of the structure before using it and use that copy instead.
- va_list backup_ap;
- va_copy(backup_ap, ap);
- int result = vsnprintf(space, sizeof(space), format, backup_ap);
- va_end(backup_ap);
-
- if (result < static_cast<int>(sizeof(space))) {
- if (result >= 0) {
- // Normal case -- everything fit.
- dst->append(space, result);
- return;
- }
-
- if (result < 0) {
- // Just an error.
- return;
- }
- }
-
- // Increase the buffer size to the size requested by vsnprintf,
- // plus one for the closing \0.
- int length = result + 1;
- char* buf = new char[length];
-
- // Restore the va_list before we use it again
- va_copy(backup_ap, ap);
- result = vsnprintf(buf, length, format, backup_ap);
- va_end(backup_ap);
-
- if (result >= 0 && result < length) {
- // It fit
- dst->append(buf, result);
- }
- delete[] buf;
-}
-
-std::string StringPrintf(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string result;
- StringAppendV(&result, fmt, ap);
- va_end(ap);
- return result;
-}
-
-void StringAppendF(std::string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
deleted file mode 100644
index fc009b1..0000000
--- a/base/stringprintf_test.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2011 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/stringprintf.h"
-
-#include <gtest/gtest.h>
-
-#include <string>
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
- EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
-}
-
-TEST(StringPrintfTest, StringAppendF) {
- std::string s("a");
- android::base::StringAppendF(&s, "b");
- EXPECT_EQ("ab", s);
-}
-
-TEST(StringPrintfTest, Errno) {
- errno = 123;
- android::base::StringPrintf("hello %s", "world");
- EXPECT_EQ(123, errno);
-}
-
-void TestN(size_t n) {
- char* buf = new char[n + 1];
- memset(buf, 'x', n);
- buf[n] = '\0';
- std::string s(android::base::StringPrintf("%s", buf));
- EXPECT_EQ(buf, s);
- delete[] buf;
-}
-
-TEST(StringPrintfTest, At1023) {
- TestN(1023);
-}
-
-TEST(StringPrintfTest, At1024) {
- TestN(1024);
-}
-
-TEST(StringPrintfTest, At1025) {
- TestN(1025);
-}
diff --git a/base/strings.cpp b/base/strings.cpp
deleted file mode 100644
index 40b2bf2..0000000
--- a/base/strings.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/strings.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <vector>
-
-namespace android {
-namespace base {
-
-#define CHECK_NE(a, b) \
- if ((a) == (b)) abort();
-
-std::vector<std::string> Split(const std::string& s,
- const std::string& delimiters) {
- CHECK_NE(delimiters.size(), 0U);
-
- std::vector<std::string> result;
-
- size_t base = 0;
- size_t found;
- while (true) {
- found = s.find_first_of(delimiters, base);
- result.push_back(s.substr(base, found - base));
- if (found == s.npos) break;
- base = found + 1;
- }
-
- return result;
-}
-
-std::string Trim(const std::string& s) {
- std::string result;
-
- if (s.size() == 0) {
- return result;
- }
-
- size_t start_index = 0;
- size_t end_index = s.size() - 1;
-
- // Skip initial whitespace.
- while (start_index < s.size()) {
- if (!isspace(s[start_index])) {
- break;
- }
- start_index++;
- }
-
- // Skip terminating whitespace.
- while (end_index >= start_index) {
- if (!isspace(s[end_index])) {
- break;
- }
- end_index--;
- }
-
- // All spaces, no beef.
- if (end_index < start_index) {
- return "";
- }
- // Start_index is the first non-space, end_index is the last one.
- return s.substr(start_index, end_index - start_index + 1);
-}
-
-// These cases are probably the norm, so we mark them extern in the header to
-// aid compile time and binary size.
-template std::string Join(const std::vector<std::string>&, char);
-template std::string Join(const std::vector<const char*>&, char);
-template std::string Join(const std::vector<std::string>&, const std::string&);
-template std::string Join(const std::vector<const char*>&, const std::string&);
-
-bool StartsWith(std::string_view s, std::string_view prefix) {
- return s.substr(0, prefix.size()) == prefix;
-}
-
-bool StartsWith(std::string_view s, char prefix) {
- return !s.empty() && s.front() == prefix;
-}
-
-bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
- return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
-}
-
-bool EndsWith(std::string_view s, std::string_view suffix) {
- return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
-}
-
-bool EndsWith(std::string_view s, char suffix) {
- return !s.empty() && s.back() == suffix;
-}
-
-bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
- return s.size() >= suffix.size() &&
- strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
-}
-
-bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
- return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
-}
-
-std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
- bool all) {
- if (from.empty()) return std::string(s);
-
- std::string result;
- std::string_view::size_type start_pos = 0;
- do {
- std::string_view::size_type pos = s.find(from, start_pos);
- if (pos == std::string_view::npos) break;
-
- result.append(s.data() + start_pos, pos - start_pos);
- result.append(to.data(), to.size());
-
- start_pos = pos + from.size();
- } while (all);
- result.append(s.data() + start_pos, s.size() - start_pos);
- return result;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
deleted file mode 100644
index 5ae3094..0000000
--- a/base/strings_test.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/strings.h"
-
-#include <gtest/gtest.h>
-
-#include <string>
-#include <vector>
-#include <set>
-#include <unordered_set>
-
-TEST(strings, split_empty) {
- std::vector<std::string> parts = android::base::Split("", ",");
- ASSERT_EQ(1U, parts.size());
- ASSERT_EQ("", parts[0]);
-}
-
-TEST(strings, split_single) {
- std::vector<std::string> parts = android::base::Split("foo", ",");
- ASSERT_EQ(1U, parts.size());
- ASSERT_EQ("foo", parts[0]);
-}
-
-TEST(strings, split_simple) {
- std::vector<std::string> parts = android::base::Split("foo,bar,baz", ",");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
- ASSERT_EQ("baz", parts[2]);
-}
-
-TEST(strings, split_with_empty_part) {
- std::vector<std::string> parts = android::base::Split("foo,,bar", ",");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("", parts[1]);
- ASSERT_EQ("bar", parts[2]);
-}
-
-TEST(strings, split_with_trailing_empty_part) {
- std::vector<std::string> parts = android::base::Split("foo,bar,", ",");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
- ASSERT_EQ("", parts[2]);
-}
-
-TEST(strings, split_null_char) {
- std::vector<std::string> parts =
- android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
- ASSERT_EQ(2U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
-}
-
-TEST(strings, split_any) {
- std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
- ASSERT_EQ("baz", parts[2]);
-}
-
-TEST(strings, split_any_with_empty_part) {
- std::vector<std::string> parts = android::base::Split("foo:,bar", ",:");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("", parts[1]);
- ASSERT_EQ("bar", parts[2]);
-}
-
-TEST(strings, trim_empty) {
- ASSERT_EQ("", android::base::Trim(""));
-}
-
-TEST(strings, trim_already_trimmed) {
- ASSERT_EQ("foo", android::base::Trim("foo"));
-}
-
-TEST(strings, trim_left) {
- ASSERT_EQ("foo", android::base::Trim(" foo"));
-}
-
-TEST(strings, trim_right) {
- ASSERT_EQ("foo", android::base::Trim("foo "));
-}
-
-TEST(strings, trim_both) {
- ASSERT_EQ("foo", android::base::Trim(" foo "));
-}
-
-TEST(strings, trim_no_trim_middle) {
- ASSERT_EQ("foo bar", android::base::Trim("foo bar"));
-}
-
-TEST(strings, trim_other_whitespace) {
- ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f"));
-}
-
-TEST(strings, join_nothing) {
- std::vector<std::string> list = {};
- ASSERT_EQ("", android::base::Join(list, ','));
-}
-
-TEST(strings, join_single) {
- std::vector<std::string> list = {"foo"};
- ASSERT_EQ("foo", android::base::Join(list, ','));
-}
-
-TEST(strings, join_simple) {
- std::vector<std::string> list = {"foo", "bar", "baz"};
- ASSERT_EQ("foo,bar,baz", android::base::Join(list, ','));
-}
-
-TEST(strings, join_separator_in_vector) {
- std::vector<std::string> list = {",", ","};
- ASSERT_EQ(",,,", android::base::Join(list, ','));
-}
-
-TEST(strings, join_simple_ints) {
- std::set<int> list = {1, 2, 3};
- ASSERT_EQ("1,2,3", android::base::Join(list, ','));
-}
-
-TEST(strings, join_unordered_set) {
- std::unordered_set<int> list = {1, 2};
- ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
- "2,1" == android::base::Join(list, ','));
-}
-
-TEST(strings, StartsWith_empty) {
- ASSERT_FALSE(android::base::StartsWith("", "foo"));
- ASSERT_TRUE(android::base::StartsWith("", ""));
-}
-
-TEST(strings, StartsWithIgnoreCase_empty) {
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("", "foo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("", ""));
-}
-
-TEST(strings, StartsWith_simple) {
- ASSERT_TRUE(android::base::StartsWith("foo", ""));
- ASSERT_TRUE(android::base::StartsWith("foo", "f"));
- ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
- ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
-}
-
-TEST(strings, StartsWithIgnoreCase_simple) {
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", ""));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "f"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "F"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Fo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Foo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FoO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOO"));
-}
-
-TEST(strings, StartsWith_prefix_too_long) {
- ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
-}
-
-TEST(strings, StartsWithIgnoreCase_prefix_too_long) {
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "foobar"));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "FOOBAR"));
-}
-
-TEST(strings, StartsWith_contains_prefix) {
- ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
- ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
-}
-
-TEST(strings, StartsWithIgnoreCase_contains_prefix) {
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "oba"));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "OBA"));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "bar"));
- 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("", ""));
-}
-
-TEST(strings, EndsWithIgnoreCase_empty) {
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "foo"));
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "FOO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("", ""));
-}
-
-TEST(strings, EndsWith_simple) {
- ASSERT_TRUE(android::base::EndsWith("foo", ""));
- ASSERT_TRUE(android::base::EndsWith("foo", "o"));
- ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
- ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
-}
-
-TEST(strings, EndsWithIgnoreCase_simple) {
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", ""));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "o"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "O"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Oo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "OO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Foo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FoO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOO"));
-}
-
-TEST(strings, EndsWith_prefix_too_long) {
- ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
-}
-
-TEST(strings, EndsWithIgnoreCase_prefix_too_long) {
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "foobar"));
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "FOOBAR"));
-}
-
-TEST(strings, EndsWith_contains_prefix) {
- ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
- ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
-}
-
-TEST(strings, EndsWithIgnoreCase_contains_prefix) {
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
-}
-
-TEST(strings, StartsWith_std_string) {
- ASSERT_TRUE(android::base::StartsWith("hello", std::string{"hell"}));
- ASSERT_FALSE(android::base::StartsWith("goodbye", std::string{"hell"}));
-}
-
-TEST(strings, StartsWithIgnoreCase_std_string) {
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("HeLlO", std::string{"hell"}));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("GoOdByE", std::string{"hell"}));
-}
-
-TEST(strings, EndsWith_std_string) {
- ASSERT_TRUE(android::base::EndsWith("hello", std::string{"lo"}));
- ASSERT_FALSE(android::base::EndsWith("goodbye", std::string{"lo"}));
-}
-
-TEST(strings, EndsWithIgnoreCase_std_string) {
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("HeLlO", std::string{"lo"}));
- 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"));
- ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
- ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
-}
-
-TEST(strings, ubsan_28729303) {
- android::base::Split("/dev/null", ":");
-}
-
-TEST(strings, ConsumePrefix) {
- std::string_view s{"foo.bar"};
- ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
- ASSERT_EQ("foo.bar", s);
- ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
- ASSERT_EQ("bar", s);
-}
-
-TEST(strings, ConsumeSuffix) {
- std::string_view s{"foo.bar"};
- ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
- ASSERT_EQ("foo.bar", s);
- ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
- ASSERT_EQ("foo", s);
-}
-
-TEST(strings, StringReplace_false) {
- // No change.
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", false));
- ASSERT_EQ("", android::base::StringReplace("", "z", "Z", false));
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", false));
-
- // Equal lengths.
- ASSERT_EQ("Abcabc", android::base::StringReplace("abcabc", "a", "A", false));
- ASSERT_EQ("aBcabc", android::base::StringReplace("abcabc", "b", "B", false));
- ASSERT_EQ("abCabc", android::base::StringReplace("abcabc", "c", "C", false));
-
- // Longer replacement.
- ASSERT_EQ("foobcabc", android::base::StringReplace("abcabc", "a", "foo", false));
- ASSERT_EQ("afoocabc", android::base::StringReplace("abcabc", "b", "foo", false));
- ASSERT_EQ("abfooabc", android::base::StringReplace("abcabc", "c", "foo", false));
-
- // Shorter replacement.
- ASSERT_EQ("xxyz", android::base::StringReplace("abcxyz", "abc", "x", false));
- ASSERT_EQ("axyz", android::base::StringReplace("abcxyz", "bcx", "x", false));
- ASSERT_EQ("abcx", android::base::StringReplace("abcxyz", "xyz", "x", false));
-}
-
-TEST(strings, StringReplace_true) {
- // No change.
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", true));
- ASSERT_EQ("", android::base::StringReplace("", "z", "Z", true));
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", true));
-
- // Equal lengths.
- ASSERT_EQ("AbcAbc", android::base::StringReplace("abcabc", "a", "A", true));
- ASSERT_EQ("aBcaBc", android::base::StringReplace("abcabc", "b", "B", true));
- ASSERT_EQ("abCabC", android::base::StringReplace("abcabc", "c", "C", true));
-
- // Longer replacement.
- ASSERT_EQ("foobcfoobc", android::base::StringReplace("abcabc", "a", "foo", true));
- ASSERT_EQ("afoocafooc", android::base::StringReplace("abcabc", "b", "foo", true));
- ASSERT_EQ("abfooabfoo", android::base::StringReplace("abcabc", "c", "foo", true));
-
- // Shorter replacement.
- ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
- ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
-}
diff --git a/base/test_main.cpp b/base/test_main.cpp
deleted file mode 100644
index 7fa6a84..0000000
--- a/base/test_main.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "android-base/logging.h"
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- android::base::InitLogging(argv, android::base::StderrLogger);
- return RUN_ALL_TESTS();
-}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
deleted file mode 100644
index 36b4cdf..0000000
--- a/base/test_utils.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/test_utils.h"
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
- Start();
-}
-
-CapturedStdFd::~CapturedStdFd() {
- if (old_fd_ != -1) {
- Stop();
- }
-}
-
-int CapturedStdFd::fd() const {
- return temp_file_.fd;
-}
-
-std::string CapturedStdFd::str() {
- std::string result;
- CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
- android::base::ReadFdToString(fd(), &result);
- return result;
-}
-
-void CapturedStdFd::Reset() {
- // Do not reset while capturing.
- CHECK_EQ(-1, old_fd_);
- CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
- CHECK_EQ(0, ftruncate(fd(), 0));
-}
-
-void CapturedStdFd::Start() {
-#if defined(_WIN32)
- // On Windows, stderr is often buffered, so make sure it is unbuffered so
- // that we can immediately read back what was written to stderr.
- if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, nullptr, _IONBF, 0));
-#endif
- old_fd_ = dup(std_fd_);
- CHECK_NE(-1, old_fd_);
- CHECK_NE(-1, dup2(fd(), std_fd_));
-}
-
-void CapturedStdFd::Stop() {
- CHECK_NE(-1, old_fd_);
- CHECK_NE(-1, dup2(old_fd_, std_fd_));
- close(old_fd_);
- old_fd_ = -1;
- // Note: cannot restore prior setvbuf() setting.
-}
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
deleted file mode 100644
index 15a79dd..0000000
--- a/base/test_utils_test.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#include "android-base/test_utils.h"
-
-#include <gtest/gtest-spi.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace base {
-
-TEST(TestUtilsTest, AssertMatch) {
- ASSERT_MATCH("foobar", R"(fo+baz?r)");
- EXPECT_FATAL_FAILURE(ASSERT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, AssertNotMatch) {
- ASSERT_NOT_MATCH("foobar", R"(foobaz)");
- EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, ExpectMatch) {
- EXPECT_MATCH("foobar", R"(fo+baz?r)");
- EXPECT_NONFATAL_FAILURE(EXPECT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, ExpectNotMatch) {
- EXPECT_NOT_MATCH("foobar", R"(foobaz)");
- EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, CaptureStdout_smoke) {
- CapturedStdout cap;
- printf("This should be captured.\n");
- cap.Stop();
- printf("This will not be captured.\n");
- ASSERT_EQ("This should be captured.\n", cap.str());
-
- cap.Start();
- printf("And this text should be captured too.\n");
- cap.Stop();
- ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
-
- printf("Still not going to be captured.\n");
- cap.Reset();
- cap.Start();
- printf("Only this will be captured.\n");
- ASSERT_EQ("Only this will be captured.\n", cap.str());
-}
-
-TEST(TestUtilsTest, CaptureStderr_smoke) {
- CapturedStderr cap;
- fprintf(stderr, "This should be captured.\n");
- cap.Stop();
- fprintf(stderr, "This will not be captured.\n");
- ASSERT_EQ("This should be captured.\n", cap.str());
-
- cap.Start();
- fprintf(stderr, "And this text should be captured too.\n");
- cap.Stop();
- ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
-
- fprintf(stderr, "Still not going to be captured.\n");
- cap.Reset();
- cap.Start();
- fprintf(stderr, "Only this will be captured.\n");
- ASSERT_EQ("Only this will be captured.\n", cap.str());
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/threads.cpp b/base/threads.cpp
deleted file mode 100644
index 48f6197..0000000
--- a/base/threads.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/threads.h>
-
-#include <stdint.h>
-#include <unistd.h>
-
-#if defined(__APPLE__)
-#include <pthread.h>
-#elif defined(__linux__) && !defined(__ANDROID__)
-#include <syscall.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-namespace android {
-namespace base {
-
-uint64_t GetThreadId() {
-#if defined(__BIONIC__)
- return gettid();
-#elif defined(__APPLE__)
- uint64_t tid;
- pthread_threadid_np(NULL, &tid);
- return tid;
-#elif defined(__linux__)
- return syscall(__NR_gettid);
-#elif defined(_WIN32)
- return GetCurrentThreadId();
-#endif
-}
-
-} // namespace base
-} // namespace android
-
-#if defined(__GLIBC__)
-int tgkill(int tgid, int tid, int sig) {
- return syscall(__NR_tgkill, tgid, tid, sig);
-}
-#endif
diff --git a/base/tidy/unique_fd_test.cpp b/base/tidy/unique_fd_test.cpp
deleted file mode 100644
index b3a99fc..0000000
--- a/base/tidy/unique_fd_test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2020 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/unique_fd.h"
-
-#include <utility>
-
-#include <gtest/gtest.h>
-
-extern void consume_unique_fd(android::base::unique_fd fd);
-
-TEST(unique_fd, bugprone_use_after_move) {
- // Compile time test for clang-tidy's bugprone-use-after-move check.
- android::base::unique_fd ufd(open("/dev/null", O_RDONLY | O_CLOEXEC));
- consume_unique_fd(std::move(ufd));
- ufd.reset(open("/dev/null", O_RDONLY | O_CLOEXEC));
- ufd.get();
- consume_unique_fd(std::move(ufd));
-}
diff --git a/base/tidy/unique_fd_test2.cpp b/base/tidy/unique_fd_test2.cpp
deleted file mode 100644
index b0c71e2..0000000
--- a/base/tidy/unique_fd_test2.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2020 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/unique_fd.h"
-
-void consume_unique_fd(android::base::unique_fd) {}
diff --git a/base/utf8.cpp b/base/utf8.cpp
deleted file mode 100644
index adb46d0..0000000
--- a/base/utf8.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <windows.h>
-
-#include "android-base/utf8.h"
-
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <algorithm>
-#include <string>
-
-#include "android-base/logging.h"
-
-namespace android {
-namespace base {
-
-// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
-static void SetErrnoFromLastError() {
- switch (GetLastError()) {
- case ERROR_NO_UNICODE_TRANSLATION:
- errno = EILSEQ;
- break;
- default:
- errno = EINVAL;
- break;
- }
-}
-
-bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
- utf8->clear();
-
- if (size == 0) {
- return true;
- }
-
- // TODO: Consider using std::wstring_convert once libcxx is supported on
- // Windows.
-
- // Only Vista or later has this flag that causes WideCharToMultiByte() to
- // return an error on invalid characters.
- const DWORD flags =
-#if (WINVER >= 0x0600)
- WC_ERR_INVALID_CHARS;
-#else
- 0;
-#endif
-
- const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
- NULL, 0, NULL, NULL);
- if (chars_required <= 0) {
- SetErrnoFromLastError();
- return false;
- }
-
- // This could potentially throw a std::bad_alloc exception.
- utf8->resize(chars_required);
-
- const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
- &(*utf8)[0], chars_required, NULL,
- NULL);
- if (result != chars_required) {
- SetErrnoFromLastError();
- CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
- << " chars to buffer of " << chars_required << " chars";
- utf8->clear();
- return false;
- }
-
- return true;
-}
-
-bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
- // Compute string length of NULL-terminated string with wcslen().
- return WideToUTF8(utf16, wcslen(utf16), utf8);
-}
-
-bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
- // Use the stored length of the string which allows embedded NULL characters
- // to be converted.
- return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
-}
-
-// Internal helper function that takes MultiByteToWideChar() flags.
-static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
- const DWORD flags) {
- utf16->clear();
-
- if (size == 0) {
- return true;
- }
-
- // TODO: Consider using std::wstring_convert once libcxx is supported on
- // Windows.
- const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
- NULL, 0);
- if (chars_required <= 0) {
- SetErrnoFromLastError();
- return false;
- }
-
- // This could potentially throw a std::bad_alloc exception.
- utf16->resize(chars_required);
-
- const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
- &(*utf16)[0], chars_required);
- if (result != chars_required) {
- SetErrnoFromLastError();
- CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
- << " chars to buffer of " << chars_required << " chars";
- utf16->clear();
- return false;
- }
-
- return true;
-}
-
-bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
- // If strictly interpreting as UTF-8 succeeds, return success.
- if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
- return true;
- }
-
- const int saved_errno = errno;
-
- // Fallback to non-strict interpretation, allowing invalid characters and
- // converting as best as possible, and return false to signify a problem.
- (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
- errno = saved_errno;
- return false;
-}
-
-bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
- // Compute string length of NULL-terminated string with strlen().
- return UTF8ToWide(utf8, strlen(utf8), utf16);
-}
-
-bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
- // Use the stored length of the string which allows embedded NULL characters
- // to be converted.
- return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
-}
-
-static bool isDriveLetter(wchar_t c) {
- return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
-}
-
-bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
- if (!UTF8ToWide(utf8, utf16)) {
- return false;
- }
- // Note: Although most Win32 File I/O API are limited to MAX_PATH (260
- // characters), the CreateDirectory API is limited to 248 characters.
- if (utf16->length() >= 248) {
- // If path is of the form "x:\" or "x:/"
- if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
- ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
- // Append long path prefix, and make sure there are no unix-style
- // separators to ensure a fully compliant Win32 long path string.
- utf16->insert(0, LR"(\\?\)");
- std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
- }
- }
- return true;
-}
-
-// Versions of standard library APIs that support UTF-8 strings.
-namespace utf8 {
-
-FILE* fopen(const char* name, const char* mode) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return nullptr;
- }
-
- std::wstring mode_utf16;
- if (!UTF8ToWide(mode, &mode_utf16)) {
- return nullptr;
- }
-
- return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
-}
-
-int mkdir(const char* name, mode_t) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return -1;
- }
-
- return _wmkdir(name_utf16.c_str());
-}
-
-int open(const char* name, int flags, ...) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return -1;
- }
-
- int mode = 0;
- if ((flags & O_CREAT) != 0) {
- va_list args;
- va_start(args, flags);
- mode = va_arg(args, int);
- va_end(args);
- }
-
- return _wopen(name_utf16.c_str(), flags, mode);
-}
-
-int unlink(const char* name) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return -1;
- }
-
- return _wunlink(name_utf16.c_str());
-}
-
-} // namespace utf8
-} // namespace base
-} // namespace android
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
deleted file mode 100644
index 472e82c..0000000
--- a/base/utf8_test.cpp
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
-* Copyright (C) 2015 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-#include "android-base/utf8.h"
-
-#include <gtest/gtest.h>
-
-#include <fcntl.h>
-#include <stdlib.h>
-
-#include "android-base/file.h"
-#include "android-base/macros.h"
-#include "android-base/unique_fd.h"
-
-namespace android {
-namespace base {
-
-TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
- std::wstring wide;
-
- errno = 0;
-
- // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
- // error. Concatenate two C/C++ literal string constants to prevent the
- // compiler from giving an error about "\xa2af" containing a "hex escape
- // sequence out of range".
- EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
-
- EXPECT_EQ(EILSEQ, errno);
-
- // Even if an invalid character is encountered, UTF8ToWide() should still do
- // its best to convert the rest of the string. sysdeps_win32.cpp:
- // _console_write_utf8() depends on this behavior.
- //
- // Thus, we verify that the valid characters are converted, but we ignore the
- // specific replacement character that UTF8ToWide() may replace the invalid
- // UTF-8 characters with because we want to allow that to change if the
- // implementation changes.
- EXPECT_EQ(0U, wide.find(L"before"));
- const wchar_t after_wide[] = L"after";
- EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
-}
-
-// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
-
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// The tests below from utf_string_conversions_unittest.cc check for this
-// preprocessor symbol, so define it, as it is appropriate for Windows.
-#define WCHAR_T_IS_UTF16
-static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
-
-// The tests below from utf_string_conversions_unittest.cc call versions of
-// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
-// stub implementations with that signature. These are just for testing and
-// should not be moved to base because they assert/expect no errors which is
-// probably not a good idea (or at least it is something that should be left
-// up to the caller, not a base library).
-
-static std::wstring UTF8ToWide(const std::string& utf8) {
- std::wstring utf16;
- EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
- return utf16;
-}
-
-static std::string WideToUTF8(const std::wstring& utf16) {
- std::string utf8;
- EXPECT_TRUE(WideToUTF8(utf16, &utf8));
- return utf8;
-}
-
-namespace {
-
-const wchar_t* const kConvertRoundtripCases[] = {
- L"Google Video",
- // "网页 图片 资讯更多 »"
- L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
- // "Παγκόσμιος Ιστός"
- L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
- L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
- // "Поиск страниц на русском"
- L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
- L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
- L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
- // "전체서비스"
- L"\xc804\xccb4\xc11c\xbe44\xc2a4",
-
- // Test characters that take more than 16 bits. This will depend on whether
- // wchar_t is 16 or 32 bits.
-#if defined(WCHAR_T_IS_UTF16)
- L"\xd800\xdf00",
- // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
- L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
-#elif defined(WCHAR_T_IS_UTF32)
- L"\x10300",
- // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
- L"\x11d40\x11d41\x11d42\x11d43\x11d44",
-#endif
-};
-
-} // namespace
-
-TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
- // we round-trip all the wide strings through UTF-8 to make sure everything
- // agrees on the conversion. This uses the stream operators to test them
- // simultaneously.
- for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
- std::ostringstream utf8;
- utf8 << WideToUTF8(kConvertRoundtripCases[i]);
- std::wostringstream wide;
- wide << UTF8ToWide(utf8.str());
-
- EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
- }
-}
-
-TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
- // An empty std::wstring should be converted to an empty std::string,
- // and vice versa.
- std::wstring wempty;
- std::string empty;
- EXPECT_EQ(empty, WideToUTF8(wempty));
- EXPECT_EQ(wempty, UTF8ToWide(empty));
-}
-
-TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
- struct UTF8ToWideCase {
- const char* utf8;
- const wchar_t* wide;
- bool success;
- } convert_cases[] = {
- // Regular UTF-8 input.
- {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
- // Non-character is passed through.
- {"\xef\xbf\xbfHello", L"\xffffHello", true},
- // Truncated UTF-8 sequence.
- {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
- // Truncated off the end.
- {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
- // Non-shortest-form UTF-8.
- {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
- // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
- // Note that for whatever reason, this test fails on Windows XP.
- {"\xed\xb0\x80", L"\xfffd", false},
- // Non-BMP characters. The second is a non-character regarded as valid.
- // The result will either be in UTF-16 or UTF-32.
-#if defined(WCHAR_T_IS_UTF16)
- {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
- {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
-#elif defined(WCHAR_T_IS_UTF32)
- {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
- {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
-#endif
- };
-
- for (size_t i = 0; i < arraysize(convert_cases); i++) {
- std::wstring converted;
- errno = 0;
- const bool success = UTF8ToWide(convert_cases[i].utf8,
- strlen(convert_cases[i].utf8),
- &converted);
- EXPECT_EQ(convert_cases[i].success, success);
- // The original test always compared expected and converted, but don't do
- // that because our implementation of UTF8ToWide() does not guarantee to
- // produce the same output in error situations.
- if (success) {
- std::wstring expected(convert_cases[i].wide);
- EXPECT_EQ(expected, converted);
- } else {
- EXPECT_EQ(EILSEQ, errno);
- }
- }
-
- // Manually test an embedded NULL.
- std::wstring converted;
- EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
- ASSERT_EQ(3U, converted.length());
- EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
- EXPECT_EQ('Z', converted[1]);
- EXPECT_EQ('\t', converted[2]);
-
- // Make sure that conversion replaces, not appends.
- EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
- ASSERT_EQ(1U, converted.length());
- EXPECT_EQ('B', converted[0]);
-}
-
-#if defined(WCHAR_T_IS_UTF16)
-// This test is only valid when wchar_t == UTF-16.
-TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
- struct WideToUTF8Case {
- const wchar_t* utf16;
- const char* utf8;
- bool success;
- } convert_cases[] = {
- // Regular UTF-16 input.
- {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
- // Test a non-BMP character.
- {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
- // Non-characters are passed through.
- {L"\xffffHello", "\xEF\xBF\xBFHello", true},
- {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
- // The first character is a truncated UTF-16 character.
- // Note that for whatever reason, this test fails on Windows XP.
- {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
-#if (WINVER >= 0x0600)
- // Only Vista and later has a new API/flag that correctly returns false.
- false
-#else
- true
-#endif
- },
- // Truncated at the end.
- // Note that for whatever reason, this test fails on Windows XP.
- {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
-#if (WINVER >= 0x0600)
- // Only Vista and later has a new API/flag that correctly returns false.
- false
-#else
- true
-#endif
- },
- };
-
- for (size_t i = 0; i < arraysize(convert_cases); i++) {
- std::string converted;
- errno = 0;
- const bool success = WideToUTF8(convert_cases[i].utf16,
- wcslen(convert_cases[i].utf16),
- &converted);
- EXPECT_EQ(convert_cases[i].success, success);
- // The original test always compared expected and converted, but don't do
- // that because our implementation of WideToUTF8() does not guarantee to
- // produce the same output in error situations.
- if (success) {
- std::string expected(convert_cases[i].utf8);
- EXPECT_EQ(expected, converted);
- } else {
- EXPECT_EQ(EILSEQ, errno);
- }
- }
-}
-
-#elif defined(WCHAR_T_IS_UTF32)
-// This test is only valid when wchar_t == UTF-32.
-TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
- struct WideToUTF8Case {
- const wchar_t* utf32;
- const char* utf8;
- bool success;
- } convert_cases[] = {
- // Regular 16-bit input.
- {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
- // Test a non-BMP character.
- {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
- // Non-characters are passed through.
- {L"\xffffHello", "\xEF\xBF\xBFHello", true},
- {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
- // Invalid Unicode code points.
- {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
- // The first character is a truncated UTF-16 character.
- {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
- {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
- };
-
- for (size_t i = 0; i < arraysize(convert_cases); i++) {
- std::string converted;
- EXPECT_EQ(convert_cases[i].success,
- WideToUTF8(convert_cases[i].utf32,
- wcslen(convert_cases[i].utf32),
- &converted));
- std::string expected(convert_cases[i].utf8);
- EXPECT_EQ(expected, converted);
- }
-}
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// The test below uses these types and functions, so just do enough to get the
-// test running.
-typedef wchar_t char16;
-typedef std::wstring string16;
-
-template<typename T>
-static void* WriteInto(T* t, size_t size) {
- // std::(w)string::resize() already includes space for a NULL terminator.
- t->resize(size - 1);
- return &(*t)[0];
-}
-
-// A stub implementation that calls a helper from above, just to get the test
-// below working. This is just for testing and should not be moved to base
-// because this ignores errors which is probably not a good idea, plus it takes
-// a string16 type which we don't really have.
-static std::string UTF16ToUTF8(const string16& utf16) {
- return WideToUTF8(utf16);
-}
-
-TEST(UTFStringConversionsTest, ConvertMultiString) {
- static char16 multi16[] = {
- 'f', 'o', 'o', '\0',
- 'b', 'a', 'r', '\0',
- 'b', 'a', 'z', '\0',
- '\0'
- };
- static char multi[] = {
- 'f', 'o', 'o', '\0',
- 'b', 'a', 'r', '\0',
- 'b', 'a', 'z', '\0',
- '\0'
- };
- string16 multistring16;
- memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
- sizeof(multi16));
- EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
- std::string expected;
- memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
- EXPECT_EQ(arraysize(multi) - 1, expected.length());
- const std::string& converted = UTF16ToUTF8(multistring16);
- EXPECT_EQ(arraysize(multi) - 1, converted.length());
- EXPECT_EQ(expected, converted);
-}
-
-// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
-// and SysUTF8ToWide(), so these are stub implementations that call the helpers
-// above. These are just for testing and should not be moved to base because
-// they ignore errors which is probably not a good idea.
-
-static std::string SysWideToUTF8(const std::wstring& utf16) {
- return WideToUTF8(utf16);
-}
-
-static std::wstring SysUTF8ToWide(const std::string& utf8) {
- return UTF8ToWide(utf8);
-}
-
-// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
-
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifdef WCHAR_T_IS_UTF32
-static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
-#else
-static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
-#endif
-
-TEST(SysStrings, SysWideToUTF8) {
- EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
- EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
-
- // >16 bits
- EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
-
- // Error case. When Windows finds a UTF-16 character going off the end of
- // a string, it just converts that literal value to UTF-8, even though this
- // is invalid.
- //
- // This is what XP does, but Vista has different behavior, so we don't bother
- // verifying it:
- // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
- // SysWideToUTF8(L"\x4f60\xd800zyxw"));
-
- // Test embedded NULLs.
- std::wstring wide_null(L"a");
- wide_null.push_back(0);
- wide_null.push_back('b');
-
- std::string expected_null("a");
- expected_null.push_back(0);
- expected_null.push_back('b');
-
- EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
-}
-
-TEST(SysStrings, SysUTF8ToWide) {
- EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
- EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
- // >16 bits
- EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
-
- // Error case. When Windows finds an invalid UTF-8 character, it just skips
- // it. This seems weird because it's inconsistent with the reverse conversion.
- //
- // This is what XP does, but Vista has different behavior, so we don't bother
- // verifying it:
- // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
-
- // Test embedded NULLs.
- std::string utf8_null("a");
- utf8_null.push_back(0);
- utf8_null.push_back('b');
-
- std::wstring expected_null(L"a");
- expected_null.push_back(0);
- expected_null.push_back('b');
-
- EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
-}
-
-TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
- std::string utf8 = "c:\\mypath\\myfile.txt";
-
- std::wstring wide;
- EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
-
- EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
-}
-
-TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
- std::string utf8 = "c:\\mypath";
- while (utf8.length() < 300 /* MAX_PATH is 260 */) {
- utf8 += "\\mypathsegment";
- }
-
- std::wstring wide;
- EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
-
- EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
- EXPECT_EQ(std::string::npos, wide.find(L"/"));
-}
-
-TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
- std::string utf8 = "c:/mypath";
- while (utf8.length() < 300 /* MAX_PATH is 260 */) {
- utf8 += "/mypathsegment";
- }
-
- std::wstring wide;
- EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
-
- EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
- EXPECT_EQ(std::string::npos, wide.find(L"/"));
-}
-
-namespace utf8 {
-
-TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
- TemporaryDir td;
-
- // Create long directory path
- std::string utf8 = td.path;
- while (utf8.length() < 300 /* MAX_PATH is 260 */) {
- utf8 += "\\mypathsegment";
- EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
- }
-
- // Create file
- utf8 += "\\test-file.bin";
- int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
- int mode = 0666;
- android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
- EXPECT_NE(-1, fd.get());
-
- // Close file
- fd.reset();
- EXPECT_EQ(-1, fd.get());
-
- // Open file with fopen
- FILE* file = fopen(utf8.c_str(), "rb");
- EXPECT_NE(nullptr, file);
-
- if (file) {
- fclose(file);
- }
-
- // Delete file
- EXPECT_EQ(0, unlink(utf8.c_str()));
-}
-
-} // namespace utf8
-} // namespace base
-} // namespace android
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d67b522..31c2d5d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -169,6 +169,7 @@
"libdebuggerd/backtrace.cpp",
"libdebuggerd/gwp_asan.cpp",
"libdebuggerd/open_files_list.cpp",
+ "libdebuggerd/scudo.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/utility.cpp",
],
@@ -176,8 +177,13 @@
local_include_dirs: ["libdebuggerd/include"],
export_include_dirs: ["libdebuggerd/include"],
- // Needed for private/bionic_fdsan.h
- include_dirs: ["bionic/libc"],
+ include_dirs: [
+ // Needed for private/bionic_fdsan.h
+ "bionic/libc",
+
+ // Needed for scudo/interface.h
+ "external/scudo/standalone/include",
+ ],
header_libs: [
"bionic_libc_platform_headers",
"gwp_asan_headers",
@@ -192,7 +198,10 @@
"liblog",
],
- whole_static_libs: ["gwp_asan_crash_handler"],
+ whole_static_libs: [
+ "gwp_asan_crash_handler",
+ "libscudo",
+ ],
target: {
recovery: {
@@ -206,6 +215,9 @@
debuggable: {
cflags: ["-DROOT_POSSIBLE"],
},
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
},
}
@@ -256,6 +268,10 @@
"gwp_asan_headers",
],
+ include_dirs: [
+ "external/scudo/standalone/include",
+ ],
+
local_include_dirs: [
"libdebuggerd",
],
@@ -271,6 +287,12 @@
},
test_suites: ["device-tests"],
+
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
}
cc_benchmark {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 0cd2350..d7cb972 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -289,6 +289,8 @@
process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address;
process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;
process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
+ process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
+ process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
FALLTHROUGH_INTENDED;
case 1:
case 2:
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 054f836..9d7658e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -31,6 +31,9 @@
#include <android/fdsan.h>
#include <android/set_abort_message.h>
+#include <bionic/malloc.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <android-base/cmsg.h>
@@ -331,6 +334,184 @@
R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
}
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+static void SetTagCheckingLevelSync() {
+ int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (tagged_addr_ctrl < 0) {
+ abort();
+ }
+
+ tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_SYNC;
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) != 0) {
+ abort();
+ }
+
+ HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
+ if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) {
+ abort();
+ }
+}
+#endif
+
+TEST_F(CrasherTest, mte_uaf) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ free((void *)p);
+ p[0] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a 16-byte allocation.*
+
+allocated by thread .*
+ #00 pc)");
+ ASSERT_MATCH(result, R"(deallocated by thread .*
+ #00 pc)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_overflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ p[4] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation.*
+
+allocated by thread .*
+ #00 pc)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_underflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ p[-1] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a 16-byte allocation.*
+
+allocated by thread .*
+ #00 pc)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_multiple_causes) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+
+ // Make two allocations with the same tag and close to one another. Check for both properties
+ // with a bounds check -- this relies on the fact that only if the allocations have the same tag
+ // would they be measured as closer than 128 bytes to each other. Otherwise they would be about
+ // (some non-zero value << 56) apart.
+ //
+ // The out-of-bounds access will be considered either an overflow of one or an underflow of the
+ // other.
+ std::set<uintptr_t> allocs;
+ for (int i = 0; i != 4096; ++i) {
+ uintptr_t alloc = reinterpret_cast<uintptr_t>(malloc(16));
+ auto it = allocs.insert(alloc).first;
+ if (it != allocs.begin() && *std::prev(it) + 128 > alloc) {
+ *reinterpret_cast<int*>(*std::prev(it) + 16) = 42;
+ }
+ if (std::next(it) != allocs.end() && alloc + 128 > *std::next(it)) {
+ *reinterpret_cast<int*>(alloc + 16) = 42;
+ }
+ }
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+ ASSERT_MATCH(
+ result,
+ R"(Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.)");
+
+ // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
+ // overflows), so we can't match explicitly for an underflow message.
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
TEST_F(CrasherTest, LD_PRELOAD) {
int intercept_result;
unique_fd output_fd;
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 6650294..254ed4f 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -40,6 +40,8 @@
void* fdsan_table;
const gwp_asan::AllocatorState* gwp_asan_state;
const gwp_asan::AllocationMetadata* gwp_asan_metadata;
+ const char* scudo_stack_depot;
+ const char* scudo_region_info;
};
// These callbacks are called in a signal handler, and thus must be async signal safe.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
new file mode 100644
index 0000000..4d00ece
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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 "types.h"
+#include "utility.h"
+
+#include <memory.h>
+
+#include "scudo/interface.h"
+
+class ScudoCrashData {
+ public:
+ ScudoCrashData() = delete;
+ ~ScudoCrashData() = default;
+ ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
+
+ bool CrashIsMine() const;
+
+ void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ private:
+ scudo_error_info error_info_ = {};
+ uintptr_t untagged_fault_addr_;
+
+ void DumpReport(const scudo_error_report* report, log_t* log,
+ unwindstack::Unwinder* unwinder) const;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 35c3fd6..04c4b5c 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -41,6 +41,8 @@
uintptr_t fdsan_table_address = 0;
uintptr_t gwp_asan_state = 0;
uintptr_t gwp_asan_metadata = 0;
+ uintptr_t scudo_stack_depot = 0;
+ uintptr_t scudo_region_info = 0;
bool has_fault_address = false;
uintptr_t fault_address = 0;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
new file mode 100644
index 0000000..f8bfe07
--- /dev/null
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2020 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 "libdebuggerd/scudo.h"
+#include "libdebuggerd/gwp_asan.h"
+
+#include "unwindstack/Memory.h"
+#include "unwindstack/Unwinder.h"
+
+#include <bionic/macros.h>
+
+std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
+ size_t size) {
+ auto buf = std::make_unique<char[]>(size);
+ if (!process_memory->ReadFully(addr, buf.get(), size)) {
+ return std::unique_ptr<char[]>();
+ }
+ return buf;
+}
+
+static const uintptr_t kTagGranuleSize = 16;
+
+ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
+ const ProcessInfo& process_info) {
+ if (!process_info.has_fault_address) {
+ return;
+ }
+
+ auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+ __scudo_get_stack_depot_size());
+ auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
+ __scudo_get_region_info_size());
+
+ untagged_fault_addr_ = untag_address(process_info.fault_address);
+ uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
+
+ uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+ if (memory_begin > fault_page) {
+ return;
+ }
+
+ uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+ if (memory_end < fault_page) {
+ return;
+ }
+
+ auto memory = std::make_unique<char[]>(memory_end - memory_begin);
+ for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
+ process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+ }
+
+ auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
+ for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
+ memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
+ }
+
+ __scudo_get_error_info(&error_info_, process_info.fault_address, stack_depot.get(),
+ region_info.get(), memory.get(), memory_tags.get(), memory_begin,
+ memory_end - memory_begin);
+}
+
+bool ScudoCrashData::CrashIsMine() const {
+ return error_info_.reports[0].error_type != UNKNOWN;
+}
+
+void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
+ if (error_info_.reports[1].error_type != UNKNOWN) {
+ _LOG(log, logtype::HEADER,
+ "\nNote: multiple potential causes for this crash were detected, listing them in "
+ "decreasing order of probability.\n");
+ }
+
+ size_t report_num = 0;
+ while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
+ error_info_.reports[report_num].error_type != UNKNOWN) {
+ DumpReport(&error_info_.reports[report_num++], log, unwinder);
+ }
+}
+
+void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
+ unwindstack::Unwinder* unwinder) const {
+ const char *error_type_str;
+ switch (report->error_type) {
+ case USE_AFTER_FREE:
+ error_type_str = "Use After Free";
+ break;
+ case BUFFER_OVERFLOW:
+ error_type_str = "Buffer Overflow";
+ break;
+ case BUFFER_UNDERFLOW:
+ error_type_str = "Buffer Underflow";
+ break;
+ default:
+ error_type_str = "Unknown";
+ break;
+ }
+
+ uintptr_t diff;
+ const char* location_str;
+
+ if (untagged_fault_addr_ < report->allocation_address) {
+ // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+ location_str = "left of";
+ diff = report->allocation_address - untagged_fault_addr_;
+ } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
+ // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+ location_str = "into";
+ diff = untagged_fault_addr_ - report->allocation_address;
+ } else {
+ // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
+ location_str = "right of";
+ diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
+ }
+
+ // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+ const char* byte_suffix = "s";
+ if (diff == 1) {
+ byte_suffix = "";
+ }
+ _LOG(log, logtype::HEADER,
+ "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_type_str, diff, byte_suffix, location_str, report->allocation_size,
+ report->allocation_address);
+
+ if (report->allocation_trace[0]) {
+ _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < 64 && report->allocation_trace[i]; ++i) {
+ unwindstack::FrameData frame_data =
+ unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
+ frame_data.num = i;
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+ }
+
+ if (report->deallocation_trace[0]) {
+ _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < 64 && report->deallocation_trace[i]; ++i) {
+ unwindstack::FrameData frame_data =
+ unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
+ frame_data.num = i;
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+ }
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d6b2e25..ab65dd1 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -56,6 +56,7 @@
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/gwp_asan.h"
#include "libdebuggerd/open_files_list.h"
+#include "libdebuggerd/scudo.h"
#include "libdebuggerd/utility.h"
#include "gwp_asan/common.h"
@@ -389,14 +390,17 @@
}
std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
+ std::unique_ptr<ScudoCrashData> scudo_crash_data;
if (primary_thread) {
gwp_asan_crash_data = std::make_unique<GwpAsanCrashData>(unwinder->GetProcessMemory().get(),
process_info, thread_info);
+ scudo_crash_data =
+ std::make_unique<ScudoCrashData>(unwinder->GetProcessMemory().get(), process_info);
}
if (primary_thread && gwp_asan_crash_data->CrashIsMine()) {
gwp_asan_crash_data->DumpCause(log);
- } else if (thread_info.siginfo) {
+ } else if (thread_info.siginfo && !(primary_thread && scudo_crash_data->CrashIsMine())) {
dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
thread_info.registers.get());
}
@@ -427,6 +431,8 @@
gwp_asan_crash_data->DumpAllocationTrace(log, unwinder);
}
+ scudo_crash_data->DumpCause(log, unwinder);
+
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 3bf28b6..c8a3431 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,6 +35,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <debuggerd/handler.h>
#include <log/log.h>
@@ -374,6 +375,12 @@
return "SEGV_ADIDERR";
case SEGV_ADIPERR:
return "SEGV_ADIPERR";
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+ case SEGV_MTEAERR:
+ return "SEGV_MTEAERR";
+ case SEGV_MTESERR:
+ return "SEGV_MTESERR";
+#endif
}
static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
break;
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index e85660c..53a76ea 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -95,6 +95,8 @@
uintptr_t fdsan_table_address;
uintptr_t gwp_asan_state;
uintptr_t gwp_asan_metadata;
+ uintptr_t scudo_stack_depot;
+ uintptr_t scudo_region_info;
};
struct __attribute__((__packed__)) CrashInfo {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 3a2deb7..bdb786c 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -117,8 +117,10 @@
"device/main.cpp",
"device/usb.cpp",
"device/usb_client.cpp",
+ "device/tcp_client.cpp",
"device/utility.cpp",
"device/variables.cpp",
+ "socket.cpp",
],
shared_libs: [
@@ -143,12 +145,14 @@
],
static_libs: [
+ "libgtest_prod",
"libhealthhalutils",
"libsnapshot_nobinder",
"update_metadata-protos",
],
header_libs: [
+ "avb_headers",
"libsnapshot_headers",
]
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index bb085c5..1b0859f 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -19,6 +19,7 @@
#include <algorithm>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware/boot/1.0/IBootControl.h>
#include <android/hardware/fastboot/1.0/IFastboot.h>
@@ -28,6 +29,7 @@
#include "constants.h"
#include "flashing.h"
+#include "tcp_client.h"
#include "usb_client.h"
using android::fs_mgr::EnsurePathUnmounted;
@@ -60,11 +62,16 @@
{FB_CMD_GSI, GsiHandler},
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
}),
- transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()),
health_hal_(get_health_service()),
fastboot_hal_(IFastboot::getService()),
active_slot_("") {
+ if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") {
+ transport_ = std::make_unique<ClientTcpTransport>();
+ } else {
+ transport_ = std::make_unique<ClientUsbTransport>();
+ }
+
if (boot_control_hal_) {
boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
}
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index fd6ff8e..1bf4c9c 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -31,6 +31,7 @@
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
+#include <libavb/libavb.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <libsnapshot/snapshot.h>
@@ -122,6 +123,27 @@
}
}
+static void CopyAVBFooter(std::vector<char>* data, const uint64_t block_device_size) {
+ if (data->size() < AVB_FOOTER_SIZE) {
+ return;
+ }
+ std::string footer;
+ uint64_t footer_offset = data->size() - AVB_FOOTER_SIZE;
+ for (int idx = 0; idx < AVB_FOOTER_MAGIC_LEN; idx++) {
+ footer.push_back(data->at(footer_offset + idx));
+ }
+ if (0 != footer.compare(AVB_FOOTER_MAGIC)) {
+ return;
+ }
+
+ // copy AVB footer from end of data to end of block device
+ uint64_t original_data_size = data->size();
+ data->resize(block_device_size, 0);
+ for (int idx = 0; idx < AVB_FOOTER_SIZE; idx++) {
+ data->at(block_device_size - 1 - idx) = data->at(original_data_size - 1 - idx);
+ }
+}
+
int Flash(FastbootDevice* device, const std::string& partition_name) {
PartitionHandle handle;
if (!OpenPartition(device, partition_name, &handle)) {
@@ -131,8 +153,14 @@
std::vector<char> data = std::move(device->download_data());
if (data.size() == 0) {
return -EINVAL;
- } else if (data.size() > get_block_device_size(handle.fd())) {
+ }
+ uint64_t block_device_size = get_block_device_size(handle.fd());
+ if (data.size() > block_device_size) {
return -EOVERFLOW;
+ } else if (data.size() < block_device_size &&
+ (partition_name == "boot" || partition_name == "boot_a" ||
+ partition_name == "boot_b")) {
+ CopyAVBFooter(&data, block_device_size);
}
WipeOverlayfsForPartition(device, partition_name);
int result = FlashBlockDevice(handle.fd(), data);
diff --git a/fastboot/device/tcp_client.cpp b/fastboot/device/tcp_client.cpp
new file mode 100644
index 0000000..ec5e1e3
--- /dev/null
+++ b/fastboot/device/tcp_client.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2020 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 "tcp_client.h"
+#include "constants.h"
+
+#include <android-base/errors.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+static constexpr int kDefaultPort = 5554;
+static constexpr int kProtocolVersion = 1;
+static constexpr int kHandshakeTimeoutMs = 2000;
+static constexpr size_t kHandshakeLength = 4;
+
+// Extract the big-endian 8-byte message length into a 64-bit number.
+static uint64_t ExtractMessageLength(const void* buffer) {
+ uint64_t ret = 0;
+ for (int i = 0; i < 8; ++i) {
+ ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);
+ }
+ return ret;
+}
+
+// Encode the 64-bit number into a big-endian 8-byte message length.
+static void EncodeMessageLength(uint64_t length, void* buffer) {
+ for (int i = 0; i < 8; ++i) {
+ reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);
+ }
+}
+
+ClientTcpTransport::ClientTcpTransport() {
+ service_ = Socket::NewServer(Socket::Protocol::kTcp, kDefaultPort);
+
+ // A workaround to notify recovery to continue its work.
+ android::base::SetProperty("sys.usb.ffs.ready", "1");
+}
+
+ssize_t ClientTcpTransport::Read(void* data, size_t len) {
+ if (len > SSIZE_MAX) {
+ return -1;
+ }
+
+ size_t total_read = 0;
+ do {
+ // Read a new message
+ while (message_bytes_left_ == 0) {
+ if (socket_ == nullptr) {
+ ListenFastbootSocket();
+ }
+
+ char buffer[8];
+ if (socket_->ReceiveAll(buffer, 8, 0) == 8) {
+ message_bytes_left_ = ExtractMessageLength(buffer);
+ } else {
+ // If connection is closed by host, Receive will return 0 immediately.
+ socket_.reset(nullptr);
+ // In DATA phase, return error.
+ if (downloading_) {
+ return -1;
+ }
+ }
+ }
+
+ size_t read_length = len - total_read;
+ if (read_length > message_bytes_left_) {
+ read_length = message_bytes_left_;
+ }
+ ssize_t bytes_read =
+ socket_->ReceiveAll(reinterpret_cast<char*>(data) + total_read, read_length, 0);
+ if (bytes_read == -1) {
+ socket_.reset(nullptr);
+ return -1;
+ } else {
+ message_bytes_left_ -= bytes_read;
+ total_read += bytes_read;
+ }
+ // There are more than one DATA phases if the downloading buffer is too
+ // large, like a very big system image. All of data phases should be
+ // received until the whole buffer is filled in that case.
+ } while (downloading_ && total_read < len);
+
+ return total_read;
+}
+
+ssize_t ClientTcpTransport::Write(const void* data, size_t len) {
+ if (socket_ == nullptr || len > SSIZE_MAX) {
+ return -1;
+ }
+
+ // Use multi-buffer writes for better performance.
+ char header[8];
+ EncodeMessageLength(len, header);
+
+ if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, len}})) {
+ socket_.reset(nullptr);
+ return -1;
+ }
+
+ // In DATA phase
+ if (android::base::StartsWith(reinterpret_cast<const char*>(data), RESPONSE_DATA)) {
+ downloading_ = true;
+ } else {
+ downloading_ = false;
+ }
+
+ return len;
+}
+
+int ClientTcpTransport::Close() {
+ if (socket_ == nullptr) {
+ return -1;
+ }
+ socket_.reset(nullptr);
+
+ return 0;
+}
+
+int ClientTcpTransport::Reset() {
+ return Close();
+}
+
+void ClientTcpTransport::ListenFastbootSocket() {
+ while (true) {
+ socket_ = service_->Accept();
+
+ // Handshake
+ char buffer[kHandshakeLength + 1];
+ buffer[kHandshakeLength] = '\0';
+ if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) !=
+ kHandshakeLength) {
+ PLOG(ERROR) << "No Handshake message received";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ if (memcmp(buffer, "FB", 2) != 0) {
+ PLOG(ERROR) << "Unrecognized initialization message";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ int version = 0;
+ if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
+ LOG(ERROR) << "Unknown TCP protocol version " << buffer + 2
+ << ", our version: " << kProtocolVersion;
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion));
+ if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {
+ PLOG(ERROR) << "Failed to send initialization message";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ break;
+ }
+}
diff --git a/fastboot/device/tcp_client.h b/fastboot/device/tcp_client.h
new file mode 100644
index 0000000..32e9834
--- /dev/null
+++ b/fastboot/device/tcp_client.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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 <memory>
+
+#include "socket.h"
+#include "transport.h"
+
+class ClientTcpTransport : public Transport {
+ public:
+ ClientTcpTransport();
+ ~ClientTcpTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+ int Reset() override;
+
+ private:
+ void ListenFastbootSocket();
+
+ std::unique_ptr<Socket> service_;
+ std::unique_ptr<Socket> socket_;
+ uint64_t message_bytes_left_ = 0;
+ bool downloading_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientTcpTransport);
+};
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index e56ffcf..5a14b63 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -54,7 +54,9 @@
while (total < length) {
ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);
- if (bytes == -1) {
+ // Returns 0 only when the peer has disconnected because our requested length is not 0. So
+ // we return immediately to avoid dead loop here.
+ if (bytes <= 0) {
if (total == 0) {
return -1;
}
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 676f446..6cd0430 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -14,6 +14,9 @@
},
{
"name": "vts_libsnapshot_test"
+ },
+ {
+ "name": "libsnapshot_fuzzer_test"
}
]
}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index d2daaa1..8c2e001 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -454,7 +454,8 @@
<< entry.encryption_options;
return;
}
- if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) != 0) {
+ if ((options.flags &
+ (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) != 0) {
// We can only use this policy on ext4 if the "stable_inodes" feature
// is set on the filesystem, otherwise shrinking will break encrypted files.
if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index ea9c957..9046132 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -52,17 +52,27 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
- const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const CreateLogicalPartitionParams& params,
const LpMetadataBlockDevice& block_device,
const std::string& super_device, std::string* result) {
// If the super device is the source of this block device's metadata,
// make sure we use the correct super device (and not just "super",
// which might not exist.)
std::string name = GetBlockDevicePartitionName(block_device);
- std::string dev_string = opener.GetDeviceString(name);
- if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
- dev_string = opener.GetDeviceString(super_device);
+ if (android::base::StartsWith(name, "dm-")) {
+ // Device-mapper nodes are not normally allowed in LpMetadata, since
+ // they are not consistent across reboots. However for the purposes of
+ // testing it's useful to handle them. For example when running DSUs,
+ // userdata is a device-mapper device, and some stacking will result
+ // when using libfiemap.
+ *result = "/dev/block/" + name;
+ return true;
+ }
+
+ auto opener = params.partition_opener;
+ std::string dev_string = opener->GetDeviceString(name);
+ if (GetMetadataSuperBlockDevice(*params.metadata) == &block_device) {
+ dev_string = opener->GetDeviceString(super_device);
}
// Note: device-mapper will not accept symlinks, so we must use realpath
@@ -93,8 +103,8 @@
case LP_TARGET_TYPE_LINEAR: {
const auto& block_device = params.metadata->block_devices[extent.target_source];
std::string dev_string;
- if (!GetPhysicalPartitionDevicePath(*params.partition_opener, *params.metadata,
- block_device, super_device, &dev_string)) {
+ if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,
+ &dev_string)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0825a77..f333a85 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -408,16 +408,17 @@
return fstab_result;
}
-// Identify path to fstab file. Lookup is based on pattern fstab.<hardware>,
-// fstab.<hardware.platform> in folders /odm/etc, vendor/etc, or /.
+// Identify path to fstab file. Lookup is based on pattern
+// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
+// folders /odm/etc, vendor/etc, or /.
std::string GetFstabPath() {
- for (const char* prop : {"hardware", "hardware.platform"}) {
- std::string hw;
+ for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+ std::string suffix;
- if (!fs_mgr_get_boot_config(prop, &hw)) continue;
+ if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
- std::string fstab_path = prefix + hw;
+ std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
}
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 6717922..3ee742f 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -51,6 +51,7 @@
using android::fs_mgr::GetPartitionName;
static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
+static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test";
std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
auto metadata_dir = "/metadata/gsi/" + dir_prefix;
@@ -135,10 +136,13 @@
return !!FindPartition(*metadata.get(), name);
}
+static bool IsTestDir(const std::string& path) {
+ return android::base::StartsWith(path, kTestImageMetadataDir) ||
+ android::base::StartsWith(path, kOtaTestImageMetadataDir);
+}
+
static bool IsUnreliablePinningAllowed(const std::string& path) {
- return android::base::StartsWith(path, "/data/gsi/dsu/") ||
- android::base::StartsWith(path, "/data/gsi/test/") ||
- android::base::StartsWith(path, "/data/gsi/ota/test/");
+ return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
}
FiemapStatus ImageManager::CreateBackingImage(
@@ -174,8 +178,7 @@
// if device-mapper is stacked in some complex way not supported by
// FiemapWriter.
auto device_path = GetDevicePathForFile(fw.get());
- if (android::base::StartsWith(device_path, "/dev/block/dm-") &&
- !android::base::StartsWith(metadata_dir_, kTestImageMetadataDir)) {
+ if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
fw = {};
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 5388b44..6663391 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -131,132 +131,6 @@
ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
- public:
- ImageTest() : dm_(DeviceMapper::Instance()) {}
-
- void SetUp() override {
- manager_ = ImageManager::Open(kMetadataPath, gDataPath);
- ASSERT_NE(manager_, nullptr);
-
- manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
- ASSERT_NE(submanager_, nullptr);
-
- submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- // Ensure that metadata is cleared in between runs.
- submanager_->RemoveAllImages();
- manager_->RemoveAllImages();
-
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- base_name_ = tinfo->name();
- test_image_name_ = base_name_ + "-base";
- wrapper_device_name_ = base_name_ + "-wrapper";
-
- ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
- ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
- }
-
- void TearDown() override {
- submanager_->UnmapImageDevice(test_image_name_);
- umount(gDataMountPath.c_str());
- dm_.DeleteDeviceIfExists(wrapper_device_name_);
- manager_->UnmapImageDevice(base_name_);
- manager_->DeleteBackingImage(base_name_);
- }
-
- protected:
- bool DoFormat(const std::string& device) {
- // clang-format off
- std::vector<std::string> mkfs_args = {
- "/system/bin/mke2fs",
- "-F",
- "-b 4096",
- "-t ext4",
- "-m 0",
- "-O has_journal",
- device,
- ">/dev/null",
- "2>/dev/null",
- "</dev/null",
- };
- // clang-format on
- auto command = android::base::Join(mkfs_args, " ");
- return system(command.c_str()) == 0;
- }
-
- std::unique_ptr<ImageManager> manager_;
- std::unique_ptr<ImageManager> submanager_;
-
- DeviceMapper& dm_;
- std::string base_name_;
- std::string base_device_;
- std::string test_image_name_;
- std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
- ASSERT_TRUE(DoFormat(base_device_));
- ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148874852";
-#endif
- // Create a simple wrapper around the base device that we'll mount from
- // instead. This will simulate the code paths for dm-crypt/default-key/bow
- // and force us to use device-mapper rather than loop devices.
- uint64_t device_size = 0;
- {
- unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- device_size = get_block_device_size(fd);
- ASSERT_EQ(device_size, kTestImageSize * 16);
- }
- uint64_t num_sectors = device_size / 512;
-
- auto& dm = DeviceMapper::Instance();
-
- DmTable table;
- table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
- ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
- // Format and mount.
- std::string wrapper_device;
- ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
- ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
- ASSERT_TRUE(DoFormat(wrapper_device));
- ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::set<std::string> backing_devices;
- auto init = [&](std::set<std::string> devices) -> bool {
- backing_devices = std::move(devices);
- return true;
- };
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
- ASSERT_TRUE(submanager_->MapAllImages(init));
- ASSERT_FALSE(backing_devices.empty());
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-}
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 4505382..2288674 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -124,6 +124,64 @@
return true;
}
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+ bool found = false;
+ const uint8_t* desc_partition_name;
+ auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
+
+ for (const auto& vbmeta : vbmeta_images) {
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
+ avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ continue;
+ }
+
+ for (size_t n = 0; n < num_descriptors && !found; n++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+ LWARNING << "Descriptor[" << n << "] is invalid";
+ continue;
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
+ desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
+ if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
+ hash_desc.get())) {
+ continue;
+ }
+ if (hash_desc->partition_name_len != partition_name.length()) {
+ continue;
+ }
+ // Notes that desc_partition_name is not NUL-terminated.
+ std::string hash_partition_name((const char*)desc_partition_name,
+ hash_desc->partition_name_len);
+ if (hash_partition_name == partition_name) {
+ found = true;
+ }
+ }
+ }
+
+ if (found) break;
+ }
+
+ if (!found) {
+ LERROR << "Hash descriptor not found: " << partition_name;
+ return nullptr;
+ }
+
+ hash_desc->partition_name = partition_name;
+
+ const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
+ hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
+
+ const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
+ hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
+
+ return hash_desc;
+}
+
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
bool found = false;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 09c786a..e8f7c39 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -40,6 +40,9 @@
std::string GetAvbPropertyDescriptor(const std::string& key,
const std::vector<VBMetaData>& vbmeta_images);
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
// AvbHashtreeDescriptor to dm-verity table setup.
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
index f82f83d..1c14cc0 100644
--- a/fs_mgr/libfs_avb/fs_avb_util.cpp
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -74,5 +74,15 @@
return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
}
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hash descriptor.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+ VBMetaData&& vbmeta) {
+ if (!vbmeta.size()) return nullptr;
+
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+ return GetHashDescriptor(avb_partition_name, vbmeta_images);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
index ec8badb..3f37bd7 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -32,9 +32,20 @@
std::string* out_avb_partition_name,
VBMetaVerifyResult* out_verify_result);
+// Loads the single vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+ const std::string& image_path, const std::string& partition_name,
+ const std::string& expected_public_key_blob, bool allow_verification_error,
+ bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+ bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);
+
// Gets the hashtree descriptor for avb_partition_name from the vbmeta.
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& avb_partition_name, VBMetaData&& vbmeta);
+// Gets the hash descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+ VBMetaData&& vbmeta);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
index bd638e6..f2aa7cc 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/types.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -55,6 +55,12 @@
std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
+struct FsAvbHashDescriptor : AvbHashDescriptor {
+ std::string partition_name;
+ std::string salt;
+ std::string digest;
+};
+
struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
std::string partition_name;
std::string salt;
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index dc3b985..c37d70e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -19,6 +19,7 @@
#include <string.h>
#include <algorithm>
+#include <limits>
#include <android-base/unique_fd.h>
@@ -369,7 +370,10 @@
}
// Align the metadata size up to the nearest sector.
- metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+ if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {
+ LERROR << "Max metadata size " << metadata_max_size << " is too large.";
+ return false;
+ }
// Validate and build the block device list.
uint32_t logical_block_size = 0;
@@ -401,10 +405,15 @@
// 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;
+ bool ok;
if (out.alignment) {
- free_area_start = AlignTo(free_area_start, out.alignment);
+ ok = AlignTo(free_area_start, out.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -441,10 +450,15 @@
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
+ bool ok;
if (super.alignment || super.alignment_offset) {
- free_area_start = AlignTo(free_area_start, super.alignment);
+ ok = AlignTo(free_area_start, super.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -544,7 +558,11 @@
const Interval& current = extents[i];
DCHECK(previous.device_index == current.device_index);
- uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
+ uint64_t aligned;
+ if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {
+ LERROR << "Sector " << previous.end << " caused integer overflow.";
+ continue;
+ }
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
@@ -730,7 +748,10 @@
// Choose an aligned sector for the midpoint. This could lead to one half
// being slightly larger than the other, but this will not restrict the
// size of partitions (it might lead to one extra extent if "B" overflows).
- midpoint = AlignSector(super, midpoint);
+ if (!AlignSector(super, midpoint, &midpoint)) {
+ LERROR << "Unexpected integer overflow aligning midpoint " << midpoint;
+ return free_list;
+ }
std::vector<Interval> first_half;
std::vector<Interval> second_half;
@@ -768,7 +789,11 @@
// If the sector ends where the next aligned chunk begins, then there's
// no missing gap to try and allocate.
const auto& block_device = block_devices_[extent->device_index()];
- uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+ uint64_t next_aligned_sector;
+ if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {
+ LERROR << "Integer overflow aligning sector " << extent->end_sector();
+ return nullptr;
+ }
if (extent->end_sector() == next_aligned_sector) {
return nullptr;
}
@@ -925,13 +950,19 @@
return size;
}
-uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
- uint64_t sector) const {
+bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,
+ uint64_t* out) 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, block_device.alignment);
- return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+ if (!AlignTo(lba, block_device.alignment, out)) {
+ return false;
+ }
+ if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {
+ return false;
+ }
+ *out /= LP_SECTOR_SIZE;
+ return true;
}
bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
@@ -1005,7 +1036,12 @@
bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
const std::vector<Interval>& free_region_hint) {
// Align the space needed up to the nearest sector.
- uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
+ uint64_t aligned_size;
+ if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {
+ LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size
+ << " bytes; integer overflow.";
+ return false;
+ }
uint64_t old_size = partition->size();
if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 52a3217..1a3250a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -228,8 +228,9 @@
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
EXPECT_EQ(extent.num_sectors, 80);
+ uint64_t aligned_lba;
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
- uint64_t aligned_lba = AlignTo(lba, device_info.alignment);
+ ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));
EXPECT_EQ(lba, aligned_lba);
}
@@ -1051,3 +1052,17 @@
EXPECT_EQ(e2->physical_sector(), 3072);
EXPECT_EQ(e2->end_sector(), 4197368);
}
+
+TEST_F(BuilderTest, ResizeOverflow) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ Partition* p = builder->AddPartition("system", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index bd39150..89a47b1 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -144,7 +144,6 @@
std::vector<std::unique_ptr<Extent>> extents_;
uint32_t attributes_;
uint64_t size_;
- bool disabled_;
};
// An interval in the metadata. This is similar to a LinearExtent with one difference.
@@ -321,9 +320,6 @@
// Set the LP_HEADER_FLAG_VIRTUAL_AB_DEVICE flag.
void SetVirtualABDeviceFlag();
- // If set, checks for slot suffixes will be ignored internally.
- void IgnoreSlotSuffixing();
-
bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
@@ -359,7 +355,7 @@
bool GrowPartition(Partition* partition, uint64_t aligned_size,
const std::vector<Interval>& free_region_hint);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+ bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) 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;
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index f210eaf..c4fe3ed 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <limits>
#include <string>
#include <string_view>
@@ -66,16 +67,26 @@
void SHA256(const void* data, size_t length, uint8_t out[32]);
// Align |base| such that it is evenly divisible by |alignment|, which does not
-// have to be a power of two.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+// have to be a power of two. Return false on overflow.
+template <typename T>
+bool AlignTo(T base, uint32_t alignment, T* out) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(!std::numeric_limits<T>::is_signed);
if (!alignment) {
- return base;
+ *out = base;
+ return true;
}
- uint64_t remainder = base % alignment;
+ T remainder = base % alignment;
if (remainder == 0) {
- return base;
+ *out = base;
+ return true;
}
- return base + (alignment - remainder);
+ T to_add = alignment - remainder;
+ if (to_add > std::numeric_limits<T>::max() - base) {
+ return false;
+ }
+ *out = base + to_add;
+ return true;
}
// Update names from C++ strings.
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index b64861d..fc90872 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <optional>
+
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -58,15 +60,28 @@
EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
}
+std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) {
+ uint64_t r;
+ if (!AlignTo(base, alignment, &r)) {
+ return {};
+ }
+ return {r};
+}
+
TEST(liblp, AlignTo) {
- EXPECT_EQ(AlignTo(37, 0), 37);
- EXPECT_EQ(AlignTo(1024, 1024), 1024);
- EXPECT_EQ(AlignTo(555, 1024), 1024);
- EXPECT_EQ(AlignTo(555, 1000), 1000);
- EXPECT_EQ(AlignTo(0, 1024), 0);
- EXPECT_EQ(AlignTo(54, 32), 64);
- EXPECT_EQ(AlignTo(32, 32), 32);
- EXPECT_EQ(AlignTo(17, 32), 32);
+ EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37));
+ EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000));
+ EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0));
+ EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64));
+ EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32));
+ EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32));
+
+ auto u32limit = std::numeric_limits<uint32_t>::max();
+ auto u64limit = std::numeric_limits<uint64_t>::max();
+ EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit});
+ EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{});
}
TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index e916693..95301ff 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -246,12 +246,8 @@
gtest: false,
}
-cc_fuzz {
- name: "libsnapshot_fuzzer",
-
- // TODO(b/154633114): make host supported.
- // host_supported: true,
-
+cc_defaults {
+ name: "libsnapshot_fuzzer_defaults",
native_coverage : true,
srcs: [
// Compile the protobuf definition again with type full.
@@ -289,7 +285,12 @@
canonical_path_from_root: false,
local_include_dirs: ["."],
},
+}
+cc_fuzz {
+ name: "libsnapshot_fuzzer",
+ defaults: ["libsnapshot_fuzzer_defaults"],
+ corpus: ["corpus/*"],
fuzz_config: {
cc: ["android-virtual-ab+bugs@google.com"],
componentid: 30545,
@@ -298,3 +299,14 @@
fuzz_on_haiku_device: true,
},
}
+
+cc_test {
+ name: "libsnapshot_fuzzer_test",
+ defaults: ["libsnapshot_fuzzer_defaults"],
+ data: ["corpus/*"],
+ test_suites: [
+ "device-tests",
+ ],
+ auto_gen_config: true,
+ require_root: true,
+}
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
index 91fbb60..a55b42a 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
@@ -64,6 +64,7 @@
bool has_metadata_device_object = 1;
bool metadata_mounted = 2;
}
+ reserved 18 to 9999;
oneof value {
NoArgs begin_update = 1;
NoArgs cancel_update = 2;
@@ -82,6 +83,9 @@
NoArgs dump = 15;
NoArgs ensure_metadata_mounted = 16;
NoArgs get_snapshot_merge_stats_instance = 17;
+
+ // Test directives that has nothing to do with ISnapshotManager API surface.
+ NoArgs switch_slot = 10000;
}
}
@@ -97,7 +101,10 @@
bool is_super_metadata_valid = 3;
chromeos_update_engine.DeltaArchiveManifest super_data = 4;
+ // Whether the directory that mocks /metadata/ota/snapshot is created.
+ bool has_metadata_snapshots_dir = 5;
+
// More data used to prep the test before running actions.
- reserved 5 to 9999;
+ reserved 6 to 9999;
repeated SnapshotManagerActionProto actions = 10000;
}
diff --git a/fs_mgr/libsnapshot/corpus/launch_device.txt b/fs_mgr/libsnapshot/corpus/launch_device.txt
new file mode 100644
index 0000000..55a7f2c
--- /dev/null
+++ b/fs_mgr/libsnapshot/corpus/launch_device.txt
@@ -0,0 +1,161 @@
+device_info_data {
+ slot_suffix_is_a: true
+ is_overlayfs_setup: false
+ allow_set_boot_control_merge_status: true
+ allow_set_slot_as_unbootable: true
+ is_recovery: false
+}
+manager_data {
+ is_local_image_manager: false
+}
+is_super_metadata_valid: true
+super_data {
+ partitions {
+ partition_name: "sys_a"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ partitions {
+ partition_name: "vnd_a"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ partitions {
+ partition_name: "prd_a"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ dynamic_partition_metadata {
+ groups {
+ name: "group_google_dp_a"
+ size: 15728640
+ partition_names: "sys_a"
+ partition_names: "vnd_a"
+ partition_names: "prd_a"
+ }
+ }
+}
+has_metadata_snapshots_dir: true
+actions {
+ begin_update {
+ }
+}
+actions {
+ create_update_snapshots {
+ partitions {
+ partition_name: "sys"
+ new_partition_info {
+ size: 3878912
+ }
+ operations {
+ type: ZERO,
+ dst_extents {
+ start_block: 0
+ num_blocks: 947
+ }
+ }
+ }
+ partitions {
+ partition_name: "vnd"
+ new_partition_info {
+ size: 3878912
+ }
+ operations {
+ type: ZERO,
+ dst_extents {
+ start_block: 0
+ num_blocks: 947
+ }
+ }
+ }
+ partitions {
+ partition_name: "prd"
+ new_partition_info {
+ size: 3878912
+ }
+ operations {
+ type: ZERO,
+ dst_extents {
+ start_block: 0
+ num_blocks: 947
+ }
+ }
+ }
+ dynamic_partition_metadata {
+ groups {
+ name: "group_google_dp"
+ size: 15728640
+ partition_names: "sys"
+ partition_names: "vnd"
+ partition_names: "prd"
+ }
+ }
+ }
+}
+actions {
+ map_update_snapshot {
+ use_correct_super: true
+ has_metadata_slot: true
+ metadata_slot: 1
+ partition_name: "sys_b"
+ force_writable: true
+ timeout_millis: 3000
+ }
+}
+actions {
+ map_update_snapshot {
+ use_correct_super: true
+ has_metadata_slot: true
+ metadata_slot: 1
+ partition_name: "vnd_b"
+ force_writable: true
+ timeout_millis: 3000
+ }
+}
+actions {
+ map_update_snapshot {
+ use_correct_super: true
+ has_metadata_slot: true
+ metadata_slot: 1
+ partition_name: "prd_b"
+ force_writable: true
+ timeout_millis: 3000
+ }
+}
+actions {
+ finished_snapshot_writes: false
+}
+actions {
+ unmap_update_snapshot: "sys_b"
+}
+actions {
+ unmap_update_snapshot: "vnd_b"
+}
+actions {
+ unmap_update_snapshot: "prd_b"
+}
+actions {
+ switch_slot {
+ }
+}
+actions {
+ need_snapshots_in_first_stage_mount {
+ }
+}
+actions {
+ create_logical_and_snapshot_partitions {
+ use_correct_super: true
+ timeout_millis: 5000
+ }
+}
+actions {
+ initiate_merge {
+ }
+}
+actions {
+ process_update_state {
+ }
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
index 2910129..0e57674 100755
--- a/fs_mgr/libsnapshot/fuzz.sh
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -3,7 +3,8 @@
FUZZ_TARGET=libsnapshot_fuzzer
TARGET_ARCH=$(get_build_var TARGET_ARCH)
FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}
-DEVICE_CORPSE_DIR=/data/local/tmp/${FUZZ_TARGET}
+DEVICE_INIT_CORPUS_DIR=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/corpus
+DEVICE_GENERATED_CORPUS_DIR=/data/local/tmp/${FUZZ_TARGET}/corpus
DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
@@ -26,13 +27,14 @@
prepare_device() {
adb root && adb remount &&
- adb shell mkdir -p ${DEVICE_CORPSE_DIR} &&
+ adb shell mkdir -p ${DEVICE_GENERATED_CORPUS_DIR} &&
adb shell rm -rf ${DEVICE_GCOV_DIR} &&
adb shell mkdir -p ${DEVICE_GCOV_DIR}
}
push_binary() {
- adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY}
+ adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY} &&
+ adb push ${ANDROID_PRODUCT_OUT}/${DEVICE_INIT_CORPUS_DIR} $(dirname ${FUZZ_BINARY})
}
prepare_host() {
@@ -52,7 +54,7 @@
prepare_device &&
build_normal &&
push_binary &&
- adb shell ${FUZZ_BINARY} "$@" ${DEVICE_CORPSE_DIR}
+ adb shell ${FUZZ_BINARY} "$@" ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
}
run_snapshot_fuzz() {
@@ -62,7 +64,7 @@
adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
${FUZZ_BINARY} \
-runs=0 \
- ${DEVICE_CORPSE_DIR}
+ ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
}
show_fuzz_result() {
@@ -82,7 +84,7 @@
# run_snapshot_fuzz -runs=10000
run_snapshot_fuzz_all() {
- generate_corpse "$@" &&
+ generate_corpus "$@" &&
run_snapshot_fuzz &&
show_fuzz_result
}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
index 4dc6cdc..20b13b2 100644
--- a/fs_mgr/libsnapshot/fuzz_utils.h
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -68,17 +68,25 @@
return 0;
}
+// Get the field descriptor for the oneof field in the action message. If no oneof field is set,
+// return nullptr.
template <typename Action>
-void ExecuteActionProto(typename Action::Class* module,
- const typename Action::Proto& action_proto) {
+const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
+ const typename Action::Proto& action_proto) {
static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
auto* action_refl = Action::Proto::GetReflection();
if (!action_refl->HasOneof(action_proto, action_value_desc)) {
- return;
+ return nullptr;
}
+ return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+}
- const auto* field_desc = action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+template <typename Action>
+void ExecuteActionProto(typename Action::ClassType* module,
+ const typename Action::Proto& action_proto) {
+ const auto* field_desc = GetValueFieldDescriptor<Action>(action_proto);
+ if (field_desc == nullptr) return;
auto number = field_desc->number();
const auto& map = *Action::GetFunctionMap();
auto it = map.find(number);
@@ -89,7 +97,7 @@
template <typename Action>
void ExecuteAllActionProtos(
- typename Action::Class* module,
+ typename Action::ClassType* module,
const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
for (const auto& proto : action_protos) {
ExecuteActionProto<Action>(module, proto);
@@ -134,53 +142,57 @@
// ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
// with these arguments.
template <typename FuzzFunction, typename Signature, typename Enabled = void>
-struct ActionPerfomer; // undefined
+struct ActionPerformerImpl; // undefined
template <typename FuzzFunction, typename MessageProto>
-struct ActionPerfomer<
+struct ActionPerformerImpl<
FuzzFunction, void(const MessageProto&),
typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
- static void Invoke(typename FuzzFunction::Class* module,
- const google::protobuf::Message& action_proto,
- const google::protobuf::FieldDescriptor* field_desc) {
+ static typename FuzzFunction::ReturnType Invoke(
+ typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
action_proto.GetReflection()->GetMessage(action_proto, field_desc));
- FuzzFunction::ImplBody(module, arg);
+ return FuzzFunction::ImplBody(module, arg);
}
};
template <typename FuzzFunction, typename Primitive>
-struct ActionPerfomer<FuzzFunction, void(Primitive),
- typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
- static void Invoke(typename FuzzFunction::Class* module,
- const google::protobuf::Message& action_proto,
- const google::protobuf::FieldDescriptor* field_desc) {
+struct ActionPerformerImpl<FuzzFunction, void(Primitive),
+ typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
+ static typename FuzzFunction::ReturnType Invoke(
+ typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
action_proto, field_desc);
- FuzzFunction::ImplBody(module, arg);
+ return FuzzFunction::ImplBody(module, arg);
}
};
template <typename FuzzFunction>
-struct ActionPerfomer<FuzzFunction, void()> {
- static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&,
- const google::protobuf::FieldDescriptor*) {
- FuzzFunction::ImplBody(module);
+struct ActionPerformerImpl<FuzzFunction, void()> {
+ static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
+ const google::protobuf::Message&,
+ const google::protobuf::FieldDescriptor*) {
+ return FuzzFunction::ImplBody(module);
}
};
template <typename FuzzFunction>
-struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
- static void Invoke(typename FuzzFunction::Class* module,
- const google::protobuf::Message& action_proto,
- const google::protobuf::FieldDescriptor* field_desc) {
+struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
+ static typename FuzzFunction::ReturnType Invoke(
+ typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
std::string scratch;
const std::string& arg = action_proto.GetReflection()->GetStringReference(
action_proto, field_desc, &scratch);
- FuzzFunction::ImplBody(module, arg);
+ return FuzzFunction::ImplBody(module, arg);
}
};
+template <typename FuzzFunction>
+struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
+
} // namespace android::fuzz
// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
@@ -197,11 +209,11 @@
// FUZZ_CLASS(Foo, FooAction)
// After linking functions of Foo to FooAction, execute all actions by:
// FooAction::ExecuteAll(foo_object, action_protos)
-#define FUZZ_CLASS(ClassType, Action) \
+#define FUZZ_CLASS(Class, Action) \
class Action { \
public: \
using Proto = Action##Proto; \
- using Class = ClassType; \
+ using ClassType = Class; \
using FunctionMap = android::fuzz::FunctionMap<Class>; \
static FunctionMap* GetFunctionMap() { \
static Action::FunctionMap map; \
@@ -225,29 +237,33 @@
// }
// class Foo { public: void DoAwesomeFoo(bool arg); };
// FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoFoo, module, bool arg) {
+// FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
// module->DoAwesomeFoo(arg);
// }
// The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
-#define FUZZ_FUNCTION(Action, FunctionName, module, ...) \
- class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \
- public: \
- using Class = Action::Class; \
- static void ImplBody(Action::Class*, ##__VA_ARGS__); \
- \
- private: \
- static bool registered_; \
- }; \
- auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \
- auto tag = Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
- auto func = \
- &::android::fuzz::ActionPerfomer<FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName), \
- void(__VA_ARGS__)>::Invoke; \
- Action::GetFunctionMap()->CheckEmplace(tag, func); \
- return true; \
- })(); \
- void FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(Action::Class* module, \
- ##__VA_ARGS__)
+#define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...) \
+ class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \
+ public: \
+ using ActionType = Action; \
+ using ClassType = Action::ClassType; \
+ using ReturnType = Return; \
+ using Signature = void(__VA_ARGS__); \
+ static constexpr const char name[] = #FunctionName; \
+ static constexpr const auto tag = \
+ Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
+ static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__); \
+ \
+ private: \
+ static bool registered_; \
+ }; \
+ auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \
+ auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag; \
+ auto func = &::android::fuzz::ActionPerformer<FUZZ_FUNCTION_CLASS_NAME( \
+ Action, FunctionName)>::Invoke; \
+ Action::GetFunctionMap()->CheckEmplace(tag, func); \
+ return true; \
+ })(); \
+ Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__)
// Implement a simple action by linking it to the function with the same name. Example:
// message FooActionProto {
@@ -261,5 +277,9 @@
// FUZZ_FUNCTION(FooAction, DoBar);
// The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
// also the name of the function of Foo.
-#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
- FUZZ_FUNCTION(Action, FunctionName, module) { (void)module->FunctionName(); }
+#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
+ FUZZ_FUNCTION(Action, FunctionName, \
+ decltype(std::declval<Action::ClassType>().FunctionName()), \
+ Action::ClassType* module) { \
+ return module->FunctionName(); \
+ }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 5acd039..2d6071f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -580,6 +580,10 @@
bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
const std::function<bool()>& callback);
+ // Return device string of a mapped image, or if it is not available, the mapped image path.
+ bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
+ std::string* device_string_or_mapped_path);
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 03efd68..488009a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1691,7 +1691,7 @@
return false;
}
std::string cow_device;
- if (!dm.GetDeviceString(cow_name, &cow_device)) {
+ if (!GetMappedImageDeviceStringOrPath(cow_name, &cow_device)) {
LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
return false;
}
@@ -1788,7 +1788,7 @@
// If the COW image exists, append it as the last extent.
if (snapshot_status.cow_file_size() > 0) {
std::string cow_image_device;
- if (!dm.GetDeviceString(cow_image_name, &cow_image_device)) {
+ if (!GetMappedImageDeviceStringOrPath(cow_image_name, &cow_image_device)) {
LOG(ERROR) << "Cannot determine major/minor for: " << cow_image_name;
return false;
}
@@ -2364,7 +2364,6 @@
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
CHECK(lock);
- auto& dm = DeviceMapper::Instance();
CreateLogicalPartitionParams cow_params{
.block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
.metadata = exported_target_metadata,
@@ -2389,7 +2388,7 @@
}
std::string cow_path;
- if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
+ if (!images_->GetMappedImageDevice(cow_name, &cow_path)) {
LOG(ERROR) << "Cannot determine path for " << cow_name;
return Return::Error();
}
@@ -2742,5 +2741,24 @@
return SnapshotMergeStats::GetInstance(*this);
}
+bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
+ std::string* device_string_or_mapped_path) {
+ auto& dm = DeviceMapper::Instance();
+ // Try getting the device string if it is a device mapper device.
+ if (dm.GetState(device_name) != DmDeviceState::INVALID) {
+ return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+ }
+
+ // Otherwise, get path from IImageManager.
+ if (!images_->GetMappedImageDevice(device_name, device_string_or_mapped_path)) {
+ return false;
+ }
+
+ LOG(WARNING) << "Calling GetMappedImageDevice with local image manager; device "
+ << (device_string_or_mapped_path ? *device_string_or_mapped_path : "(nullptr)")
+ << "may not be available in first stage init! ";
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 421154d..5b145c3 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -21,14 +21,21 @@
#include <tuple>
#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/result.h>
+#include <gtest/gtest.h>
#include <src/libfuzzer/libfuzzer_macro.h>
#include <storage_literals/storage_literals.h>
#include "fuzz_utils.h"
#include "snapshot_fuzz_utils.h"
+using android::base::Error;
+using android::base::GetBoolProperty;
using android::base::LogId;
using android::base::LogSeverity;
+using android::base::ReadFileToString;
+using android::base::Result;
using android::base::SetLogger;
using android::base::StderrLogger;
using android::base::StdioLogger;
@@ -37,6 +44,8 @@
using android::snapshot::SnapshotFuzzData;
using android::snapshot::SnapshotFuzzEnv;
using chromeos_update_engine::DeltaArchiveManifest;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::Message;
using google::protobuf::RepeatedPtrField;
// Avoid linking to libgsi since it needs disk I/O.
@@ -54,6 +63,7 @@
namespace android::snapshot {
const SnapshotFuzzData* current_data = nullptr;
+const SnapshotTestModule* current_module = nullptr;
SnapshotFuzzEnv* GetSnapshotFuzzEnv();
@@ -73,48 +83,49 @@
FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
-#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \
- FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__)
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...) \
+ FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
+ ##__VA_ARGS__)
-SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) {
- (void)snapshot->FinishedSnapshotWrites(wipe);
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
+ return snapshot->FinishedSnapshotWrites(wipe);
}
-SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) {
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
std::function<bool()> before_cancel;
if (args.has_before_cancel()) {
before_cancel = [&]() { return args.fail_before_cancel(); };
}
- (void)snapshot->ProcessUpdateState({}, before_cancel);
+ return snapshot->ProcessUpdateState({}, before_cancel);
}
-SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) {
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
double progress;
- (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+ return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
}
-SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) {
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
std::function<void()> callback;
if (has_callback) {
callback = []() {};
}
- (void)snapshot->HandleImminentDataWipe(callback);
+ return snapshot->HandleImminentDataWipe(callback);
}
-SNAPSHOT_FUZZ_FUNCTION(Dump) {
+SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
std::stringstream ss;
- (void)snapshot->Dump(ss);
+ return snapshot->Dump(ss);
}
-SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) {
- (void)snapshot->CreateUpdateSnapshots(manifest);
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
+ return snapshot->CreateUpdateSnapshots(manifest);
}
-SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) {
- (void)snapshot->UnmapUpdateSnapshot(name);
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
+ return snapshot->UnmapUpdateSnapshot(name);
}
-SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
const CreateLogicalAndSnapshotPartitionsArgs& args) {
const std::string* super;
if (args.use_correct_super()) {
@@ -122,20 +133,21 @@
} else {
super = &args.super();
}
- (void)snapshot->CreateLogicalAndSnapshotPartitions(
+ return snapshot->CreateLogicalAndSnapshotPartitions(
*super, std::chrono::milliseconds(args.timeout_millis()));
}
-SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata,
+SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
const RecoveryCreateSnapshotDevicesArgs& args) {
std::unique_ptr<AutoDevice> device;
if (args.has_metadata_device_object()) {
device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
}
- (void)snapshot->RecoveryCreateSnapshotDevices(device);
+ return snapshot->RecoveryCreateSnapshotDevices(device);
}
-SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) {
+SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
+ const CreateLogicalPartitionParamsProto& params_proto) {
auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
CreateLogicalPartitionParams params;
if (params_proto.use_correct_super()) {
@@ -152,7 +164,14 @@
params.device_name = params_proto.device_name();
params.partition_opener = partition_opener.get();
std::string path;
- (void)snapshot->MapUpdateSnapshot(params, &path);
+ return snapshot->MapUpdateSnapshot(params, &path);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
+ (void)snapshot;
+ CHECK(current_module != nullptr);
+ CHECK(current_module->device_info != nullptr);
+ current_module->device_info->SwitchSlot();
}
// During global init, log all messages to stdio. This is only done once.
@@ -186,7 +205,8 @@
}
// Stop logging (except fatal messages) after global initialization. This is only done once.
int StopLoggingAfterGlobalInit() {
- [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer;
+ (void)GetSnapshotFuzzEnv();
+ [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
SetLogger(&FatalOnlyLogger);
return 0;
}
@@ -194,22 +214,139 @@
SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
[[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
static SnapshotFuzzEnv env;
- [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
return &env;
}
+SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
+ current_data = &snapshot_fuzz_data;
+
+ auto env = GetSnapshotFuzzEnv();
+ env->CheckSoftReset();
+
+ auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
+ current_module = &test_module;
+ CHECK(test_module.snapshot);
+ return test_module;
+}
+
+void TearDownTest() {
+ current_module = nullptr;
+ current_data = nullptr;
+}
+
} // namespace android::snapshot
DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
using namespace android::snapshot;
- current_data = &snapshot_fuzz_data;
-
- auto env = GetSnapshotFuzzEnv();
- env->CheckSoftReset();
-
- auto snapshot_manager = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
- CHECK(snapshot_manager);
-
- SnapshotManagerAction::ExecuteAll(snapshot_manager.get(), snapshot_fuzz_data.actions());
+ [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+ auto test_module = SetUpTest(snapshot_fuzz_data);
+ SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
+ TearDownTest();
}
+
+namespace android::snapshot {
+
+// Work-around to cast a 'void' value to Result<void>.
+template <typename T>
+struct GoodResult {
+ template <typename F>
+ static Result<T> Cast(F&& f) {
+ return f();
+ }
+};
+
+template <>
+struct GoodResult<void> {
+ template <typename F>
+ static Result<void> Cast(F&& f) {
+ f();
+ return {};
+ }
+};
+
+class LibsnapshotFuzzerTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ // Do initialization once.
+ (void)GetSnapshotFuzzEnv();
+ }
+ void SetUp() override {
+ bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false);
+ if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices.";
+ }
+ void SetUpFuzzData(const std::string& fn) {
+ auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn;
+ std::string proto_text;
+ ASSERT_TRUE(ReadFileToString(path, &proto_text));
+ snapshot_fuzz_data_ = std::make_unique<SnapshotFuzzData>();
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text,
+ snapshot_fuzz_data_.get()));
+ test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_);
+ }
+ void TearDown() override { android::snapshot::TearDownTest(); }
+ template <typename FuzzFunction>
+ Result<typename FuzzFunction::ReturnType> Execute(int action_index) {
+ if (action_index >= snapshot_fuzz_data_->actions_size()) {
+ return Error() << "Index " << action_index << " is out of bounds ("
+ << snapshot_fuzz_data_->actions_size() << " actions in corpus";
+ }
+ const auto& action_proto = snapshot_fuzz_data_->actions(action_index);
+ const auto* field_desc =
+ android::fuzz::GetValueFieldDescriptor<typename FuzzFunction::ActionType>(
+ action_proto);
+ if (field_desc == nullptr) {
+ return Error() << "Action at index " << action_index << " has no value defined.";
+ }
+ if (FuzzFunction::tag != field_desc->number()) {
+ return Error() << "Action at index " << action_index << " is expected to be "
+ << FuzzFunction::name << ", but it is " << field_desc->name()
+ << " in corpus.";
+ }
+ return GoodResult<typename FuzzFunction::ReturnType>::Cast([&]() {
+ return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
+ action_proto, field_desc);
+ });
+ }
+
+ std::unique_ptr<SnapshotFuzzData> snapshot_fuzz_data_;
+ SnapshotTestModule test_module_;
+};
+
+#define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name)
+
+MATCHER_P(ResultIs, expected, "") {
+ if (!arg.ok()) {
+ *result_listener << arg.error();
+ return false;
+ }
+ *result_listener << "expected: " << expected;
+ return arg.value() == expected;
+}
+
+#define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true))
+
+// Check that launch_device.txt is executed correctly.
+TEST_F(LibsnapshotFuzzerTest, LaunchDevice) {
+ SetUpFuzzData("launch_device.txt");
+
+ int i = 0;
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
+ ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
+ ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
+ ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
+}
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index 8101d03..8926535 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -24,7 +24,10 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <libsnapshot/auto_device.h>
#include <libsnapshot/snapshot.h>
#include <storage_literals/storage_literals.h>
@@ -41,21 +44,30 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
+using android::base::Basename;
+using android::base::ReadFileToString;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::unique_fd;
using android::base::WriteStringToFile;
+using android::dm::DeviceMapper;
+using android::dm::DmTarget;
using android::dm::LoopControl;
using android::fiemap::IImageManager;
using android::fiemap::ImageManager;
using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::FstabEntry;
using android::fs_mgr::IPartitionOpener;
using chromeos_update_engine::DynamicPartitionMetadata;
-// This directory is exempted from pinning in ImageManager.
-static const char MNT_DIR[] = "/data/gsi/ota/test/";
+static const char MNT_DIR[] = "/mnt";
+static const char BLOCK_SYSFS[] = "/sys/block";
static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
static const auto SUPER_IMAGE_SIZE = 16_MiB;
+static const auto DATA_IMAGE_SIZE = 16_MiB;
static const auto FAKE_ROOT_SIZE = 64_MiB;
namespace android::snapshot {
@@ -98,6 +110,141 @@
return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
}
+std::string GetLinearBaseDeviceString(const DeviceMapper::TargetInfo& target) {
+ if (target.spec.target_type != "linear"s) return {};
+ auto tokens = Split(target.data, " ");
+ CHECK_EQ(2, tokens.size());
+ return tokens[0];
+}
+
+std::vector<std::string> GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo& target) {
+ if (target.spec.target_type != "snapshot"s && target.spec.target_type != "snapshot-merge"s)
+ return {};
+ auto tokens = Split(target.data, " ");
+ CHECK_EQ(4, tokens.size());
+ return {tokens[0], tokens[1]};
+}
+
+bool ShouldDeleteLoopDevice(const std::string& node) {
+ std::string backing_file;
+ if (ReadFileToString(StringPrintf("%s/loop/backing_file", node.data()), &backing_file)) {
+ if (StartsWith(backing_file, std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<DeviceMapper::TargetInfo> GetTableInfoIfExists(const std::string& dev_name) {
+ auto& dm = DeviceMapper::Instance();
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (!dm.GetTableInfo(dev_name, &table)) {
+ PCHECK(errno == ENODEV);
+ return {};
+ }
+ return table;
+}
+
+std::set<std::string> GetAllBaseDeviceStrings(const std::string& child_dev) {
+ std::set<std::string> ret;
+ for (const auto& child_target : GetTableInfoIfExists(child_dev)) {
+ auto snapshot_bases = GetSnapshotBaseDeviceStrings(child_target);
+ ret.insert(snapshot_bases.begin(), snapshot_bases.end());
+
+ auto linear_base = GetLinearBaseDeviceString(child_target);
+ if (!linear_base.empty()) {
+ ret.insert(linear_base);
+ }
+ }
+ return ret;
+}
+
+using PropertyList = std::set<std::string>;
+void InsertProperty(const char* key, const char* /*name*/, void* cookie) {
+ reinterpret_cast<PropertyList*>(cookie)->insert(key);
+}
+
+// Attempt to delete all devices that is based on dev_name, including itself.
+void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false,
+ uint64_t depth = 100) {
+ CHECK(depth > 0) << "Reaching max depth when deleting " << dev_name
+ << ". There may be devices referencing itself. Check `dmctl list devices -v`.";
+
+ auto& dm = DeviceMapper::Instance();
+ auto table = GetTableInfoIfExists(dev_name);
+ if (table.empty()) {
+ PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
+ return;
+ }
+
+ if (!known_allow_delete) {
+ for (const auto& target : table) {
+ auto base_device_string = GetLinearBaseDeviceString(target);
+ if (base_device_string.empty()) continue;
+ if (ShouldDeleteLoopDevice(
+ StringPrintf("/sys/dev/block/%s", base_device_string.data()))) {
+ known_allow_delete = true;
+ break;
+ }
+ }
+ }
+ if (!known_allow_delete) {
+ return;
+ }
+
+ std::string dev_string;
+ PCHECK(dm.GetDeviceString(dev_name, &dev_string));
+
+ std::vector<DeviceMapper::DmBlockDevice> devices;
+ PCHECK(dm.GetAvailableDevices(&devices));
+ for (const auto& child_dev : devices) {
+ auto child_bases = GetAllBaseDeviceStrings(child_dev.name());
+ if (child_bases.find(dev_string) != child_bases.end()) {
+ CheckDeleteDeviceMapperTree(child_dev.name(), true /* known_allow_delete */, depth - 1);
+ }
+ }
+
+ PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
+}
+
+// Attempt to clean up residues from previous runs.
+void CheckCleanupDeviceMapperDevices() {
+ auto& dm = DeviceMapper::Instance();
+ std::vector<DeviceMapper::DmBlockDevice> devices;
+ PCHECK(dm.GetAvailableDevices(&devices));
+
+ for (const auto& dev : devices) {
+ CheckDeleteDeviceMapperTree(dev.name());
+ }
+}
+
+void CheckUmount(const std::string& path) {
+ PCHECK(TEMP_FAILURE_RETRY(umount(path.data()) == 0) || errno == ENOENT || errno == EINVAL)
+ << path;
+}
+
+void CheckDetachLoopDevices(const std::set<std::string>& exclude_names = {}) {
+ // ~SnapshotFuzzEnv automatically does the following.
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(BLOCK_SYSFS), closedir);
+ PCHECK(dir != nullptr) << BLOCK_SYSFS;
+ LoopControl loop_control;
+ dirent* dp;
+ while ((dp = readdir(dir.get())) != nullptr) {
+ if (exclude_names.find(dp->d_name) != exclude_names.end()) {
+ continue;
+ }
+ if (!ShouldDeleteLoopDevice(StringPrintf("%s/%s", BLOCK_SYSFS, dp->d_name).data())) {
+ continue;
+ }
+ PCHECK(loop_control.Detach(StringPrintf("/dev/block/%s", dp->d_name).data()));
+ }
+}
+
+void CheckUmountAll() {
+ CheckUmount(std::string(MNT_DIR) + "/snapshot_fuzz_data");
+ CheckUmount(std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME);
+}
+
class AutoDeleteDir : public AutoDevice {
public:
static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
@@ -108,9 +255,7 @@
}
~AutoDeleteDir() {
if (!HasDevice()) return;
- if (rmdir(name_.c_str()) == -1) {
- PLOG(ERROR) << "Cannot remove " << name_;
- }
+ PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_;
}
private:
@@ -119,6 +264,15 @@
class AutoUnmount : public AutoDevice {
public:
+ ~AutoUnmount() {
+ if (!HasDevice()) return;
+ CheckUmount(name_);
+ }
+ AutoUnmount(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmountTmpfs : public AutoUnmount {
+ public:
static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
if (mount("tmpfs", path.c_str(), "tmpfs", 0,
(void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
@@ -127,30 +281,20 @@
}
return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
}
- ~AutoUnmount() {
- if (!HasDevice()) return;
- if (umount(name_.c_str()) == -1) {
- PLOG(ERROR) << "Cannot umount " << name_;
- }
- }
-
private:
- AutoUnmount(const std::string& path) : AutoDevice(path) {}
+ using AutoUnmount::AutoUnmount;
};
// A directory on tmpfs. Upon destruct, it is unmounted and deleted.
class AutoMemBasedDir : public AutoDevice {
public:
static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
- if (!Mkdir(MNT_DIR)) {
- return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
- }
auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
if (!ret->auto_delete_mount_dir_->HasDevice()) {
return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
}
- ret->auto_umount_mount_point_ = AutoUnmount::New(ret->mount_path(), size);
+ ret->auto_umount_mount_point_ = AutoUnmountTmpfs::New(ret->mount_path(), size);
if (!ret->auto_umount_mount_point_->HasDevice()) {
return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
}
@@ -191,14 +335,39 @@
};
SnapshotFuzzEnv::SnapshotFuzzEnv() {
+ CheckCleanupDeviceMapperDevices();
+ CheckDetachLoopDevices();
+ CheckUmountAll();
+
fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
CHECK(fake_root_ != nullptr);
CHECK(fake_root_->HasDevice());
loop_control_ = std::make_unique<LoopControl>();
- mapped_super_ = CheckMapSuper(fake_root_->persist_path(), loop_control_.get(), &fake_super_);
+
+ fake_data_mount_point_ = MNT_DIR + "/snapshot_fuzz_data"s;
+ auto_delete_data_mount_point_ = AutoDeleteDir::New(fake_data_mount_point_);
+ CHECK(auto_delete_data_mount_point_ != nullptr);
+ CHECK(auto_delete_data_mount_point_->HasDevice());
+
+ const auto& fake_persist_path = fake_root_->persist_path();
+ mapped_super_ = CheckMapImage(fake_persist_path + "/super.img", SUPER_IMAGE_SIZE,
+ loop_control_.get(), &fake_super_);
+ mapped_data_ = CheckMapImage(fake_persist_path + "/data.img", DATA_IMAGE_SIZE,
+ loop_control_.get(), &fake_data_block_device_);
+ mounted_data_ = CheckMountFormatData(fake_data_block_device_, fake_data_mount_point_);
}
-SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
+SnapshotFuzzEnv::~SnapshotFuzzEnv() {
+ CheckCleanupDeviceMapperDevices();
+ mounted_data_ = nullptr;
+ auto_delete_data_mount_point_ = nullptr;
+ mapped_data_ = nullptr;
+ mapped_super_ = nullptr;
+ CheckDetachLoopDevices();
+ loop_control_ = nullptr;
+ fake_root_ = nullptr;
+ CheckUmountAll();
+}
void CheckZeroFill(const std::string& file, size_t size) {
std::string zeros(size, '\0');
@@ -208,18 +377,15 @@
void SnapshotFuzzEnv::CheckSoftReset() {
fake_root_->CheckSoftReset();
CheckZeroFill(super(), SUPER_IMAGE_SIZE);
+ CheckCleanupDeviceMapperDevices();
+ CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)});
}
std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager(
- const std::string& path) {
- auto images_dir = path + "/images";
- auto metadata_dir = images_dir + "/metadata";
- auto data_dir = images_dir + "/data";
-
- PCHECK(Mkdir(images_dir));
+ const std::string& metadata_dir, const std::string& data_dir) {
PCHECK(Mkdir(metadata_dir));
PCHECK(Mkdir(data_dir));
- return ImageManager::Open(metadata_dir, data_dir);
+ return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
}
// Helper to create a loop device for a file.
@@ -236,36 +402,42 @@
public:
AutoDetachLoopDevice(LoopControl* control, const std::string& device)
: AutoDevice(device), control_(control) {}
- ~AutoDetachLoopDevice() { control_->Detach(name_); }
+ ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << name_; }
private:
LoopControl* control_;
};
-std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapSuper(const std::string& fake_persist_path,
- LoopControl* control,
- std::string* fake_super) {
- auto super_img = fake_persist_path + "/super.img";
- CheckZeroFill(super_img, SUPER_IMAGE_SIZE);
- CheckCreateLoopDevice(control, super_img, 1s, fake_super);
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapImage(const std::string& img_path,
+ uint64_t size, LoopControl* control,
+ std::string* mapped_path) {
+ CheckZeroFill(img_path, size);
+ CheckCreateLoopDevice(control, img_path, 1s, mapped_path);
- return std::make_unique<AutoDetachLoopDevice>(control, *fake_super);
+ return std::make_unique<AutoDetachLoopDevice>(control, *mapped_path);
}
-std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
- const SnapshotFuzzData& data) {
+SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) {
+ SnapshotTestModule ret;
auto partition_opener = std::make_unique<TestPartitionOpener>(super());
+ ret.opener = partition_opener.get();
CheckWriteSuperMetadata(data, *partition_opener);
auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
PCHECK(Mkdir(metadata_dir));
+ if (data.has_metadata_snapshots_dir()) {
+ PCHECK(Mkdir(metadata_dir + "/snapshots"));
+ }
- auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
- std::move(partition_opener), metadata_dir);
- auto snapshot = SnapshotManager::New(device_info /* takes ownership */);
- snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path());
+ ret.device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
+ std::move(partition_opener), metadata_dir);
+ auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */);
+ snapshot->images_ =
+ CheckCreateFakeImageManager(fake_root_->tmp_path() + "/images_manager_metadata",
+ fake_data_mount_point_ + "/image_manager_data");
snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager();
+ ret.snapshot = std::move(snapshot);
- return snapshot;
+ return ret;
}
const std::string& SnapshotFuzzEnv::super() const {
@@ -311,4 +483,34 @@
CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
}
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device,
+ const std::string& mount_point) {
+ FstabEntry entry{
+ .blk_device = blk_device,
+ .length = static_cast<off64_t>(DATA_IMAGE_SIZE),
+ .fs_type = "ext4",
+ .mount_point = mount_point,
+ };
+ CHECK(0 == fs_mgr_do_format(entry, false /* crypt_footer */));
+ CHECK(0 == fs_mgr_do_mount_one(entry));
+ return std::make_unique<AutoUnmount>(mount_point);
+}
+
+SnapshotFuzzImageManager::~SnapshotFuzzImageManager() {
+ // Remove relevant gsid.mapped_images.* props.
+ for (const auto& name : mapped_) {
+ CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name;
+ }
+}
+
+bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name,
+ const std::chrono::milliseconds& timeout_ms,
+ std::string* path) {
+ if (impl_->MapImageDevice(name, timeout_ms, path)) {
+ mapped_.insert(name);
+ return true;
+ }
+ return false;
+}
+
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 5533def..fa327b8 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <memory>
+#include <set>
#include <string>
#include <android-base/file.h>
@@ -31,12 +33,19 @@
namespace android::snapshot {
class AutoMemBasedDir;
+class SnapshotFuzzDeviceInfo;
class DummyAutoDevice : public AutoDevice {
public:
DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
};
+struct SnapshotTestModule {
+ std::unique_ptr<ISnapshotManager> snapshot;
+ SnapshotFuzzDeviceInfo* device_info = nullptr;
+ TestPartitionOpener* opener = nullptr;
+};
+
// Prepare test environment. This has a heavy overhead and should be done once.
class SnapshotFuzzEnv {
public:
@@ -54,7 +63,7 @@
// Create a snapshot manager for this test run.
// Client is responsible for maintaining the lifetime of |data| over the life time of
// ISnapshotManager.
- std::unique_ptr<ISnapshotManager> CheckCreateSnapshotManager(const SnapshotFuzzData& data);
+ SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data);
// Return path to super partition.
const std::string& super() const;
@@ -62,14 +71,22 @@
private:
std::unique_ptr<AutoMemBasedDir> fake_root_;
std::unique_ptr<android::dm::LoopControl> loop_control_;
+ std::string fake_data_mount_point_;
+ std::unique_ptr<AutoDevice> auto_delete_data_mount_point_;
std::unique_ptr<AutoDevice> mapped_super_;
std::string fake_super_;
+ std::unique_ptr<AutoDevice> mapped_data_;
+ std::string fake_data_block_device_;
+ std::unique_ptr<AutoDevice> mounted_data_;
static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
- const std::string& fake_tmp_path);
- static std::unique_ptr<AutoDevice> CheckMapSuper(const std::string& fake_persist_path,
+ const std::string& metadata_dir, const std::string& data_dir);
+ static std::unique_ptr<AutoDevice> CheckMapImage(const std::string& fake_persist_path,
+ uint64_t size,
android::dm::LoopControl* control,
- std::string* fake_super);
+ std::string* mapped_path);
+ static std::unique_ptr<AutoDevice> CheckMountFormatData(const std::string& blk_device,
+ const std::string& mount_point);
void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
const android::fs_mgr::IPartitionOpener& opener);
@@ -97,10 +114,8 @@
}
// Following APIs are fuzzed.
- std::string GetSlotSuffix() const override { return data_->slot_suffix_is_a() ? "_a" : "_b"; }
- std::string GetOtherSlotSuffix() const override {
- return data_->slot_suffix_is_a() ? "_b" : "_a";
- }
+ std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; }
+ std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; }
bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
return data_->allow_set_boot_control_merge_status();
@@ -110,10 +125,79 @@
}
bool IsRecovery() const override { return data_->is_recovery(); }
+ void SwitchSlot() { switched_slot_ = !switched_slot_; }
+
private:
const FuzzDeviceInfoData* data_;
std::unique_ptr<TestPartitionOpener> partition_opener_;
std::string metadata_dir_;
+ bool switched_slot_ = false;
+
+ bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
+};
+
+// A spy class on ImageManager implementation. Upon destruction, unmaps all images
+// map through this object.
+class SnapshotFuzzImageManager : public android::fiemap::IImageManager {
+ public:
+ static std::unique_ptr<SnapshotFuzzImageManager> Open(const std::string& metadata_dir,
+ const std::string& data_dir) {
+ auto impl = android::fiemap::ImageManager::Open(metadata_dir, data_dir);
+ if (impl == nullptr) return nullptr;
+ return std::unique_ptr<SnapshotFuzzImageManager>(
+ new SnapshotFuzzImageManager(std::move(impl)));
+ }
+
+ ~SnapshotFuzzImageManager();
+
+ // Spied APIs.
+ bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
+ std::string* path) override;
+
+ // Other functions call through.
+ android::fiemap::FiemapStatus CreateBackingImage(
+ const std::string& name, uint64_t size, int flags,
+ std::function<bool(uint64_t, uint64_t)>&& on_progress) override {
+ return impl_->CreateBackingImage(name, size, flags, std::move(on_progress));
+ }
+ bool DeleteBackingImage(const std::string& name) override {
+ return impl_->DeleteBackingImage(name);
+ }
+ bool UnmapImageDevice(const std::string& name) override {
+ return impl_->UnmapImageDevice(name);
+ }
+ bool BackingImageExists(const std::string& name) override {
+ return impl_->BackingImageExists(name);
+ }
+ bool IsImageMapped(const std::string& name) override { return impl_->IsImageMapped(name); }
+ bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
+ std::string* dev) override {
+ return impl_->MapImageWithDeviceMapper(opener, name, dev);
+ }
+ bool GetMappedImageDevice(const std::string& name, std::string* device) override {
+ return impl_->GetMappedImageDevice(name, device);
+ }
+ bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override {
+ return impl_->MapAllImages(init);
+ }
+ bool DisableImage(const std::string& name) override { return impl_->DisableImage(name); }
+ bool RemoveDisabledImages() override { return impl_->RemoveDisabledImages(); }
+ std::vector<std::string> GetAllBackingImages() override { return impl_->GetAllBackingImages(); }
+ android::fiemap::FiemapStatus ZeroFillNewImage(const std::string& name,
+ uint64_t bytes) override {
+ return impl_->ZeroFillNewImage(name, bytes);
+ }
+ bool RemoveAllImages() override { return impl_->RemoveAllImages(); }
+ bool UnmapImageIfExists(const std::string& name) override {
+ return impl_->UnmapImageIfExists(name);
+ }
+
+ private:
+ std::unique_ptr<android::fiemap::IImageManager> impl_;
+ std::set<std::string> mapped_;
+
+ SnapshotFuzzImageManager(std::unique_ptr<android::fiemap::IImageManager>&& impl)
+ : impl_(std::move(impl)) {}
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
index be5e1fe..8a11eaa 100644
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -45,7 +45,12 @@
}
message InstallOperation {
- enum Type { SOURCE_COPY = 4; }
+ enum Type {
+ SOURCE_COPY = 4;
+ // Not used by libsnapshot. Declared here so that the fuzzer has an
+ // alternative value to use for |type|.
+ ZERO = 6;
+ }
required Type type = 1;
repeated Extent src_extents = 4;
repeated Extent dst_extents = 6;
diff --git a/init/README.md b/init/README.md
index 726c0cc..0dd1490 100644
--- a/init/README.md
+++ b/init/README.md
@@ -547,13 +547,16 @@
* `ref`: use the systemwide DE key
* `per_boot_ref`: use the key freshly generated on each boot.
-`mount_all <fstab> [ <path> ]\* [--<option>]`
+`mount_all [ <fstab> ] [--<option>]`
> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
options "early" and "late".
With "--early" set, the init executable will skip mounting entries with
"latemount" flag and triggering fs encryption state event. With "--late" set,
init executable will only mount entries with "latemount" flag. By default,
no option is set, and mount\_all will process all entries in the given fstab.
+ If the fstab parameter is not specified, fstab.${ro.boot.fstab_suffix},
+ fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
+ under /odm/etc, /vendor/etc, or / at runtime, in that order.
`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
> Attempt to mount the named device at the directory _dir_
@@ -644,7 +647,8 @@
`wait <path> [ <timeout> ]`
> Poll for the existence of the given file and return when found,
or the timeout has been reached. If timeout is not specified it
- currently defaults to five seconds.
+ currently defaults to five seconds. The timeout value can be
+ fractional seconds, specified in floating point notation.
`wait_for_prop <name> <value>`
> Wait for system property _name_ to be _value_. Properties are expanded
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 200bfff..e918e12 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -49,6 +49,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -517,21 +518,21 @@
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
*
- * start_index: index of the first path in the args list
+ * rc_paths: list of paths to rc files to import
*/
-static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
+static void import_late(const std::vector<std::string>& rc_paths) {
auto& action_manager = ActionManager::GetInstance();
auto& service_list = ServiceList::GetInstance();
Parser parser = CreateParser(action_manager, service_list);
- if (end_index <= start_index) {
+ if (rc_paths.empty()) {
// Fallbacks for partitions on which early mount isn't enabled.
for (const auto& path : late_import_paths) {
parser.ParseConfig(path);
}
late_import_paths.clear();
} else {
- for (size_t i = start_index; i < end_index; ++i) {
- parser.ParseConfig(args[i]);
+ for (const auto& rc_path : rc_paths) {
+ parser.ParseConfig(rc_path);
}
}
@@ -632,48 +633,44 @@
static int initial_mount_fstab_return_code = -1;
-/* mount_all <fstab> [ <path> ]* [--<options>]*
+/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
+ * >= R: mount_all [ <fstab> ] [--<options>]*
*
* This function might request a reboot, in which case it will
* not return.
*/
static Result<void> do_mount_all(const BuiltinArguments& args) {
- std::size_t na = 0;
- bool import_rc = true;
- bool queue_event = true;
- int mount_mode = MOUNT_MODE_DEFAULT;
- const auto& fstab_file = args[1];
- std::size_t path_arg_end = args.size();
- const char* prop_post_fix = "default";
+ auto mount_all = ParseMountAll(args.args);
+ if (!mount_all.ok()) return mount_all.error();
- for (na = args.size() - 1; na > 1; --na) {
- if (args[na] == "--early") {
- path_arg_end = na;
- queue_event = false;
- mount_mode = MOUNT_MODE_EARLY;
- prop_post_fix = "early";
- } else if (args[na] == "--late") {
- path_arg_end = na;
- import_rc = false;
- mount_mode = MOUNT_MODE_LATE;
- prop_post_fix = "late";
- }
+ const char* prop_post_fix = "default";
+ bool queue_event = true;
+ if (mount_all->mode == MOUNT_MODE_EARLY) {
+ prop_post_fix = "early";
+ queue_event = false;
+ } else if (mount_all->mode == MOUNT_MODE_LATE) {
+ prop_post_fix = "late";
}
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
android::base::Timer t;
Fstab fstab;
- if (!ReadFstabFromFile(fstab_file, &fstab)) {
- return Error() << "Could not read fstab";
+ if (mount_all->fstab_path.empty()) {
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "Could not read default fstab";
+ }
+ } else {
+ if (!ReadFstabFromFile(mount_all->fstab_path, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
}
- auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
+ auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_all->mode);
SetProperty(prop_name, std::to_string(t.duration().count()));
- if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
- /* Paths of .rc files are specified at the 2nd argument and beyond */
- import_late(args.args, 2, path_arg_end);
+ if (mount_all->import_rc) {
+ import_late(mount_all->rc_paths);
}
if (queue_event) {
@@ -689,11 +686,20 @@
return {};
}
-/* umount_all <fstab> */
+/* umount_all [ <fstab> ] */
static Result<void> do_umount_all(const BuiltinArguments& args) {
+ auto umount_all = ParseUmountAll(args.args);
+ if (!umount_all.ok()) return umount_all.error();
+
Fstab fstab;
- if (!ReadFstabFromFile(args[1], &fstab)) {
- return Error() << "Could not read fstab";
+ if (umount_all->empty()) {
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "Could not read default fstab";
+ }
+ } else {
+ if (!ReadFstabFromFile(*umount_all, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
}
if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
@@ -1065,11 +1071,12 @@
static Result<void> do_wait(const BuiltinArguments& args) {
auto timeout = kCommandRetryTimeout;
if (args.size() == 3) {
- int timeout_int;
- if (!android::base::ParseInt(args[2], &timeout_int)) {
+ double timeout_double;
+ if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
return Error() << "failed to parse timeout";
}
- timeout = std::chrono::seconds(timeout_int);
+ timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::duration<double>(timeout_double));
}
if (wait_for_file(args[1].c_str(), timeout) != 0) {
@@ -1347,11 +1354,11 @@
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
// imports rc scripts, etc. It should be simplified and run in vendor_init context.
// mount and umount are run in the same context as mount_all for symmetry.
- {"mount_all", {1, kMax, {false, do_mount_all}}},
+ {"mount_all", {0, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
{"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},
{"umount", {1, 1, {false, do_umount}}},
- {"umount_all", {1, 1, {false, do_umount_all}}},
+ {"umount_all", {0, 1, {false, do_umount_all}}},
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
{"readahead", {1, 2, {true, do_readahead}}},
{"remount_userdata", {0, 0, {false, do_remount_userdata}}},
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index d62ecb0..450c079 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -25,6 +25,7 @@
#include <sys/time.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -122,6 +123,14 @@
return {};
}
+Result<void> check_mount_all(const BuiltinArguments& args) {
+ auto options = ParseMountAll(args.args);
+ if (!options.ok()) {
+ return options.error();
+ }
+ return {};
+}
+
Result<void> check_mkdir(const BuiltinArguments& args) {
auto options = ParseMkdir(args.args);
if (!options.ok()) {
@@ -203,10 +212,18 @@
return {};
}
+Result<void> check_umount_all(const BuiltinArguments& args) {
+ auto options = ParseUmountAll(args.args);
+ if (!options.ok()) {
+ return options.error();
+ }
+ return {};
+}
+
Result<void> check_wait(const BuiltinArguments& args) {
if (args.size() == 3 && !args[2].empty()) {
- int timeout_int;
- if (!android::base::ParseInt(args[2], &timeout_int)) {
+ double timeout_double;
+ if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
return Error() << "failed to parse timeout";
}
}
diff --git a/init/check_builtins.h b/init/check_builtins.h
index fb34556..725a6fd 100644
--- a/init/check_builtins.h
+++ b/init/check_builtins.h
@@ -32,11 +32,13 @@
Result<void> check_load_system_props(const BuiltinArguments& args);
Result<void> check_loglevel(const BuiltinArguments& args);
Result<void> check_mkdir(const BuiltinArguments& args);
+Result<void> check_mount_all(const BuiltinArguments& args);
Result<void> check_restorecon(const BuiltinArguments& args);
Result<void> check_restorecon_recursive(const BuiltinArguments& args);
Result<void> check_setprop(const BuiltinArguments& args);
Result<void> check_setrlimit(const BuiltinArguments& args);
Result<void> check_sysclktz(const BuiltinArguments& args);
+Result<void> check_umount_all(const BuiltinArguments& args);
Result<void> check_wait(const BuiltinArguments& args);
Result<void> check_wait_for_prop(const BuiltinArguments& args);
diff --git a/init/init.cpp b/init/init.cpp
index 3f8f628..631db8e 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -537,7 +537,9 @@
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
-static void set_usb_controller() {
+static void SetUsbController() {
+ static auto controller_set = false;
+ if (controller_set) return;
std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
if (!dir) return;
@@ -546,6 +548,7 @@
if (dp->d_name[0] == '.') continue;
SetProperty("sys.usb.controller", dp->d_name);
+ controller_set = true;
break;
}
}
@@ -800,7 +803,7 @@
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
MountHandler mount_handler(&epoll);
- set_usb_controller();
+ SetUsbController();
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
@@ -910,6 +913,7 @@
}
if (!IsShuttingDown()) {
HandleControlMessages();
+ SetUsbController();
}
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 842b2e5..82f5b8c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -877,28 +877,32 @@
}
void PropertyLoadBootDefaults() {
- // TODO(b/117892318): merge prop.default and build.prop files into one
// We read the properties and their values into a map, in order to always allow properties
// loaded in the later property files to override the properties in loaded in the earlier
// property files, regardless of if they are "ro." properties or not.
std::map<std::string, std::string> properties;
- if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
- // Try recovery path
- if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
- // Try legacy path
- load_properties_from_file("/default.prop", nullptr, &properties);
- }
+
+ if (IsRecoveryMode()) {
+ load_properties_from_file("/prop.default", nullptr, &properties);
}
+
load_properties_from_file("/system/build.prop", nullptr, &properties);
load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
- load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+
+ // TODO(b/117892318): uncomment the following condition when vendor.imgs for
+ // aosp_* targets are all updated.
+// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
+ load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+// }
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
} else {
load_properties_from_file("/odm/default.prop", nullptr, &properties);
load_properties_from_file("/odm/build.prop", nullptr, &properties);
}
+
load_properties_from_file("/product/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index e89f74a..ffd58a3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -66,8 +66,6 @@
#include "sigchld_handler.h"
#include "util.h"
-#define PROC_SYSRQ "/proc/sysrq-trigger"
-
using namespace std::literals;
using android::base::boot_clock;
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index 485188b..76460a5 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -29,6 +29,7 @@
#include <cutils/android_reboot.h>
#include "capabilities.h"
+#include "reboot_utils.h"
namespace android {
namespace init {
@@ -138,6 +139,9 @@
LOG(ERROR) << backtrace->FormatFrameData(i);
}
if (init_fatal_panic) {
+ LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
+ android::base::WriteStringToFile("c", PROC_SYSRQ);
+ LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";
_exit(signal_number);
}
RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 878ad96..05bb9ae 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -18,6 +18,8 @@
#include <string>
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
namespace android {
namespace init {
diff --git a/init/test_kill_services/AndroidTest.xml b/init/test_kill_services/AndroidTest.xml
index c1dcd59..8018efa 100644
--- a/init/test_kill_services/AndroidTest.xml
+++ b/init/test_kill_services/AndroidTest.xml
@@ -18,7 +18,16 @@
<option name="test-suite-tag" value="apct-native" />
<!-- cannot be autogenerated: b/153565474 -->
- <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
+ <!-- flake mitigation, in case device is in bad state-->
+ <option name="pre-reboot" value="true" />
+ <!-- sometimes device gets into bad state, and we don't detect it in this test,
+ so the test succeeds and the next test fails. This is a really bad result, so
+ to avoid that, making sure we reboot the device again before running any more
+ tests.
+ TODO(b/152556737): add metrics for successful device recovery -->
+ <option name="post-reboot" value="true" />
+ </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
diff --git a/init/util.cpp b/init/util.cpp
index f9be055..40f24b1 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -572,6 +572,48 @@
return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
}
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
+ bool compat_mode = false;
+ bool import_rc = false;
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+ if (args.size() <= 1) {
+ return Error() << "mount_all requires at least 1 argument";
+ }
+ compat_mode = true;
+ import_rc = true;
+ }
+
+ std::size_t first_option_arg = args.size();
+ enum mount_mode mode = MOUNT_MODE_DEFAULT;
+
+ // If we are <= Q, then stop looking for non-fstab arguments at slot 2.
+ // Otherwise, stop looking at slot 1 (as the fstab path argument is optional >= R).
+ for (std::size_t na = args.size() - 1; na > (compat_mode ? 1 : 0); --na) {
+ if (args[na] == "--early") {
+ first_option_arg = na;
+ mode = MOUNT_MODE_EARLY;
+ } else if (args[na] == "--late") {
+ first_option_arg = na;
+ mode = MOUNT_MODE_LATE;
+ import_rc = false;
+ }
+ }
+
+ std::string fstab_path;
+ if (first_option_arg > 1) {
+ fstab_path = args[1];
+ } else if (compat_mode) {
+ return Error() << "mount_all argument 1 must be the fstab path";
+ }
+
+ std::vector<std::string> rc_paths;
+ for (std::size_t na = 2; na < first_option_arg; ++na) {
+ rc_paths.push_back(args[na]);
+ }
+
+ return MountAllOptions{rc_paths, fstab_path, mode, import_rc};
+}
+
Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
const std::vector<std::string>& args) {
struct flag_type {
@@ -612,6 +654,15 @@
return std::pair(flag, paths);
}
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args) {
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+ if (args.size() <= 1) {
+ return Error() << "umount_all requires at least 1 argument";
+ }
+ }
+ return args[1];
+}
+
static void InitAborter(const char* abort_message) {
// When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
// simply abort instead of trying to reboot the system.
diff --git a/init/util.h b/init/util.h
index 8167b02..28f6b18 100644
--- a/init/util.h
+++ b/init/util.h
@@ -22,6 +22,7 @@
#include <chrono>
#include <functional>
#include <string>
+#include <vector>
#include <android-base/chrono_utils.h>
@@ -33,6 +34,12 @@
namespace android {
namespace init {
+enum mount_mode {
+ MOUNT_MODE_DEFAULT = 0,
+ MOUNT_MODE_EARLY = 1,
+ MOUNT_MODE_LATE = 2,
+};
+
static const char kColdBootDoneProp[] = "ro.cold_boot_done";
extern void (*trigger_shutdown)(const std::string& command);
@@ -73,9 +80,20 @@
Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args);
+struct MountAllOptions {
+ std::vector<std::string> rc_paths;
+ std::string fstab_path;
+ mount_mode mode;
+ bool import_rc;
+};
+
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args);
+
Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
const std::vector<std::string>& args);
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args);
+
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
void InitSecondStageLogging(char** argv);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 96a5b55..565e7d4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -61,8 +61,8 @@
TEST(util, ReadFileSymbolicLink) {
errno = 0;
- // lrw------- 1 root root 23 2008-12-31 19:00 default.prop -> system/etc/prop.default
- auto file_contents = ReadFile("/default.prop");
+ // lrwxr-xr-x 1 root shell 6 2009-01-01 09:00 /system/bin/ps -> toybox
+ auto file_contents = ReadFile("/system/bin/ps");
EXPECT_EQ(ELOOP, errno);
ASSERT_FALSE(file_contents.ok());
EXPECT_EQ("open() failed: Too many symbolic links encountered",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index dc989a0..c4cfa6f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -48,6 +48,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "apex_inherit",
}
cc_defaults {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index d7c83a2..60400c9 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -152,6 +152,7 @@
"iosched_policy.cpp",
"load_file.cpp",
"native_handle.cpp",
+ "properties.cpp",
"record_stream.cpp",
"strlcpy.c",
"threads.cpp",
@@ -187,7 +188,6 @@
"fs_config.cpp",
"klog.cpp",
"partition_utils.cpp",
- "properties.cpp",
"qtaguid.cpp",
"trace-dev.cpp",
"uevent.cpp",
@@ -268,6 +268,7 @@
name: "libcutils_test_default",
srcs: [
"native_handle_test.cpp",
+ "properties_test.cpp",
"sockets_test.cpp",
],
@@ -280,7 +281,6 @@
"fs_config_test.cpp",
"memset_test.cpp",
"multiuser_test.cpp",
- "properties_test.cpp",
"sched_policy_test.cpp",
"str_parms_test.cpp",
"trace-dev_test.cpp",
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index d2e0871..78d8bc6 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -14,27 +14,30 @@
* limitations under the License.
*/
-#ifndef __CUTILS_PROPERTIES_H
-#define __CUTILS_PROPERTIES_H
+#pragma once
#include <sys/cdefs.h>
#include <stddef.h>
-#include <sys/system_properties.h>
#include <stdint.h>
+#if __has_include(<sys/system_properties.h>)
+#include <sys/system_properties.h>
+#else
+#define PROP_VALUE_MAX 92
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
-/* System properties are *small* name value pairs managed by the
-** property service. If your data doesn't fit in the provided
-** space it is not appropriate for a system property.
-**
-** WARNING: system/bionic/include/sys/system_properties.h also defines
-** these, but with different names. (TODO: fix that)
-*/
-#define PROPERTY_KEY_MAX PROP_NAME_MAX
-#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
+//
+// Deprecated.
+//
+// See <android-base/properties.h> for better API.
+//
+
+#define PROPERTY_KEY_MAX PROP_NAME_MAX
+#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
/* property_get: returns the length of the value which will never be
** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
@@ -146,5 +149,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 5dbbeba..03f0496 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -16,27 +16,19 @@
#include <cutils/properties.h>
-#define LOG_TAG "properties"
-// #define LOG_NDEBUG 0
-
-#include <assert.h>
-#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <cutils/sockets.h>
-#include <log/log.h>
+#include <android-base/properties.h>
-int8_t property_get_bool(const char *key, int8_t default_value) {
- if (!key) {
- return default_value;
- }
+int8_t property_get_bool(const char* key, int8_t default_value) {
+ if (!key) return default_value;
int8_t result = default_value;
- char buf[PROPERTY_VALUE_MAX] = {'\0'};
+ char buf[PROPERTY_VALUE_MAX] = {};
int len = property_get(key, buf, "");
if (len == 1) {
@@ -57,73 +49,53 @@
return result;
}
-// Convert string property to int (default if fails); return default value if out of bounds
-static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
- intmax_t default_value) {
- if (!key) {
- return default_value;
+template <typename T>
+static T property_get_int(const char* key, T default_value) {
+ if (!key) return default_value;
+
+ char value[PROPERTY_VALUE_MAX] = {};
+ if (property_get(key, value, "") < 1) return default_value;
+
+ // libcutils unwisely allows octal, which libbase doesn't.
+ T result = default_value;
+ int saved_errno = errno;
+ errno = 0;
+ char* end = nullptr;
+ intmax_t v = strtoimax(value, &end, 0);
+ if (errno != ERANGE && end != value && v >= std::numeric_limits<T>::min() &&
+ v <= std::numeric_limits<T>::max()) {
+ result = v;
}
-
- intmax_t result = default_value;
- char buf[PROPERTY_VALUE_MAX] = {'\0'};
- char *end = NULL;
-
- int len = property_get(key, buf, "");
- if (len > 0) {
- int tmp = errno;
- errno = 0;
-
- // Infer base automatically
- result = strtoimax(buf, &end, /*base*/ 0);
- if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
- // Over or underflow
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value);
- } else if (result < lower_bound || result > upper_bound) {
- // Out of range of requested bounds
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value);
- } else if (end == buf) {
- // Numeric conversion failed
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", __FUNCTION__, key,
- default_value);
- }
-
- errno = tmp;
- }
-
+ errno = saved_errno;
return result;
}
-int64_t property_get_int64(const char *key, int64_t default_value) {
- return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value);
+int64_t property_get_int64(const char* key, int64_t default_value) {
+ return property_get_int<int64_t>(key, default_value);
}
-int32_t property_get_int32(const char *key, int32_t default_value) {
- return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value);
+int32_t property_get_int32(const char* key, int32_t default_value) {
+ return property_get_int<int32_t>(key, default_value);
}
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-int property_set(const char *key, const char *value) {
+int property_set(const char* key, const char* value) {
return __system_property_set(key, value);
}
-int property_get(const char *key, char *value, const char *default_value) {
+int property_get(const char* key, char* value, const char* default_value) {
int len = __system_property_get(key, value);
- if (len > 0) {
- return len;
- }
- if (default_value) {
- len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);
- memcpy(value, default_value, len);
- value[len] = '\0';
+ if (len < 1 && default_value) {
+ snprintf(value, PROPERTY_VALUE_MAX, "%s", default_value);
+ return strlen(value);
}
return len;
}
+#if __has_include(<sys/system_properties.h>)
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
struct callback_data {
void (*callback)(const char* name, const char* value, void* cookie);
void* cookie;
@@ -139,6 +111,8 @@
}
int property_list(void (*fn)(const char* name, const char* value, void* cookie), void* cookie) {
- callback_data data = { fn, cookie };
+ callback_data data = {fn, cookie};
return __system_property_foreach(property_list_callback, &data);
}
+
+#endif
diff --git a/libcutils/properties_test.cpp b/libcutils/properties_test.cpp
index 7921972..efc0183 100644
--- a/libcutils/properties_test.cpp
+++ b/libcutils/properties_test.cpp
@@ -93,160 +93,179 @@
}
};
-TEST_F(PropertiesTest, SetString) {
-
+TEST_F(PropertiesTest, property_set_null_key) {
// Null key -> unsuccessful set
- {
- // Null key -> fails
- EXPECT_GT(0, property_set(/*key*/NULL, PROPERTY_TEST_VALUE_DEFAULT));
- }
+ EXPECT_GT(0, property_set(/*key*/ NULL, PROPERTY_TEST_VALUE_DEFAULT));
+}
- // Null value -> returns default value
- {
- // Null value -> OK , and it clears the value
- EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));
- ResetValue();
+TEST_F(PropertiesTest, property_set_null_value) {
+ // Null value -> OK, and it clears the value
+ EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/ NULL));
+ ResetValue();
- // Since the value is null, default value will be returned
- size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
- EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
- EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
- }
+ // Since the value is null, default value will be returned
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
+ EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
+}
+TEST_F(PropertiesTest, property_set) {
// Trivial case => get returns what was set
- {
- size_t len = SetAndGetProperty("hello_world");
- EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
- EXPECT_STREQ("hello_world", mValue);
- ResetValue();
- }
+ size_t len = SetAndGetProperty("hello_world");
+ EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
+ EXPECT_STREQ("hello_world", mValue);
+ ResetValue();
+}
+TEST_F(PropertiesTest, property_set_empty) {
// Set to empty string => get returns default always
- {
- const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
- size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
- EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
- EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
- ResetValue();
- }
+ const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
+ size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
+ EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
+ EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
+ ResetValue();
+}
+TEST_F(PropertiesTest, property_set_max_length) {
// Set to max length => get returns what was set
- {
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
- int len = SetAndGetProperty(maxLengthString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX-1, len) << "max length key";
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
- }
+ int len = SetAndGetProperty(maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len) << "max length key";
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+}
+TEST_F(PropertiesTest, property_set_too_long) {
// Set to max length + 1 => set fails
- {
- const char* VALID_TEST_VALUE = "VALID_VALUE";
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));
+ const char* VALID_TEST_VALUE = "VALID_VALUE";
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));
- std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
- // Expect that the value set fails since it's too long
- EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
- size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ // Expect that the value set fails since it's too long
+ EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
- EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
- EXPECT_STREQ(VALID_TEST_VALUE, mValue);
- ResetValue();
- }
+ EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
+ EXPECT_STREQ(VALID_TEST_VALUE, mValue);
+ ResetValue();
}
-TEST_F(PropertiesTest, GetString) {
-
+TEST_F(PropertiesTest, property_get_too_long) {
// Try to use a default value that's too long => get truncates the value
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
- std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
- // Expect that the value is truncated since it's too long (by 1)
- int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a default value that's the max length => get succeeds
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
-
- // Expect that the value matches maxLengthString
- int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a default value of length one => get succeeds
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- std::string oneCharString = std::string(1, 'c');
-
- // Expect that the value matches oneCharString
- int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
- EXPECT_EQ(1, len);
- EXPECT_STREQ(oneCharString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a default value of length zero => get succeeds
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- std::string zeroCharString = std::string(0, 'd');
-
- // Expect that the value matches oneCharString
- int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
- EXPECT_EQ(0, len);
- EXPECT_STREQ(zeroCharString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a NULL default value => get returns 0
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- // Expect a return value of 0
- int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
- EXPECT_EQ(0, len);
- ResetValue();
- }
+ // Expect that the value is truncated since it's too long (by 1)
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
}
-TEST_F(PropertiesTest, GetBool) {
- /**
- * TRUE
- */
- const char *valuesTrue[] = { "1", "true", "y", "yes", "on", };
- for (size_t i = 0; i < arraysize(valuesTrue); ++i) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i]));
- bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);
- EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'";
- }
+TEST_F(PropertiesTest, property_get_default_too_long) {
+ // Try to use a default value that's the max length => get succeeds
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- /**
- * FALSE
- */
- const char *valuesFalse[] = { "0", "false", "n", "no", "off", };
- for (size_t i = 0; i < arraysize(valuesFalse); ++i) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i]));
- bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);
- EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'";
- }
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
- /**
- * NEITHER
- */
+ // Expect that the value matches maxLengthString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_default_okay) {
+ // Try to use a default value of length one => get succeeds
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string oneCharString = std::string(1, 'c');
+
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
+ EXPECT_EQ(1, len);
+ EXPECT_STREQ(oneCharString.c_str(), mValue);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_default_empty) {
+ // Try to use a default value of length zero => get succeeds
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string zeroCharString = std::string(0, 'd');
+
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
+ EXPECT_EQ(0, len);
+ EXPECT_STREQ(zeroCharString.c_str(), mValue);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_default_NULL) {
+ // Try to use a NULL default value => get returns 0
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ // Expect a return value of 0
+ int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
+ EXPECT_EQ(0, len);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_bool_0) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "0"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_1) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "1"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_false) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "false"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_n) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "n"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_no) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "no"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_off) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "off"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_on) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "on"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_true) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "true"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_y) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "y"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_yes) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "yes"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_neither) {
const char *valuesNeither[] = { "x0", "x1", "2", "-2", "True", "False", "garbage", "", " ",
"+1", " 1 ", " true", " true ", " y ", " yes", "yes ",
"+0", "-0", "00", " 00 ", " false", "false ",
@@ -263,7 +282,7 @@
}
}
-TEST_F(PropertiesTest, GetInt64) {
+TEST_F(PropertiesTest, property_get_int64) {
const int64_t DEFAULT_VALUE = INT64_C(0xDEADBEEFBEEFDEAD);
const std::string longMaxString = ToString(INT64_MAX);
@@ -310,7 +329,7 @@
}
}
-TEST_F(PropertiesTest, GetInt32) {
+TEST_F(PropertiesTest, property_get_int32) {
const int32_t DEFAULT_VALUE = INT32_C(0xDEADBEEF);
const std::string intMaxString = ToString(INT32_MAX);
diff --git a/liblog/README.md b/liblog/README.md
index f64f376..74a2cd7 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -60,8 +60,6 @@
LOG_EVENT_INT(tag, value)
LOG_EVENT_LONG(tag, value)
- clockid_t android_log_clockid()
-
log_id_t android_logger_get_id(struct logger *logger)
int android_logger_clear(struct logger *logger)
int android_logger_get_log_size(struct logger *logger)
@@ -119,7 +117,7 @@
multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
`android_logger_open()` for each log id. Each entry can be retrieved with
`android_logger_list_read()`. The log(s) can be closed with `android_logger_list_free()`.
-`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
+`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
code, otherwise the `android_logger_list_read()` call will block for new entries.
The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 820b7cb..d7e9b7d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -133,12 +133,6 @@
(void)__android_log_bswrite(_tag, _value);
#endif
-#ifdef __linux__
-
-clockid_t android_log_clockid(void);
-
-#endif /* __linux__ */
-
/* --------------------------------------------------------------------- */
/*
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 24b88d2..23d76f4 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -112,7 +112,6 @@
/* Multiple log_id_t opens */
struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
-#define android_logger_close android_logger_free
/* Single log_id_t open */
struct logger_list* android_logger_list_open(log_id_t id, int mode,
unsigned int tail, pid_t pid);
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 6b4458c..f50764d 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -37,9 +37,7 @@
uint32_t tv_sec = 0; /* good to Feb 5 2106 */
uint32_t tv_nsec = 0;
- static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
- static const uint32_t tv_nsec_max = 999999999UL;
- static const timespec EPOCH;
+ static constexpr timespec EPOCH = {0, 0};
log_time() {}
explicit log_time(const timespec& T)
@@ -55,16 +53,6 @@
tv_nsec = static_cast<uint32_t>(T.tv_nsec);
}
#endif
- explicit log_time(const char* T) {
- const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
- tv_sec = c[0] | (static_cast<uint32_t>(c[1]) << 8) |
- (static_cast<uint32_t>(c[2]) << 16) |
- (static_cast<uint32_t>(c[3]) << 24);
- tv_nsec = c[4] | (static_cast<uint32_t>(c[5]) << 8) |
- (static_cast<uint32_t>(c[6]) << 16) |
- (static_cast<uint32_t>(c[7]) << 24);
- }
-
/* timespec */
bool operator==(const timespec& T) const {
return (tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
@@ -90,17 +78,6 @@
return !(*this > T);
}
- log_time operator-=(const timespec& T);
- log_time operator-(const timespec& T) const {
- log_time local(*this);
- return local -= T;
- }
- log_time operator+=(const timespec& T);
- log_time operator+(const timespec& T) const {
- log_time local(*this);
- return local += T;
- }
-
/* log_time */
bool operator==(const log_time& T) const {
return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
@@ -123,12 +100,36 @@
return !(*this > T);
}
- log_time operator-=(const log_time& T);
+ log_time operator-=(const log_time& T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = log_time(EPOCH);
+ }
+
+ if (this->tv_nsec < T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+ }
log_time operator-(const log_time& T) const {
log_time local(*this);
return local -= T;
}
- log_time operator+=(const log_time& T);
+ log_time operator+=(const log_time& T) {
+ this->tv_nsec += T.tv_nsec;
+ if (this->tv_nsec >= NS_PER_SEC) {
+ this->tv_nsec -= NS_PER_SEC;
+ ++this->tv_sec;
+ }
+ this->tv_sec += T.tv_sec;
+
+ return *this;
+ }
log_time operator+(const log_time& T) const {
log_time local(*this);
return local += T;
@@ -146,10 +147,8 @@
tv_nsec / (NS_PER_SEC / MS_PER_SEC);
}
- static const char default_format[];
-
/* Add %#q for the fraction of a second to the standard library functions */
- char* strptime(const char* s, const char* format = default_format);
+ char* strptime(const char* s, const char* format);
} __attribute__((__packed__));
}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 3fbe1cb..14c408c 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -21,11 +21,7 @@
#include <private/android_logger.h>
-const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-const timespec log_time::EPOCH = {0, 0};
-
// Add %#q for fractional seconds to standard strptime function
-
char* log_time::strptime(const char* s, const char* format) {
time_t now;
#ifdef __linux__
@@ -131,59 +127,3 @@
#endif
return ret;
}
-
-log_time log_time::operator-=(const timespec& T) {
- // No concept of negative time, clamp to EPOCH
- if (*this <= T) {
- return *this = log_time(EPOCH);
- }
-
- if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
- --this->tv_sec;
- this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
- } else {
- this->tv_nsec -= T.tv_nsec;
- }
- this->tv_sec -= T.tv_sec;
-
- return *this;
-}
-
-log_time log_time::operator+=(const timespec& T) {
- this->tv_nsec += (unsigned long int)T.tv_nsec;
- if (this->tv_nsec >= NS_PER_SEC) {
- this->tv_nsec -= NS_PER_SEC;
- ++this->tv_sec;
- }
- this->tv_sec += T.tv_sec;
-
- return *this;
-}
-
-log_time log_time::operator-=(const log_time& T) {
- // No concept of negative time, clamp to EPOCH
- if (*this <= T) {
- return *this = log_time(EPOCH);
- }
-
- if (this->tv_nsec < T.tv_nsec) {
- --this->tv_sec;
- this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
- } else {
- this->tv_nsec -= T.tv_nsec;
- }
- this->tv_sec -= T.tv_sec;
-
- return *this;
-}
-
-log_time log_time::operator+=(const log_time& T) {
- this->tv_nsec += T.tv_nsec;
- if (this->tv_nsec >= NS_PER_SEC) {
- this->tv_nsec -= NS_PER_SEC;
- ++this->tv_sec;
- }
- this->tv_sec += T.tv_sec;
-
- return *this;
-}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 3a75fa3..22c7eca 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -192,7 +192,7 @@
return -EINVAL;
}
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
if (log_id == LOG_ID_SECURITY) {
if (vec[0].iov_len < 4) {
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 9e8d277..238431f 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -216,11 +216,7 @@
p_ret->year_output = false;
p_ret->zone_output = false;
p_ret->epoch_output = false;
-#ifdef __ANDROID__
- p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
-#else
p_ret->monotonic_output = false;
-#endif
p_ret->uid_output = false;
p_ret->descriptive_output = false;
descriptive_output = false;
@@ -1465,13 +1461,10 @@
nsec = entry->tv_nsec;
#if __ANDROID__
if (p_format->monotonic_output) {
- /* prevent convertMonotonic from being called if logd is monotonic */
- if (android_log_clockid() != CLOCK_MONOTONIC) {
- struct timespec time;
- convertMonotonic(&time, entry);
- now = time.tv_sec;
- nsec = time.tv_nsec;
- }
+ struct timespec time;
+ convertMonotonic(&time, entry);
+ now = time.tv_sec;
+ nsec = time.tv_nsec;
}
#endif
if (now < 0) {
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 0751e2c..8e676bd 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -188,7 +188,7 @@
return -EINVAL;
}
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
cp = strdup(filename);
if (!cp) {
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 37670ec..f5e060c 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -365,29 +365,6 @@
return c;
}
-static unsigned char evaluate_persist_ro(const struct cache2_char* self) {
- unsigned char c = self->cache_persist.c;
-
- if (c) {
- return c;
- }
-
- return self->cache_ro.c;
-}
-
-/*
- * Timestamp state generally remains constant, but can change at any time
- * to handle developer requirements.
- */
-clockid_t android_log_clockid() {
- static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
- "persist.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
- "ro.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
- evaluate_persist_ro};
-
- return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
-}
-
/*
* Security state generally remains constant, but the DO must be able
* to turn off logging should it become spammy after an attack is detected.
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index f4734b9..3bd5cf2 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -184,7 +184,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
android_pmsg_log_header_t pmsg_header;
pmsg_header.magic = LOGGER_MAGIC;
@@ -260,7 +260,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -335,7 +335,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -410,7 +410,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -483,7 +483,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -684,8 +684,8 @@
if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
continue;
}
- log_time tx(eventData + 4 + 1);
- if (ts != tx) {
+ log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+ if (ts != *tx) {
if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) {
state.SkipWithError("signal");
break;
@@ -757,8 +757,8 @@
if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
continue;
}
- log_time tx(eventData + 4 + 1);
- if (ts != tx) {
+ log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+ if (ts != *tx) {
if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) {
state.SkipWithError("signal");
break;
@@ -792,16 +792,6 @@
BENCHMARK(BM_is_loggable);
/*
- * Measure the time it takes for android_log_clockid.
- */
-static void BM_clockid(benchmark::State& state) {
- while (state.KeepRunning()) {
- android_log_clockid();
- }
-}
-BENCHMARK(BM_clockid);
-
-/*
* Measure the time it takes for __android_log_security.
*/
static void BM_security(benchmark::State& state) {
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index d3d8e91..fbc3d7a 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -270,10 +270,10 @@
return;
}
- log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(&eventData->payload.data);
+ if (ts == *tx) {
++count;
- } else if (ts1 == tx) {
+ } else if (ts1 == *tx) {
++second_count;
}
@@ -329,8 +329,6 @@
#ifdef __ANDROID__
pid_t pid = getpid();
- log_time ts(android_log_clockid());
-
size_t num_lines = 1, size = 0, length = 0, total = 0;
const char* cp = message;
while (*cp) {
@@ -432,7 +430,6 @@
pid_t pid = getpid();
static const char tag[] = "TEST__android_log_buf_write";
- log_time ts(android_log_clockid());
auto write_function = [&] {
EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index 9ecdd4f..64de00e 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -37,25 +37,22 @@
#include "dhcpmsg.h"
-int fatal();
+int fatal(const char*);
-int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
-{
- int s;
- struct sockaddr_ll bindaddr;
+int open_raw_socket(const char* ifname __unused, uint8_t hwaddr[ETH_ALEN], int if_index) {
+ int s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (s < 0) return fatal("socket(PF_PACKET)");
- if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
- return fatal("socket(PF_PACKET)");
- }
-
- memset(&bindaddr, 0, sizeof(bindaddr));
- bindaddr.sll_family = AF_PACKET;
- bindaddr.sll_protocol = htons(ETH_P_IP);
- bindaddr.sll_halen = ETH_ALEN;
+ struct sockaddr_ll bindaddr = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_IP),
+ .sll_ifindex = if_index,
+ .sll_halen = ETH_ALEN,
+ };
memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
- bindaddr.sll_ifindex = if_index;
if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+ close(s);
return fatal("Cannot bind raw socket to interface");
}
diff --git a/libnetutils/packet.h b/libnetutils/packet.h
index aade392..66186fc 100644
--- a/libnetutils/packet.h
+++ b/libnetutils/packet.h
@@ -17,7 +17,9 @@
#ifndef _WIFI_PACKET_H_
#define _WIFI_PACKET_H_
-int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
+#include <linux/if_ether.h>
+
+int open_raw_socket(const char* ifname, uint8_t hwaddr[ETH_ALEN], int if_index);
int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
int receive_packet(int s, struct dhcp_msg *msg);
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 766ea0f..ccc6f62 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -89,15 +89,15 @@
"test_vendor.cpp",
],
static_libs: [
+ "libbase",
"libgmock",
+ "liblog",
+ "libjsoncpp",
"libjsonpbverify",
"libjsonpbparse",
"libprocessgroup_proto",
],
shared_libs: [
- "libbase",
- "liblog",
- "libjsoncpp",
"libprotobuf-cpp-full",
],
test_suites: [
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 0c9a2b8..15b0d89 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -51,6 +51,12 @@
enabled: false,
},
},
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.art.debug",
+ "com.android.art.release",
+ ],
}
// Tests
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index e35cb0d..b883c13 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -35,8 +35,9 @@
#include "sparse_crc32.h"
#include "sparse_format.h"
+#include <android-base/mapped_file.h>
+
#ifndef _WIN32
-#include <sys/mman.h>
#define O_BINARY 0
#else
#define ftruncate64 ftruncate
@@ -45,7 +46,6 @@
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define ftruncate64 ftruncate
-#define mmap64 mmap
#define off64_t off_t
#endif
@@ -649,52 +649,10 @@
}
int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
- int ret;
- int64_t aligned_offset;
- int aligned_diff;
- uint64_t buffer_size;
- char* ptr;
+ auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
+ if (!m) return -errno;
- aligned_offset = offset & ~(4096 - 1);
- aligned_diff = offset - aligned_offset;
- buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
- if (buffer_size > SIZE_MAX) return -E2BIG;
- char* data =
- reinterpret_cast<char*>(mmap64(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
- if (data == MAP_FAILED) {
- return -errno;
- }
- ptr = data + aligned_diff;
-#else
- off64_t pos;
- char* data = reinterpret_cast<char*>(malloc(len));
- if (!data) {
- return -errno;
- }
- pos = lseek64(fd, offset, SEEK_SET);
- if (pos < 0) {
- free(data);
- return -errno;
- }
- ret = read_all(fd, data, len);
- if (ret < 0) {
- free(data);
- return ret;
- }
- ptr = data;
-#endif
-
- ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
- munmap(data, buffer_size);
-#else
- free(data);
-#endif
-
- return ret;
+ return out->sparse_ops->write_data_chunk(out, m->size(), m->data());
}
/* Write a contiguous region of data blocks from a file */
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index ff886fd..db61669 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -8,6 +8,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "apex_inherit",
export_include_dirs: ["include"],
target: {
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 627f0d4..3b98bab 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -43,6 +43,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "apex_inherit",
}
cc_test {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 5efe03f..9c1621b 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -180,6 +180,7 @@
struct ifa_cacheinfo *cacheinfo = nullptr;
char addrstr[INET6_ADDRSTRLEN] = "";
char ifname[IFNAMSIZ] = "";
+ uint32_t flags;
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
@@ -194,6 +195,9 @@
// For log messages.
const char *msgtype = rtMessageName(type);
+ // First 8 bits of flags. In practice will always be overridden when parsing IFA_FLAGS below.
+ flags = ifaddr->ifa_flags;
+
struct rtattr *rta;
int len = IFA_PAYLOAD(nh);
for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
@@ -242,6 +246,9 @@
}
cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
+
+ } else if (rta->rta_type == IFA_FLAGS) {
+ flags = *(uint32_t*)RTA_DATA(rta);
}
}
@@ -256,7 +263,7 @@
mSubsystem = strdup("net");
asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen);
asprintf(&mParams[1], "INTERFACE=%s", ifname);
- asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
+ asprintf(&mParams[2], "FLAGS=%u", flags);
asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
asprintf(&mParams[4], "IFINDEX=%u", ifaddr->ifa_index);
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index f3d3f27..3c44534 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -146,6 +146,12 @@
exclude_shared_libs: ["libdexfile_support"],
},
},
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.art.debug",
+ "com.android.art.release",
+ ],
}
// Static library without DEX support to avoid dependencies on the ART APEX.
@@ -411,6 +417,7 @@
srcs: [
"benchmarks/unwind_benchmarks.cpp",
"benchmarks/ElfBenchmark.cpp",
+ "benchmarks/MapsBenchmark.cpp",
"benchmarks/SymbolBenchmark.cpp",
"benchmarks/Utils.cpp",
],
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index f01b092..286febc 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -124,6 +124,12 @@
return false;
}
+ if (arch() == ARCH_ARM64) {
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ vaddr &= (1ULL << 56) - 1;
+ }
+
// Check the .data section.
uint64_t vaddr_start = interface_->data_vaddr_start();
if (vaddr >= vaddr_start && vaddr < interface_->data_vaddr_end()) {
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
index c108a2a..a46bd7a 100644
--- a/libunwindstack/benchmarks/ElfBenchmark.cpp
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -23,7 +23,9 @@
#include <benchmark/benchmark.h>
#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
#include "Utils.h"
@@ -75,3 +77,66 @@
BenchmarkElfCreate(state, GetCompressedElfFile());
}
BENCHMARK(BM_elf_create_compressed);
+
+static void InitializeBuildId(benchmark::State& state, unwindstack::Maps& maps,
+ unwindstack::MapInfo** build_id_map_info) {
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ return;
+ }
+
+ // Find the libc.so share library and use that for benchmark purposes.
+ *build_id_map_info = nullptr;
+ for (auto& map_info : maps) {
+ if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+ *build_id_map_info = map_info.get();
+ break;
+ }
+ }
+
+ if (*build_id_map_info == nullptr) {
+ state.SkipWithError("Failed to find a map with a BuildID.");
+ }
+}
+
+static void BM_elf_get_build_id_from_object(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ InitializeBuildId(state, maps, &build_id_map_info);
+
+ unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+ unwindstack::Regs::CurrentArch());
+ if (!elf->valid()) {
+ state.SkipWithError("Cannot get valid elf from map.");
+ }
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ state.ResumeTiming();
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_elf_get_build_id_from_object);
+
+static void BM_elf_get_build_id_from_file(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ InitializeBuildId(state, maps, &build_id_map_info);
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ state.ResumeTiming();
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_elf_get_build_id_from_file);
diff --git a/libunwindstack/benchmarks/MapsBenchmark.cpp b/libunwindstack/benchmarks/MapsBenchmark.cpp
new file mode 100644
index 0000000..5df1491
--- /dev/null
+++ b/libunwindstack/benchmarks/MapsBenchmark.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 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 <err.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Maps.h>
+
+class BenchmarkLocalUpdatableMaps : public unwindstack::LocalUpdatableMaps {
+ public:
+ BenchmarkLocalUpdatableMaps() : unwindstack::LocalUpdatableMaps() {}
+ virtual ~BenchmarkLocalUpdatableMaps() = default;
+
+ const std::string GetMapsFile() const override { return maps_file_; }
+
+ void BenchmarkSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
+
+ private:
+ std::string maps_file_;
+};
+
+static constexpr size_t kNumSmallMaps = 100;
+static constexpr size_t kNumLargeMaps = 10000;
+
+static void CreateMap(const char* filename, size_t num_maps, size_t increment = 1) {
+ std::string maps;
+ for (size_t i = 0; i < num_maps; i += increment) {
+ maps += android::base::StringPrintf("%zu-%zu r-xp 0000 00:00 0 name%zu\n", i * 1000,
+ (i + 1) * 1000 * increment, i * increment);
+ }
+ if (!android::base::WriteStringToFile(maps, filename)) {
+ errx(1, "WriteStringToFile failed");
+ }
+}
+
+static void ReparseBenchmark(benchmark::State& state, const char* maps1, size_t maps1_total,
+ const char* maps2, size_t maps2_total) {
+ for (auto _ : state) {
+ BenchmarkLocalUpdatableMaps maps;
+ maps.BenchmarkSetMapsFile(maps1);
+ if (!maps.Reparse()) {
+ errx(1, "Internal Error: reparse of initial maps filed.");
+ }
+ if (maps.Total() != maps1_total) {
+ errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
+ maps1_total);
+ }
+ maps.BenchmarkSetMapsFile(maps2);
+ if (!maps.Reparse()) {
+ errx(1, "Internal Error: reparse of second set of maps filed.");
+ }
+ if (maps.Total() != maps2_total) {
+ errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
+ maps2_total);
+ }
+ }
+}
+
+void BM_local_updatable_maps_reparse_double_initial_small(benchmark::State& state) {
+ TemporaryFile initial_maps;
+ CreateMap(initial_maps.path, kNumSmallMaps, 2);
+
+ TemporaryFile reparse_maps;
+ CreateMap(reparse_maps.path, kNumSmallMaps);
+
+ ReparseBenchmark(state, initial_maps.path, kNumSmallMaps / 2, reparse_maps.path, kNumSmallMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_double_initial_small);
+
+void BM_local_updatable_maps_reparse_double_initial_large(benchmark::State& state) {
+ TemporaryFile initial_maps;
+ CreateMap(initial_maps.path, kNumLargeMaps, 2);
+
+ TemporaryFile reparse_maps;
+ CreateMap(reparse_maps.path, kNumLargeMaps);
+
+ ReparseBenchmark(state, initial_maps.path, kNumLargeMaps / 2, reparse_maps.path, kNumLargeMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_double_initial_large);
+
+void BM_local_updatable_maps_reparse_same_maps_small(benchmark::State& state) {
+ static constexpr size_t kNumSmallMaps = 100;
+ TemporaryFile maps;
+ CreateMap(maps.path, kNumSmallMaps);
+
+ ReparseBenchmark(state, maps.path, kNumSmallMaps, maps.path, kNumSmallMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_same_maps_small);
+
+void BM_local_updatable_maps_reparse_same_maps_large(benchmark::State& state) {
+ TemporaryFile maps;
+ CreateMap(maps.path, kNumLargeMaps);
+
+ ReparseBenchmark(state, maps.path, kNumLargeMaps, maps.path, kNumLargeMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_same_maps_large);
+
+void BM_local_updatable_maps_reparse_few_extra_small(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumSmallMaps - 4);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumSmallMaps);
+
+ ReparseBenchmark(state, maps1.path, kNumSmallMaps - 4, maps2.path, kNumSmallMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_extra_small);
+
+void BM_local_updatable_maps_reparse_few_extra_large(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumLargeMaps - 4);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumLargeMaps);
+
+ ReparseBenchmark(state, maps1.path, kNumLargeMaps - 4, maps2.path, kNumLargeMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_extra_large);
+
+void BM_local_updatable_maps_reparse_few_less_small(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumSmallMaps);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumSmallMaps - 4);
+
+ ReparseBenchmark(state, maps1.path, kNumSmallMaps, maps2.path, kNumSmallMaps - 4);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_less_small);
+
+void BM_local_updatable_maps_reparse_few_less_large(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumLargeMaps);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumLargeMaps - 4);
+
+ ReparseBenchmark(state, maps1.path, kNumLargeMaps, maps2.path, kNumLargeMaps - 4);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_less_large);
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index de9137a..0bee6ef 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -22,7 +22,6 @@
#include <android-base/strings.h>
-#include <unwindstack/Elf.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -83,63 +82,4 @@
}
BENCHMARK(BM_cached_unwind);
-static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
- unwindstack::MapInfo** build_id_map_info) {
- if (!maps.Parse()) {
- state.SkipWithError("Failed to parse local maps.");
- return;
- }
-
- // Find the libc.so share library and use that for benchmark purposes.
- *build_id_map_info = nullptr;
- for (auto& map_info : maps) {
- if (map_info->offset == 0 && map_info->GetBuildID() != "") {
- *build_id_map_info = map_info.get();
- break;
- }
- }
-
- if (*build_id_map_info == nullptr) {
- state.SkipWithError("Failed to find a map with a BuildID.");
- }
-}
-
-static void BM_get_build_id_from_elf(benchmark::State& state) {
- unwindstack::LocalMaps maps;
- unwindstack::MapInfo* build_id_map_info;
- Initialize(state, maps, &build_id_map_info);
-
- unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
- unwindstack::Regs::CurrentArch());
- if (!elf->valid()) {
- state.SkipWithError("Cannot get valid elf from map.");
- }
-
- for (auto _ : state) {
- uintptr_t id = build_id_map_info->build_id;
- if (id != 0) {
- delete reinterpret_cast<std::string*>(id);
- build_id_map_info->build_id = 0;
- }
- benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
- }
-}
-BENCHMARK(BM_get_build_id_from_elf);
-
-static void BM_get_build_id_from_file(benchmark::State& state) {
- unwindstack::LocalMaps maps;
- unwindstack::MapInfo* build_id_map_info;
- Initialize(state, maps, &build_id_map_info);
-
- for (auto _ : state) {
- uintptr_t id = build_id_map_info->build_id;
- if (id != 0) {
- delete reinterpret_cast<std::string*>(id);
- build_id_map_info->build_id = 0;
- }
- benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
- }
-}
-BENCHMARK(BM_get_build_id_from_file);
-
BENCHMARK_MAIN();
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index fc90dab..3b6cb80 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -55,6 +55,8 @@
void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+ void FakeSetArch(ArchEnum arch) { arch_ = arch; }
+
void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
gnu_debugdata_interface_.reset(interface);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1f3ed81..f0852a4 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -438,6 +438,48 @@
EXPECT_EQ(0xc080U, offset);
}
+TEST_F(ElfTest, get_global_vaddr_with_tagged_pointer) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetArch(ARCH_ARM64);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x500);
+ interface->MockSetDataVaddrEnd(0x600);
+ interface->MockSetDataOffset(0xa000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+ ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0xa080U, offset);
+}
+
+TEST_F(ElfTest, get_global_vaddr_without_tagged_pointer) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetArch(ARCH_X86_64);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x8800000000000500);
+ interface->MockSetDataVaddrEnd(0x8800000000000600);
+ interface->MockSetDataOffset(0x880000000000a000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+ ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0x880000000000a080U, offset);
+}
+
TEST_F(ElfTest, is_valid_pc_elf_invalid) {
ElfFake elf(memory_);
elf.FakeSetValid(false);
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 3ab619b..ea39d34 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -22,6 +22,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "apex_inherit",
header_libs: [
"liblog_headers",
@@ -162,6 +163,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "apex_inherit",
}
cc_library {
@@ -173,8 +175,8 @@
],
shared_libs: [
- "libutils",
- "libbacktrace",
+ "libutils",
+ "libbacktrace",
],
target: {
@@ -192,6 +194,45 @@
},
}
+cc_defaults {
+ name: "libutils_fuzz_defaults",
+ host_supported: true,
+ shared_libs: [
+ "libutils",
+ "libbase",
+ ],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_bitset",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["BitSet_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_filemap",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["FileMap_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_string8",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["String8_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_string16",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["String16_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_vector",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["Vector_fuzz.cpp"],
+}
+
cc_test {
name: "libutils_test",
host_supported: true,
@@ -205,6 +246,7 @@
"String8_test.cpp",
"String16_test.cpp",
"StrongPointer_test.cpp",
+ "Timers_test.cpp",
"Unicode_test.cpp",
"Vector_test.cpp",
],
diff --git a/libutils/BitSet_fuzz.cpp b/libutils/BitSet_fuzz.cpp
new file mode 100644
index 0000000..2e6043c
--- /dev/null
+++ b/libutils/BitSet_fuzz.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/BitSet.h"
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+// We need to handle both 32 and 64 bit bitsets, so we use a function template
+// here. Sadly, std::function can't be generic, so we generate a vector of
+// std::functions using this function.
+template <typename T>
+std::vector<std::function<void(T, uint32_t)>> getOperationsForType() {
+ return {
+ [](T bs, uint32_t val) -> void { bs.markBit(val); },
+ [](T bs, uint32_t val) -> void { bs.valueForBit(val); },
+ [](T bs, uint32_t val) -> void { bs.hasBit(val); },
+ [](T bs, uint32_t val) -> void { bs.clearBit(val); },
+ [](T bs, uint32_t val) -> void { bs.getIndexOfBit(val); },
+ [](T bs, uint32_t) -> void { bs.clearFirstMarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.markFirstUnmarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.clearLastMarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.clear(); },
+ [](T bs, uint32_t) -> void { bs.count(); },
+ [](T bs, uint32_t) -> void { bs.isEmpty(); },
+ [](T bs, uint32_t) -> void { bs.isFull(); },
+ [](T bs, uint32_t) -> void { bs.firstMarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.lastMarkedBit(); },
+ };
+}
+
+// Our operations for 32 and 64 bit bitsets
+static const std::vector<std::function<void(android::BitSet32, uint32_t)>> thirtyTwoBitOps =
+ getOperationsForType<android::BitSet32>();
+static const std::vector<std::function<void(android::BitSet64, uint32_t)>> sixtyFourBitOps =
+ getOperationsForType<android::BitSet64>();
+
+void runOperationFor32Bit(android::BitSet32 bs, uint32_t bit, uint8_t operation) {
+ thirtyTwoBitOps[operation](bs, bit);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ uint32_t thirty_two_base = dataProvider.ConsumeIntegral<uint32_t>();
+ uint64_t sixty_four_base = dataProvider.ConsumeIntegral<uint64_t>();
+ android::BitSet32 b1 = android::BitSet32(thirty_two_base);
+ android::BitSet64 b2 = android::BitSet64(sixty_four_base);
+
+ size_t opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint32_t bit = dataProvider.ConsumeIntegral<uint32_t>();
+ uint8_t op = dataProvider.ConsumeIntegral<uint8_t>();
+ thirtyTwoBitOps[op % thirtyTwoBitOps.size()](b1, bit);
+ sixtyFourBitOps[op % sixtyFourBitOps.size()](b2, bit);
+ }
+ return 0;
+}
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1202c15..0abb861 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -189,13 +189,17 @@
int adjust = offset % mPageSize;
off64_t adjOffset = offset - adjust;
- size_t adjLength = length + adjust;
+ size_t adjLength;
+ if (__builtin_add_overflow(length, adjust, &adjLength)) {
+ ALOGE("adjusted length overflow: length %zu adjust %d", length, adjust);
+ return false;
+ }
int flags = MAP_SHARED;
int prot = PROT_READ;
if (!readOnly) prot |= PROT_WRITE;
- void* ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
+ void* ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset);
if (ptr == MAP_FAILED) {
if (errno == EINVAL && length == 0) {
ptr = nullptr;
diff --git a/libutils/FileMap_fuzz.cpp b/libutils/FileMap_fuzz.cpp
new file mode 100644
index 0000000..d800564
--- /dev/null
+++ b/libutils/FileMap_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 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 <iostream>
+
+#include "android-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/FileMap.h"
+
+static constexpr uint16_t MAX_STR_SIZE = 256;
+static constexpr uint8_t MAX_FILENAME_SIZE = 32;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ TemporaryFile tf;
+ // Generate file contents
+ std::string contents = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+ // If we have string contents, dump them into the file.
+ // Otherwise, just leave it as an empty file.
+ if (contents.length() > 0) {
+ const char* bytes = contents.c_str();
+ android::base::WriteStringToFd(bytes, tf.fd);
+ }
+ android::FileMap m;
+ // Generate create() params
+ std::string orig_name = dataProvider.ConsumeRandomLengthString(MAX_FILENAME_SIZE);
+ size_t length = dataProvider.ConsumeIntegralInRange<size_t>(1, SIZE_MAX);
+ off64_t offset = dataProvider.ConsumeIntegralInRange<off64_t>(1, INT64_MAX);
+ bool read_only = dataProvider.ConsumeBool();
+ m.create(orig_name.c_str(), tf.fd, offset, length, read_only);
+ m.getDataOffset();
+ m.getFileName();
+ m.getDataLength();
+ m.getDataPtr();
+ int enum_index = dataProvider.ConsumeIntegral<int>();
+ m.advise(static_cast<android::FileMap::MapAdvice>(enum_index));
+ return 0;
+}
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
index 576d89b..fd1c9b0 100644
--- a/libutils/FileMap_test.cpp
+++ b/libutils/FileMap_test.cpp
@@ -32,3 +32,36 @@
ASSERT_EQ(0u, m.getDataLength());
ASSERT_EQ(4096, m.getDataOffset());
}
+
+TEST(FileMap, large_offset) {
+ // Make sure that an offset > INT32_MAX will not fail the create
+ // function. See http://b/155662887.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ off64_t offset = INT32_MAX + 1024LL;
+
+ // Make the temporary file large enough to pass the mmap.
+ ASSERT_EQ(offset, lseek64(tf.fd, offset, SEEK_SET));
+ char value = 0;
+ ASSERT_EQ(1, write(tf.fd, &value, 1));
+
+ android::FileMap m;
+ ASSERT_TRUE(m.create("test", tf.fd, offset, 0, true));
+ ASSERT_STREQ("test", m.getFileName());
+ ASSERT_EQ(0u, m.getDataLength());
+ ASSERT_EQ(offset, m.getDataOffset());
+}
+
+TEST(FileMap, offset_overflow) {
+ // Make sure that an end that overflows SIZE_MAX will not abort.
+ // See http://b/156997193.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ off64_t offset = 200;
+ size_t length = SIZE_MAX;
+
+ android::FileMap m;
+ ASSERT_FALSE(m.create("test", tf.fd, offset, length, true));
+}
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
new file mode 100644
index 0000000..63c2800
--- /dev/null
+++ b/libutils/String16_fuzz.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2020 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 <iostream>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/String16.h"
+static constexpr int MAX_STRING_BYTES = 256;
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+std::vector<std::function<void(FuzzedDataProvider&, android::String16, android::String16)>>
+ operations = {
+
+ // Bytes and size
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.string();
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.isStaticString();
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.size();
+ }),
+
+ // Casing
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.makeLower();
+ }),
+
+ // Comparison
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.startsWith(str2);
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.contains(str2.string());
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.compare(str2);
+ }),
+
+ // Append and format
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.append(str2);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16 str2) -> void {
+ int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
+ str1.insert(pos, str2.string());
+ }),
+
+ // Find and replace operations
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+ str1.findFirst(findChar);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+ str1.findLast(findChar);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+ char16_t replaceChar = dataProvider.ConsumeIntegral<char16_t>();
+ str1.replaceAll(findChar, replaceChar);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ size_t len = dataProvider.ConsumeIntegral<size_t>();
+ size_t begin = dataProvider.ConsumeIntegral<size_t>();
+ str1.remove(len, begin);
+ }),
+};
+
+void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16 str2) {
+ operations[index](dataProvider, str1, str2);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ // We're generating two char vectors.
+ // First, generate lengths.
+ const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+ const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+
+ // Next, populate the vectors
+ std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
+ std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
+
+ // Get pointers to their data
+ char* char_one = vec.data();
+ char* char_two = vec_two.data();
+
+ // Create UTF16 representations
+ android::String16 str_one_utf16 = android::String16(char_one);
+ android::String16 str_two_utf16 = android::String16(char_two);
+
+ // Run operations against strings
+ int opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ callFunc(op, dataProvider, str_one_utf16, str_two_utf16);
+ }
+
+ str_one_utf16.remove(0, str_one_utf16.size());
+ str_two_utf16.remove(0, str_two_utf16.size());
+ return 0;
+}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
new file mode 100644
index 0000000..2adfe98
--- /dev/null
+++ b/libutils/String8_fuzz.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <functional>
+#include <iostream>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_STRING_BYTES = 256;
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+std::vector<std::function<void(FuzzedDataProvider&, android::String8, android::String8)>>
+ operations = {
+
+ // Bytes and size
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.bytes();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.isEmpty();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.length();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.size();
+ },
+
+ // Casing
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.toUpper();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.toLower();
+ },
+
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.removeAll(str2.c_str());
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.compare(str2);
+ },
+
+ // Append and format
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.append(str2);
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.appendFormat(str1.c_str(), str2.c_str());
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.format(str1.c_str(), str2.c_str());
+ },
+
+ // Find operation
+ [](FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8) -> void {
+ // We need to get a value from our fuzzer here.
+ int start_index = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
+ str1.find(str1.c_str(), start_index);
+ },
+
+ // Path handling
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getBasePath();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getPathExtension();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getPathLeaf();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getPathDir();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.convertToResPath();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ android::String8 path_out_str = android::String8();
+ str1.walkPath(&path_out_str);
+ path_out_str.clear();
+ },
+ [](FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8) -> void {
+ str1.setPathName(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ },
+ [](FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8) -> void {
+ str1.appendPath(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ },
+};
+
+void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8 str2) {
+ operations[index](dataProvider, str1, str2);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ // Generate vector lengths
+ const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+ const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+ // Populate vectors
+ std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
+ std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
+ // Create UTF-8 pointers
+ android::String8 str_one_utf8 = android::String8(vec.data());
+ android::String8 str_two_utf8 = android::String8(vec_two.data());
+
+ // Run operations against strings
+ int opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ callFunc(op, dataProvider, str_one_utf8, str_two_utf8);
+ }
+
+ // Just to be extra sure these can be freed, we're going to explicitly clear
+ // them
+ str_one_utf8.clear();
+ str_two_utf8.clear();
+ return 0;
+}
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 540dcf4..147db54 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -302,8 +302,7 @@
}
#if defined(__ANDROID__)
-int androidSetThreadPriority(pid_t tid, int pri)
-{
+int androidSetThreadPriority(pid_t tid, int pri, bool change_policy) {
int rc = 0;
int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
@@ -312,17 +311,19 @@
return rc;
}
- if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
- } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
- SchedPolicy policy = SP_FOREGROUND;
- // Change to the sched policy group of the process.
- get_sched_policy(getpid(), &policy);
- rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
- }
+ if (change_policy) {
+ if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
+ } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
+ SchedPolicy policy = SP_FOREGROUND;
+ // Change to the sched policy group of the process.
+ get_sched_policy(getpid(), &policy);
+ rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
+ }
- if (rc) {
- lasterr = errno;
+ if (rc) {
+ lasterr = errno;
+ }
}
if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 1172ae7..fd3f4a9 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -20,31 +20,37 @@
#include <utils/Timers.h>
#include <limits.h>
+#include <stdlib.h>
#include <time.h>
-// host linux support requires Linux 2.6.39+
+#include <android-base/macros.h>
+
+static constexpr size_t clock_id_max = 5;
+
+static void checkClockId(int clock) {
+ if (clock < 0 || clock >= clock_id_max) abort();
+}
+
#if defined(__linux__)
-nsecs_t systemTime(int clock)
-{
- static const clockid_t clocks[] = {
- CLOCK_REALTIME,
- CLOCK_MONOTONIC,
- CLOCK_PROCESS_CPUTIME_ID,
- CLOCK_THREAD_CPUTIME_ID,
- CLOCK_BOOTTIME
- };
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
+nsecs_t systemTime(int clock) {
+ checkClockId(clock);
+ static constexpr clockid_t clocks[] = {CLOCK_REALTIME, CLOCK_MONOTONIC,
+ CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID,
+ CLOCK_BOOTTIME};
+ static_assert(clock_id_max == arraysize(clocks));
+ timespec t = {};
clock_gettime(clocks[clock], &t);
return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
}
#else
-nsecs_t systemTime(int /*clock*/)
-{
+nsecs_t systemTime(int clock) {
+ // TODO: is this ever called with anything but REALTIME on mac/windows?
+ checkClockId(clock);
+
// Clock support varies widely across hosts. Mac OS doesn't support
- // CLOCK_BOOTTIME, and Windows is windows.
- struct timeval t;
- t.tv_sec = t.tv_usec = 0;
+ // CLOCK_BOOTTIME (and doesn't even have clock_gettime until 10.12).
+ // Windows is windows.
+ timeval t = {};
gettimeofday(&t, nullptr);
return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
}
diff --git a/libutils/Timers_test.cpp b/libutils/Timers_test.cpp
new file mode 100644
index 0000000..ec0051e
--- /dev/null
+++ b/libutils/Timers_test.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 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 <utils/Timers.h>
+
+#include <gtest/gtest.h>
+
+TEST(Timers, systemTime_invalid) {
+ EXPECT_EXIT(systemTime(-1), testing::KilledBySignal(SIGABRT), "");
+ systemTime(SYSTEM_TIME_REALTIME);
+ systemTime(SYSTEM_TIME_MONOTONIC);
+ systemTime(SYSTEM_TIME_PROCESS);
+ systemTime(SYSTEM_TIME_THREAD);
+ systemTime(SYSTEM_TIME_BOOTTIME);
+ EXPECT_EXIT(systemTime(SYSTEM_TIME_BOOTTIME + 1), testing::KilledBySignal(SIGABRT), "");
+}
diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp
new file mode 100644
index 0000000..f6df051
--- /dev/null
+++ b/libutils/Vector_fuzz.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 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 "fuzzer/FuzzedDataProvider.h"
+#include "utils/Vector.h"
+static constexpr uint16_t MAX_VEC_SIZE = 5000;
+
+void runVectorFuzz(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ android::Vector<uint8_t> vec = android::Vector<uint8_t>();
+ // We want to test handling of sizeof as well.
+ android::Vector<uint32_t> vec32 = android::Vector<uint32_t>();
+
+ // We're going to generate two vectors of this size
+ size_t vectorSize = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+ vec.setCapacity(vectorSize);
+ vec32.setCapacity(vectorSize);
+ for (size_t i = 0; i < vectorSize; i++) {
+ uint8_t count = dataProvider.ConsumeIntegralInRange<uint8_t>(1, 5);
+ vec.insertAt((uint8_t)i, i, count);
+ vec32.insertAt((uint32_t)i, i, count);
+ vec.push_front(i);
+ vec32.push(i);
+ }
+
+ // Now we'll perform some test operations with any remaining data
+ // Index to perform operations at
+ size_t index = dataProvider.ConsumeIntegralInRange<size_t>(0, vec.size());
+ std::vector<uint8_t> remainingVec = dataProvider.ConsumeRemainingBytes<uint8_t>();
+ // Insert an array and vector
+ vec.insertArrayAt(remainingVec.data(), index, remainingVec.size());
+ android::Vector<uint8_t> vecCopy = android::Vector<uint8_t>(vec);
+ vec.insertVectorAt(vecCopy, index);
+ // Same thing for 32 bit vector
+ android::Vector<uint32_t> vec32Copy = android::Vector<uint32_t>(vec32);
+ vec32.insertArrayAt(vec32Copy.array(), index, vec32.size());
+ vec32.insertVectorAt(vec32Copy, index);
+ // Replace single character
+ if (remainingVec.size() > 0) {
+ vec.replaceAt(remainingVec[0], index);
+ vec32.replaceAt(static_cast<uint32_t>(remainingVec[0]), index);
+ } else {
+ vec.replaceAt(0, index);
+ vec32.replaceAt(0, index);
+ }
+ // Add any remaining bytes
+ for (uint8_t i : remainingVec) {
+ vec.add(i);
+ vec32.add(static_cast<uint32_t>(i));
+ }
+ // Shrink capactiy
+ vec.setCapacity(remainingVec.size());
+ vec32.setCapacity(remainingVec.size());
+ // Iterate through each pointer
+ size_t sum = 0;
+ for (auto& it : vec) {
+ sum += it;
+ }
+ for (auto& it : vec32) {
+ sum += it;
+ }
+ // Cleanup
+ vec.clear();
+ vecCopy.clear();
+ vec32.clear();
+ vec32Copy.clear();
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ runVectorFuzz(data, size);
+ return 0;
+}
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index a8d7851..3c30a2a 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -78,7 +78,9 @@
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
// if the priority set failed, else another value if just the group set failed;
// in either case errno is set. Thread ID zero means current thread.
-extern int androidSetThreadPriority(pid_t tid, int prio);
+// Parameter "change_policy" indicates if sched policy should be changed. It needs
+// not be checked again if the change is done elsewhere like activity manager.
+extern int androidSetThreadPriority(pid_t tid, int prio, bool change_policy = true);
// Get the current priority of a particular thread. Returns one of the
// ANDROID_PRIORITY constants or a negative result in case of error.
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index dee577e..6002567 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -19,12 +19,20 @@
#include <unistd.h>
+#if !defined(__MINGW32__)
+#include <sys/mman.h>
+#endif
+
#if defined(__APPLE__)
/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-
+static_assert(sizeof(off_t) >= 8, "This code requires that Mac OS have at least a 64-bit off_t.");
typedef off_t off64_t;
+static inline void* mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) {
+ return mmap(addr, length, prot, flags, fd, offset);
+}
+
static inline off64_t lseek64(int fd, off64_t offset, int whence) {
return lseek(fd, offset, whence);
}
diff --git a/libutils/include/utils/Timers.h b/libutils/include/utils/Timers.h
index 54ec474..197fc26 100644
--- a/libutils/include/utils/Timers.h
+++ b/libutils/include/utils/Timers.h
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-//
-// Timer functions.
-//
-#ifndef _LIBS_UTILS_TIMERS_H
-#define _LIBS_UTILS_TIMERS_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -77,11 +73,11 @@
static CONSTEXPR inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
enum {
- SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
- SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
- SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
- SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
- SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
+ SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
+ SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
+ SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
+ SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
+ SYSTEM_TIME_BOOTTIME = 4, // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
};
// return the system-time according to the specified clock
@@ -104,5 +100,3 @@
#ifdef __cplusplus
} // extern "C"
#endif
-
-#endif // _LIBS_UTILS_TIMERS_H
diff --git a/libziparchive/.clang-format b/libziparchive/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/libziparchive/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
deleted file mode 100644
index 786e7b3..0000000
--- a/libziparchive/Android.bp
+++ /dev/null
@@ -1,231 +0,0 @@
-//
-// 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.
-
-cc_defaults {
- name: "libziparchive_flags",
- cflags: [
- // ZLIB_CONST turns on const for input buffers, which is pretty standard.
- "-DZLIB_CONST",
- "-Werror",
- "-Wall",
- "-D_FILE_OFFSET_BITS=64",
- ],
- cppflags: [
- // Incorrectly warns when C++11 empty brace {} initializer is used.
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
- "-Wno-missing-field-initializers",
- "-Wconversion",
- "-Wno-sign-conversion",
- ],
-
- // Enable -Wold-style-cast only for non-Windows targets. _islower_l,
- // _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",
- ],
- },
- },
- sanitize: {
- misc_undefined: [
- "signed-integer-overflow",
- "unsigned-integer-overflow",
- "shift",
- "integer-divide-by-zero",
- "implicit-signed-integer-truncation",
- // TODO: Fix crash when we enable this option
- // "implicit-unsigned-integer-truncation",
- // TODO: not tested yet.
- // "implicit-integer-sign-change",
- ],
- },
-}
-
-cc_defaults {
- name: "libziparchive_defaults",
- srcs: [
- "zip_archive.cc",
- "zip_archive_stream_entry.cc",
- "zip_cd_entry_map.cc",
- "zip_error.cpp",
- "zip_writer.cc",
- ],
-
- target: {
- windows: {
- cflags: ["-mno-ms-bitfields"],
-
- enabled: true,
- },
- },
-
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- // for FRIEND_TEST
- static_libs: ["libgtest_prod"],
- export_static_lib_headers: ["libgtest_prod"],
-
- export_include_dirs: ["include"],
-}
-
-cc_library {
- name: "libziparchive",
- host_supported: true,
- vendor_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- vndk: {
- enabled: true,
- },
- double_loadable: true,
- export_shared_lib_headers: ["libbase"],
-
- defaults: [
- "libziparchive_defaults",
- "libziparchive_flags",
- ],
- shared_libs: [
- "liblog",
- "libbase",
- "libz",
- ],
- target: {
- linux_bionic: {
- enabled: true,
- },
- },
-}
-
-// Tests.
-cc_test {
- name: "ziparchive-tests",
- host_supported: true,
- defaults: ["libziparchive_flags"],
-
- data: [
- "testdata/**/*",
- ],
-
- srcs: [
- "entry_name_utils_test.cc",
- "zip_archive_test.cc",
- "zip_writer_test.cc",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- static_libs: [
- "libziparchive",
- "libz",
- "libutils",
- ],
-
- target: {
- host: {
- cppflags: ["-Wno-unnamed-type-template-args"],
- },
- windows: {
- enabled: true,
- },
- },
- test_suites: ["device-tests"],
-}
-
-// Performance benchmarks.
-cc_benchmark {
- name: "ziparchive-benchmarks",
- defaults: ["libziparchive_flags"],
-
- srcs: [
- "zip_archive_benchmark.cpp",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- static_libs: [
- "libziparchive",
- "libz",
- "libutils",
- ],
-
- target: {
- host: {
- cppflags: ["-Wno-unnamed-type-template-args"],
- },
- },
-}
-
-cc_binary {
- name: "ziptool",
- defaults: ["libziparchive_flags"],
- srcs: ["ziptool.cpp"],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- recovery_available: true,
- host_supported: true,
- target: {
- android: {
- symlinks: ["unzip", "zipinfo"],
- },
- },
-}
-
-cc_fuzz {
- name: "libziparchive_fuzzer",
- srcs: ["libziparchive_fuzzer.cpp"],
- static_libs: ["libziparchive", "libbase", "libz", "liblog"],
- host_supported: true,
- corpus: ["testdata/*"],
-}
-
-sh_test {
- name: "ziptool-tests",
- src: "run-ziptool-tests-on-android.sh",
- filename: "run-ziptool-tests-on-android.sh",
- test_suites: ["general-tests"],
- host_supported: true,
- device_supported: false,
- test_config: "ziptool-tests.xml",
- data: ["cli-tests/**/*"],
- target_required: ["cli-test", "ziptool"],
-}
-
-python_test_host {
- name: "ziparchive_tests_large",
- srcs: ["test_ziparchive_large.py"],
- main: "test_ziparchive_large.py",
- version: {
- py2: {
- enabled: true,
- embedded_launcher: false,
- },
- py3: {
- enabled: false,
- embedded_launcher: false,
- },
- },
- test_suites: ["general-tests"],
-}
diff --git a/libziparchive/OWNERS b/libziparchive/OWNERS
deleted file mode 100644
index fcc567a..0000000
--- a/libziparchive/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-narayan@google.com
diff --git a/libziparchive/cli-tests/files/example.zip b/libziparchive/cli-tests/files/example.zip
deleted file mode 100644
index c3292e9..0000000
--- a/libziparchive/cli-tests/files/example.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/cli-tests/unzip.test b/libziparchive/cli-tests/unzip.test
deleted file mode 100755
index 6e5cbf2..0000000
--- a/libziparchive/cli-tests/unzip.test
+++ /dev/null
@@ -1,148 +0,0 @@
-# unzip tests.
-
-# Note: since "master key", Android uses libziparchive for all zip file
-# handling, and that scans the whole central directory immediately. Not only
-# lookups by name but also iteration is implemented using the resulting hash
-# table, meaning that any test that makes assumptions about iteration order
-# will fail on Android.
-
-name: unzip -l
-command: unzip -l $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/x.txt ]
-expected-stdout:
- Archive: $FILES/example.zip
- Length Date Time Name
- --------- ---------- ----- ----
- 1024 2017-06-04 08:45 d1/d2/x.txt
- --------- -------
- 1024 1 file
----
-
-name: unzip -lq
-command: unzip -lq $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/x.txt ]
-expected-stdout:
- Length Date Time Name
- --------- ---------- ----- ----
- 1024 2017-06-04 08:45 d1/d2/x.txt
- --------- -------
- 1024 1 file
----
-
-name: unzip -lv
-command: unzip -lv $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/file ]
-expected-stdout:
- Archive: $FILES/example.zip
- Length Method Size Cmpr Date Time CRC-32 Name
- -------- ------ ------- ---- ---------- ----- -------- ----
- 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
- -------- ------- --- -------
- 1024 11 99% 1 file
----
-
-name: unzip -v
-command: unzip -v $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/file ]
-expected-stdout:
- Archive: $FILES/example.zip
- Length Method Size Cmpr Date Time CRC-32 Name
- -------- ------ ------- ---- ---------- ----- -------- ----
- 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
- -------- ------- --- -------
- 1024 11 99% 1 file
----
-
-name: unzip one file
-command: unzip -q $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-after: [ ! -f d1/d2/b.txt ]
-expected-stdout:
- a
----
-
-name: unzip all files
-command: unzip -q $FILES/example.zip
-after: [ -f d1/d2/a.txt ]
-after: [ -f d1/d2/b.txt ]
-after: [ -f d1/d2/c.txt ]
-after: [ -f d1/d2/empty.txt ]
-after: [ -f d1/d2/x.txt ]
-after: [ -d d1/d2/dir ]
-expected-stdout:
----
-
-name: unzip -o
-before: mkdir -p d1/d2
-before: echo b > d1/d2/a.txt
-command: unzip -q -o $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-expected-stdout:
- a
----
-
-name: unzip -n
-before: mkdir -p d1/d2
-before: echo b > d1/d2/a.txt
-command: unzip -q -n $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-expected-stdout:
- b
----
-
-# The reference implementation will create *one* level of missing directories,
-# so this succeeds.
-name: unzip -d shallow non-existent
-command: unzip -q -d will-be-created $FILES/example.zip d1/d2/a.txt
-after: [ -d will-be-created ]
-after: [ -f will-be-created/d1/d2/a.txt ]
----
-
-# The reference implementation will *only* create one level of missing
-# directories, so this fails.
-name: unzip -d deep non-existent
-command: unzip -q -d oh-no/will-not-be-created $FILES/example.zip d1/d2/a.txt 2> stderr ; echo $? > status
-after: [ ! -d oh-no ]
-after: [ ! -d oh-no/will-not-be-created ]
-after: [ ! -f oh-no/will-not-be-created/d1/d2/a.txt ]
-after: grep -q "oh-no/will-not-be-created" stderr
-after: grep -q "No such file or directory" stderr
-# The reference implementation has *lots* of non-zero exit values, but we stick to 0 and 1.
-after: [ $(cat status) -gt 0 ]
----
-
-name: unzip -d exists
-before: mkdir dir
-command: unzip -q -d dir $FILES/example.zip d1/d2/a.txt && cat dir/d1/d2/a.txt
-after: [ ! -f d1/d2/a.txt ]
-expected-stdout:
- a
----
-
-name: unzip -p
-command: unzip -p $FILES/example.zip d1/d2/a.txt
-after: [ ! -f d1/d2/a.txt ]
-expected-stdout:
- a
----
-
-name: unzip -x FILE...
-# Note: the RI ignores -x DIR for some reason, but it's not obvious we should.
-command: unzip -q $FILES/example.zip -x d1/d2/a.txt d1/d2/b.txt d1/d2/empty.txt d1/d2/x.txt && cat d1/d2/c.txt
-after: [ ! -f d1/d2/a.txt ]
-after: [ ! -f d1/d2/b.txt ]
-after: [ ! -f d1/d2/empty.txt ]
-after: [ ! -f d1/d2/x.txt ]
-after: [ -d d1/d2/dir ]
-expected-stdout:
- ccc
----
-
-name: unzip FILE -x FILE...
-command: unzip -q $FILES/example.zip d1/d2/a.txt d1/d2/b.txt -x d1/d2/a.txt && cat d1/d2/b.txt
-after: [ ! -f d1/d2/a.txt ]
-after: [ -f d1/d2/b.txt ]
-after: [ ! -f d1/d2/c.txt ]
-after: [ ! -f d1/d2/empty.txt ]
-after: [ ! -f d1/d2/x.txt ]
-after: [ ! -d d1/d2/dir ]
-expected-stdout:
- bb
----
diff --git a/libziparchive/cli-tests/zipinfo.test b/libziparchive/cli-tests/zipinfo.test
deleted file mode 100755
index d5bce1c..0000000
--- a/libziparchive/cli-tests/zipinfo.test
+++ /dev/null
@@ -1,53 +0,0 @@
-# zipinfo tests.
-
-# Note: since "master key", Android uses libziparchive for all zip file
-# handling, and that scans the whole central directory immediately. Not only
-# lookups by name but also iteration is implemented using the resulting hash
-# table, meaning that any test that makes assumptions about iteration order
-# will fail on Android.
-
-name: zipinfo -1
-command: zipinfo -1 $FILES/example.zip | sort
-expected-stdout:
- d1/
- d1/d2/a.txt
- d1/d2/b.txt
- d1/d2/c.txt
- d1/d2/dir/
- d1/d2/empty.txt
- d1/d2/x.txt
----
-
-name: zipinfo header
-command: zipinfo $FILES/example.zip | head -2
-expected-stdout:
- Archive: $FILES/example.zip
- Zip file size: 1082 bytes, number of entries: 7
----
-
-name: zipinfo footer
-command: zipinfo $FILES/example.zip | tail -1
-expected-stdout:
- 7 files, 1033 bytes uncompressed, 20 bytes compressed: 98.1%
----
-
-name: zipinfo directory
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/ | sed s/17-Jun-/2017-06-/
-expected-stdout:
- drwxr-x--- 3.0 unx 0 bx stor 2017-06-04 08:40 d1/
----
-
-name: zipinfo stored
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/d2/empty.txt | sed s/17-Jun-/2017-06-/
-expected-stdout:
- -rw-r----- 3.0 unx 0 bx stor 2017-06-04 08:43 d1/d2/empty.txt
----
-
-name: zipinfo deflated
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/d2/x.txt | sed s/17-Jun-/2017-06-/
-expected-stdout:
- -rw-r----- 3.0 unx 1024 tx defN 2017-06-04 08:45 d1/d2/x.txt
----
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
deleted file mode 100644
index 10311b5..0000000
--- a/libziparchive/entry_name_utils-inl.h
+++ /dev/null
@@ -1,65 +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.
- */
-
-#ifndef LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
-#define LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <limits>
-
-// Check if |length| bytes at |entry_name| constitute a valid entry name.
-// Entry names must be valid UTF-8 and must not contain '0'. They also must
-// fit into the central directory record.
-inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
- if (length > std::numeric_limits<uint16_t>::max()) {
- return false;
- }
- for (size_t i = 0; i < length; ++i) {
- const uint8_t byte = entry_name[i];
- if (byte == 0) {
- return false;
- } else if ((byte & 0x80) == 0) {
- // Single byte sequence.
- continue;
- } else if ((byte & 0xc0) == 0x80 || (byte & 0xfe) == 0xfe) {
- // Invalid sequence.
- return false;
- } else {
- // 2-5 byte sequences.
- for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
- first = static_cast<uint8_t>((first & 0x7f) << 1)) {
- ++i;
-
- // Missing continuation byte..
- if (i == length) {
- return false;
- }
-
- // Invalid continuation byte.
- const uint8_t continuation_byte = entry_name[i];
- if ((continuation_byte & 0xc0) != 0x80) {
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
-#endif // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
deleted file mode 100644
index d83d854..0000000
--- a/libziparchive/entry_name_utils_test.cc
+++ /dev/null
@@ -1,62 +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 "entry_name_utils-inl.h"
-
-#include <gtest/gtest.h>
-
-TEST(entry_name_utils, NullChars) {
- // 'A', 'R', '\0', 'S', 'E'
- const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
- ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
-
- const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
- ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
-}
-
-TEST(entry_name_utils, InvalidSequence) {
- // 0xfe is an invalid start byte
- const uint8_t invalid[] = {0x41, 0xfe};
- ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
-
- // 0x91 is an invalid start byte (it's a valid continuation byte).
- const uint8_t invalid2[] = {0x41, 0x91};
- ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
-}
-
-TEST(entry_name_utils, TruncatedContinuation) {
- // Malayalam script with truncated bytes. There should be 2 bytes
- // after 0xe0
- const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
- ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
-
- // 0xc2 is the start of a 2 byte sequence that we've subsequently
- // dropped.
- const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
- ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
-}
-
-TEST(entry_name_utils, BadContinuation) {
- // 0x41 is an invalid continuation char, since it's MSBs
- // aren't "10..." (are 01).
- const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
- ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
-
- // 0x41 is an invalid continuation char, since it's MSBs
- // aren't "10..." (are 11).
- const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
- ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
-}
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
deleted file mode 100644
index 005d697..0000000
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-/*
- * Read-only access to Zip archives, with minimal heap allocation.
- */
-
-#include <stdint.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include <functional>
-#include <string>
-#include <string_view>
-
-#include "android-base/off64_t.h"
-
-/* Zip compression methods we support */
-enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
-};
-
-// This struct holds the common information of a zip entry other than the
-// the entry size. The compressed and uncompressed length will be handled
-// separately in the derived class.
-struct ZipEntryCommon {
- // Compression method. One of kCompressStored or kCompressDeflated.
- // See also `gpbf` for deflate subtypes.
- uint16_t method;
-
- // Modification time. The zipfile format specifies
- // that the first two little endian bytes contain the time
- // and the last two little endian bytes contain the date.
- // See `GetModificationTime`. Use signed integer to avoid the
- // sub-overflow.
- // TODO: should be overridden by extra time field, if present.
- int32_t mod_time;
-
- // Returns `mod_time` as a broken-down struct tm.
- struct tm GetModificationTime() const;
-
- // Suggested Unix mode for this entry, from the zip archive if created on
- // Unix, or a default otherwise. See also `external_file_attributes`.
- mode_t unix_mode;
-
- // 1 if this entry contains a data descriptor segment, 0
- // otherwise.
- uint8_t has_data_descriptor;
-
- // Crc32 value of this ZipEntry. This information might
- // either be stored in the local file header or in a special
- // Data descriptor footer at the end of the file entry.
- uint32_t crc32;
-
- // If the value of uncompressed length and compressed length are stored in
- // the zip64 extended info of the extra field.
- bool zip64_format_size{false};
-
- // The offset to the start of data for this ZipEntry.
- off64_t offset;
-
- // The version of zip and the host file system this came from (for zipinfo).
- uint16_t version_made_by;
-
- // The raw attributes, whose interpretation depends on the host
- // file system in `version_made_by` (for zipinfo). See also `unix_mode`.
- uint32_t external_file_attributes;
-
- // Specifics about the deflation (for zipinfo).
- uint16_t gpbf;
- // Whether this entry is believed to be text or binary (for zipinfo).
- bool is_text;
-};
-
-struct ZipEntry64;
-// Many users of the library assume the entry size is capped at UNIT32_MAX. So we keep
-// the interface for the old ZipEntry here; and we could switch them over to the new
-// ZipEntry64 later.
-struct ZipEntry : public ZipEntryCommon {
- // Compressed length of this ZipEntry. The maximum value is UNIT32_MAX.
- // Might be present either in the local file header or in the data
- // descriptor footer.
- uint32_t compressed_length{0};
-
- // Uncompressed length of this ZipEntry. The maximum value is UNIT32_MAX.
- // Might be present either in the local file header or in the data
- // descriptor footer.
- uint32_t uncompressed_length{0};
-
- // Copies the contents of a ZipEntry64 object to a 32 bits ZipEntry. Returns 0 if the
- // size of the entry fits into uint32_t, returns a negative error code
- // (kUnsupportedEntrySize) otherwise.
- static int32_t CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src);
-
- private:
- ZipEntry& operator=(const ZipEntryCommon& other) {
- ZipEntryCommon::operator=(other);
- return *this;
- }
-};
-
-// Represents information about a zip entry in a zip file.
-struct ZipEntry64 : public ZipEntryCommon {
- // Compressed length of this ZipEntry. The maximum value is UNIT64_MAX.
- // Might be present either in the local file header, the zip64 extended field,
- // or in the data descriptor footer.
- uint64_t compressed_length{0};
-
- // Uncompressed length of this ZipEntry. The maximum value is UNIT64_MAX.
- // Might be present either in the local file header, the zip64 extended field,
- // or in the data descriptor footer.
- uint64_t uncompressed_length{0};
-
- explicit ZipEntry64() = default;
- explicit ZipEntry64(const ZipEntry& zip_entry) : ZipEntryCommon(zip_entry) {
- compressed_length = zip_entry.compressed_length;
- uncompressed_length = zip_entry.uncompressed_length;
- }
-};
-
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
-/*
- * Open a Zip archive, and sets handle to the value of the opaque
- * handle for the file. This handle must be released by calling
- * CloseArchive with this handle.
- *
- * Returns 0 on success, and negative values on failure.
- */
-int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
-
-/*
- * Like OpenArchive, but takes a file descriptor open for reading
- * at the start of the file. The descriptor must be mappable (this does
- * not allow access to a stream).
- *
- * Sets handle to the value of the opaque handle for this file descriptor.
- * This handle must be released by calling CloseArchive with this handle.
- *
- * If assume_ownership parameter is 'true' calling CloseArchive will close
- * the file.
- *
- * This function maps and scans the central directory and builds a table
- * of entries for future lookups.
- *
- * "debugFileName" will appear in error messages, but is not otherwise used.
- *
- * Returns 0 on success, and negative values on failure.
- */
-int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
- bool assume_ownership = true);
-
-int32_t OpenArchiveFdRange(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
- off64_t length, off64_t offset, bool assume_ownership = true);
-
-int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
- ZipArchiveHandle* handle);
-/*
- * Close archive, releasing resources associated with it. This will
- * unmap the central directory of the zipfile and free all internal
- * data structures associated with the file. It is an error to use
- * this handle for any further operations without an intervening
- * call to one of the OpenArchive variants.
- */
-void CloseArchive(ZipArchiveHandle archive);
-
-/** See GetArchiveInfo(). */
-struct ZipArchiveInfo {
- /** The size in bytes of the archive itself. Used by zipinfo. */
- off64_t archive_size;
- /** The number of entries in the archive. */
- uint64_t entry_count;
-};
-
-/**
- * Returns information about the given archive.
- */
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
-
-/*
- * Find an entry in the Zip archive, by name. |data| must be non-null.
- *
- * Returns 0 if an entry is found, and populates |data| with information
- * about this entry. Returns negative values otherwise.
- *
- * It's important to note that |data->crc32|, |data->compLen| and
- * |data->uncompLen| might be set to values from the central directory
- * if this file entry contains a data descriptor footer. To verify crc32s
- * and length, a call to VerifyCrcAndLengths must be made after entry data
- * has been processed.
- *
- * On non-Windows platforms this method does not modify internal state and
- * can be called concurrently.
- */
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
- ZipEntry64* data);
-
-/*
- * Start iterating over all entries of a zip file. The order of iteration
- * is not guaranteed to be the same as the order of elements
- * in the central directory but is stable for a given zip file. |cookie| will
- * contain the value of an opaque cookie which can be used to make one or more
- * calls to Next. All calls to StartIteration must be matched by a call to
- * EndIteration to free any allocated memory.
- *
- * This method also accepts optional prefix and suffix to restrict iteration to
- * entry names that start with |optional_prefix| or end with |optional_suffix|.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const std::string_view optional_prefix = "",
- const std::string_view optional_suffix = "");
-
-/*
- * Start iterating over all entries of a zip file. Use the matcher functor to
- * restrict iteration to entry names that make the functor return true.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- std::function<bool(std::string_view entry_name)> matcher);
-
-/*
- * Advance to the next element in the zipfile in iteration order.
- *
- * Returns 0 on success, -1 if there are no more elements in this
- * archive and lower negative values on failure.
- */
-int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name);
-int32_t Next(void* cookie, ZipEntry64* data, std::string* name);
-
-/*
- * End iteration over all entries of a zip file and frees the memory allocated
- * in StartIteration.
- */
-void EndIteration(void* cookie);
-
-/*
- * Uncompress and write an entry to an open file identified by |fd|.
- * |entry->uncompressed_length| bytes will be written to the file at
- * its current offset, and the file will be truncated at the end of
- * the uncompressed data (no truncation if |fd| references a block
- * device).
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd);
-
-/**
- * Uncompress a given zip entry to the memory region at |begin| and of
- * size |size|. This size is expected to be the same as the *declared*
- * uncompressed length of the zip entry. It is an error if the *actual*
- * number of uncompressed bytes differs from this number.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin,
- size_t size);
-
-int GetFileDescriptor(const ZipArchiveHandle archive);
-
-/**
- * Returns the offset of the zip archive in the backing file descriptor, or 0 if the zip archive is
- * not backed by a file descriptor.
- */
-off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive);
-
-const char* ErrorCodeString(int32_t error_code);
-
-// Many users of libziparchive assume the entry size to be 32 bits long. So we keep these
-// interfaces that use 32 bit ZipEntry to make old code work. TODO(xunchang) Remove the 32 bit
-// wrapper functions once we switch all users to recognize ZipEntry64.
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
-int32_t Next(void* cookie, ZipEntry* data, std::string* name);
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd);
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin,
- size_t size);
-
-#if !defined(_WIN32)
-typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie);
-
-/*
- * Stream the uncompressed data through the supplied function,
- * passing cookie to it each time it gets called.
- */
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry,
- ProcessZipEntryFunction func, void* cookie);
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry,
- ProcessZipEntryFunction func, void* cookie);
-#endif
-
-namespace zip_archive {
-
-class Writer {
- public:
- virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
- virtual ~Writer();
-
- protected:
- Writer() = default;
-
- private:
- Writer(const Writer&) = delete;
- void operator=(const Writer&) = delete;
-};
-
-class Reader {
- public:
- virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const = 0;
- virtual ~Reader();
-
- protected:
- Reader() = default;
-
- private:
- Reader(const Reader&) = delete;
- void operator=(const Reader&) = delete;
-};
-
-/*
- * Inflates the first |compressed_length| bytes of |reader| to a given |writer|.
- * |crc_out| is set to the CRC32 checksum of the uncompressed data.
- *
- * Returns 0 on success and negative values on failure, for example if |reader|
- * cannot supply the right amount of data, or if the number of bytes written to
- * data does not match |uncompressed_length|.
- *
- * If |crc_out| is not nullptr, it is set to the crc32 checksum of the
- * uncompressed data.
- */
-int32_t Inflate(const Reader& reader, const uint64_t compressed_length,
- const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out);
-} // namespace zip_archive
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
deleted file mode 100644
index 8c6ca79..0000000
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Read-only stream access to Zip archives entries.
-#pragma once
-
-#include <ziparchive/zip_archive.h>
-
-#include <vector>
-
-#include "android-base/off64_t.h"
-
-class ZipArchiveStreamEntry {
- public:
- virtual ~ZipArchiveStreamEntry() {}
-
- virtual const std::vector<uint8_t>* Read() = 0;
-
- virtual bool Verify() = 0;
-
- static ZipArchiveStreamEntry* Create(ZipArchiveHandle handle, const ZipEntry& entry);
- static ZipArchiveStreamEntry* CreateRaw(ZipArchiveHandle handle, const ZipEntry& entry);
-
- protected:
- ZipArchiveStreamEntry(ZipArchiveHandle handle) : handle_(handle) {}
-
- virtual bool Init(const ZipEntry& entry);
-
- ZipArchiveHandle handle_;
-
- off64_t offset_ = 0;
- uint32_t crc32_ = 0u;
-};
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
deleted file mode 100644
index d68683d..0000000
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdio>
-#include <ctime>
-
-#include <gtest/gtest_prod.h>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-
-struct z_stream_s;
-typedef struct z_stream_s z_stream;
-
-/**
- * Writes a Zip file via a stateful interface.
- *
- * Example:
- *
- * FILE* file = fopen("path/to/zip.zip", "wb");
- *
- * ZipWriter writer(file);
- *
- * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
- * writer.WriteBytes(buffer, bufferLen);
- * writer.WriteBytes(buffer2, bufferLen2);
- * writer.FinishEntry();
- *
- * writer.StartEntry("empty.txt", 0);
- * writer.FinishEntry();
- *
- * writer.Finish();
- *
- * fclose(file);
- */
-class ZipWriter {
- public:
- enum {
- /**
- * Flag to compress the zip entry using deflate.
- */
- kCompress = 0x01,
-
- /**
- * Flag to align the zip entry data on a 32bit boundary. Useful for
- * mmapping the data at runtime.
- */
- kAlign32 = 0x02,
- };
-
- /**
- * A struct representing a zip file entry.
- */
- struct FileEntry {
- std::string path;
- uint16_t compression_method;
- uint32_t crc32;
- uint32_t compressed_size;
- uint32_t uncompressed_size;
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- uint16_t padding_length;
- off64_t local_file_header_offset;
- };
-
- static const char* ErrorCodeString(int32_t error_code);
-
- /**
- * Create a ZipWriter that will write into a FILE stream. The file should be opened with
- * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
- * caller is responsible for closing the file.
- */
- explicit ZipWriter(FILE* f);
-
- // Move constructor.
- ZipWriter(ZipWriter&& zipWriter) noexcept;
-
- // Move assignment.
- ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
-
- /**
- * Starts a new zip entry with the given path and flags.
- * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
- * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t StartEntry(std::string_view path, size_t flags);
-
- /**
- * Starts a new zip entry with the given path and flags, where the
- * entry will be aligned to the given alignment.
- * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
- * will result in an error.
- * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment);
-
- /**
- * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
- */
- int32_t StartEntryWithTime(std::string_view path, size_t flags, time_t time);
-
- /**
- * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
- */
- int32_t StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time, uint32_t alignment);
-
- /**
- * Writes bytes to the zip file for the previously started zip entry.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t WriteBytes(const void* data, size_t len);
-
- /**
- * Finish a zip entry started with StartEntry(const char*, size_t) or
- * StartEntryWithTime(const char*, size_t, time_t). This must be called before
- * any new zip entries are started, or before Finish() is called.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t FinishEntry();
-
- /**
- * Discards the last-written entry. Can only be called after an entry has been written using
- * FinishEntry().
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t DiscardLastEntry();
-
- /**
- * Sets `out_entry` to the last entry written after a call to FinishEntry().
- * Returns 0 on success, and an error value < 0 if no entries have been written.
- */
- int32_t GetLastEntry(FileEntry* out_entry);
-
- /**
- * Writes the Central Directory Headers and flushes the zip file stream.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t Finish();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ZipWriter);
-
- int32_t HandleError(int32_t error_code);
- int32_t PrepareDeflate();
- int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
- int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
- int32_t FlushCompressedBytes(FileEntry* file);
- bool ShouldUseDataDescriptor() const;
-
- enum class State {
- kWritingZip,
- kWritingEntry,
- kDone,
- kError,
- };
-
- FILE* file_;
- bool seekable_;
- off64_t current_offset_;
- State state_;
- std::vector<FileEntry> files_;
- FileEntry current_file_entry_;
-
- std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
- std::vector<uint8_t> buffer_;
-
- FRIEND_TEST(zipwriter, WriteToUnseekableFile);
-};
diff --git a/libziparchive/libziparchive_fuzzer.cpp b/libziparchive/libziparchive_fuzzer.cpp
deleted file mode 100644
index 75e7939..0000000
--- a/libziparchive/libziparchive_fuzzer.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <ziparchive/zip_archive.h>
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- ZipArchiveHandle handle = nullptr;
- OpenArchiveFromMemory(data, size, "fuzz", &handle);
- CloseArchive(handle);
- return 0;
-}
diff --git a/libziparchive/run-ziptool-tests-on-android.sh b/libziparchive/run-ziptool-tests-on-android.sh
deleted file mode 100755
index 3c23d43..0000000
--- a/libziparchive/run-ziptool-tests-on-android.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-# Copy the tests across.
-adb shell rm -rf /data/local/tmp/ziptool-tests/
-adb shell mkdir /data/local/tmp/ziptool-tests/
-adb push cli-tests/ /data/local/tmp/ziptool-tests/
-#adb push cli-test /data/local/tmp/ziptool-tests/
-
-if tty -s; then
- dash_t="-t"
-else
- dash_t=""
-fi
-
-exec adb shell $dash_t cli-test /data/local/tmp/ziptool-tests/cli-tests/*.test
diff --git a/libziparchive/test_ziparchive_large.py b/libziparchive/test_ziparchive_large.py
deleted file mode 100644
index 46d02aa..0000000
--- a/libziparchive/test_ziparchive_large.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2020 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.
-#
-
-"""Unittests for parsing files in zip64 format"""
-
-import os
-import subprocess
-import tempfile
-import unittest
-import zipfile
-import time
-
-class Zip64Test(unittest.TestCase):
- @staticmethod
- def _WriteFile(path, size_in_kib):
- contents = os.path.basename(path)[0] * 1024
- with open(path, 'w') as f:
- for it in range(0, size_in_kib):
- f.write(contents)
-
- @staticmethod
- def _AddEntriesToZip(output_zip, entries_dict=None):
- for name, size in entries_dict.items():
- file_path = tempfile.NamedTemporaryFile()
- Zip64Test._WriteFile(file_path.name, size)
- output_zip.write(file_path.name, arcname = name)
-
- def _getEntryNames(self, zip_name):
- cmd = ['ziptool', 'zipinfo', '-1', zip_name]
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- output, _ = proc.communicate()
- self.assertEquals(0, proc.returncode)
- self.assertNotEqual(None, output)
- return output.split()
-
- def _ExtractEntries(self, zip_name):
- temp_dir = tempfile.mkdtemp()
- cmd = ['ziptool', 'unzip', '-d', temp_dir, zip_name]
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- proc.communicate()
- self.assertEquals(0, proc.returncode)
-
- def test_entriesSmallerThan2G(self):
- zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
- # Add a few entries with each of them smaller than 2GiB. But the entire zip file is larger
- # than 4GiB in size.
- with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip:
- entry_dict = {'a.txt': 1025 * 1024, 'b.txt': 1025 * 1024, 'c.txt': 1025 * 1024,
- 'd.txt': 1025 * 1024, 'e.txt': 1024}
- self._AddEntriesToZip(output_zip, entry_dict)
-
- read_names = self._getEntryNames(zip_path.name)
- self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
- self._ExtractEntries(zip_path.name)
-
-
- def test_largeNumberOfEntries(self):
- zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
- entry_dict = {}
- # Add 100k entries (more than 65535|UINT16_MAX).
- for num in range(0, 100 * 1024):
- entry_dict[str(num)] = 50
-
- with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip:
- self._AddEntriesToZip(output_zip, entry_dict)
-
- read_names = self._getEntryNames(zip_path.name)
- self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
- self._ExtractEntries(zip_path.name)
-
-
- def test_largeCompressedEntriesSmallerThan4G(self):
- zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
- with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED,
- allowZip64=True) as output_zip:
- # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
- # sizes in the extra field. Test if our ziptool should be able to parse it.
- entry_dict = {'e.txt': 4095 * 1024, 'f.txt': 4095 * 1024}
- self._AddEntriesToZip(output_zip, entry_dict)
-
- read_names = self._getEntryNames(zip_path.name)
- self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
- self._ExtractEntries(zip_path.name)
-
-
- def test_forceDataDescriptor(self):
- file_path = tempfile.NamedTemporaryFile(suffix='.txt')
- self._WriteFile(file_path.name, 5000 * 1024)
-
- zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
- with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip:
- pass
- # The fd option force writes a data descriptor
- cmd = ['zip', '-fd', zip_path.name, file_path.name]
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- proc.communicate()
- read_names = self._getEntryNames(zip_path.name)
- self.assertEquals([file_path.name[1:]], read_names)
- self._ExtractEntries(zip_path.name)
-
-
- def test_largeUncompressedEntriesLargerThan4G(self):
- zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
- with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_STORED,
- allowZip64=True) as output_zip:
- # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
- # sizes in the extra field. Test if our ziptool should be able to parse it.
- entry_dict = {'g.txt': 5000 * 1024, 'h.txt': 6000 * 1024}
- self._AddEntriesToZip(output_zip, entry_dict)
-
- read_names = self._getEntryNames(zip_path.name)
- self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
- self._ExtractEntries(zip_path.name)
-
-
- def test_largeCompressedEntriesLargerThan4G(self):
- zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
- with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED,
- allowZip64=True) as output_zip:
- # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
- # sizes in the extra field. Test if our ziptool should be able to parse it.
- entry_dict = {'i.txt': 4096 * 1024, 'j.txt': 7000 * 1024}
- self._AddEntriesToZip(output_zip, entry_dict)
-
- read_names = self._getEntryNames(zip_path.name)
- self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
- self._ExtractEntries(zip_path.name)
-
-
-if __name__ == '__main__':
- testsuite = unittest.TestLoader().discover(
- os.path.dirname(os.path.realpath(__file__)))
- unittest.TextTestRunner(verbosity=2).run(testsuite)
diff --git a/libziparchive/testdata/bad_crc.zip b/libziparchive/testdata/bad_crc.zip
deleted file mode 100644
index e12ba07..0000000
--- a/libziparchive/testdata/bad_crc.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/bad_filename.zip b/libziparchive/testdata/bad_filename.zip
deleted file mode 100644
index 294eaf5..0000000
--- a/libziparchive/testdata/bad_filename.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/crash.apk b/libziparchive/testdata/crash.apk
deleted file mode 100644
index d6dd52d..0000000
--- a/libziparchive/testdata/crash.apk
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/declaredlength.zip b/libziparchive/testdata/declaredlength.zip
deleted file mode 100644
index 773380c..0000000
--- a/libziparchive/testdata/declaredlength.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/dummy-update.zip b/libziparchive/testdata/dummy-update.zip
deleted file mode 100644
index 6976bf1..0000000
--- a/libziparchive/testdata/dummy-update.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/empty.zip b/libziparchive/testdata/empty.zip
deleted file mode 100644
index 15cb0ec..0000000
--- a/libziparchive/testdata/empty.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/large.zip b/libziparchive/testdata/large.zip
deleted file mode 100644
index 49659c8..0000000
--- a/libziparchive/testdata/large.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/valid.zip b/libziparchive/testdata/valid.zip
deleted file mode 100644
index 9e7cb78..0000000
--- a/libziparchive/testdata/valid.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/zero-size-cd.zip b/libziparchive/testdata/zero-size-cd.zip
deleted file mode 100644
index b6c8cbe..0000000
--- a/libziparchive/testdata/zero-size-cd.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/zip64.zip b/libziparchive/testdata/zip64.zip
deleted file mode 100644
index 3f25a4c..0000000
--- a/libziparchive/testdata/zip64.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
deleted file mode 100644
index 014f881..0000000
--- a/libziparchive/zip_archive.cc
+++ /dev/null
@@ -1,1586 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-/*
- * Read-only access to Zip archives, with minimal heap allocation.
- */
-
-#define LOG_TAG "ziparchive"
-
-#include "ziparchive/zip_archive.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <memory>
-#include <optional>
-#include <vector>
-
-#if defined(__APPLE__)
-#define lseek64 lseek
-#endif
-
-#if defined(__BIONIC__)
-#include <android/fdsan.h>
-#endif
-
-#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/strings.h>
-#include <android-base/utf8.h>
-#include <log/log.h>
-#include "zlib.h"
-
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
-#include "zip_archive_private.h"
-
-// Used to turn on crc checks - verify that the content CRC matches the values
-// specified in the local file header and the central directory.
-static constexpr bool kCrcChecksEnabled = false;
-
-// The maximum number of bytes to scan backwards for the EOCD start.
-static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
-
-// Set a reasonable cap (256 GiB) for the zip file size. So the data is always valid when
-// we parse the fields in cd or local headers as 64 bits signed integers.
-static constexpr uint64_t kMaxFileLength = 256 * static_cast<uint64_t>(1u << 30u);
-
-/*
- * A Read-only Zip archive.
- *
- * We want "open" and "find entry by name" to be fast operations, and
- * we want to use as little memory as possible. We memory-map the zip
- * central directory, and load a hash table with pointers to the filenames
- * (which aren't null-terminated). The other fields are at a fixed offset
- * from the filename, so we don't need to extract those (but we do need
- * to byte-read and endian-swap them every time we want them).
- *
- * It's possible that somebody has handed us a massive (~1GB) zip archive,
- * so we can't expect to mmap the entire file.
- *
- * To speed comparisons when doing a lookup by name, we could make the mapping
- * "private" (copy-on-write) and null-terminate the filenames after verifying
- * the record structure. However, this requires a private mapping of
- * every page that the Central Directory touches. Easier to tuck a copy
- * of the string length into the hash table entry.
- */
-
-#if defined(__BIONIC__)
-uint64_t GetOwnerTag(const ZipArchive* archive) {
- return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
- reinterpret_cast<uint64_t>(archive));
-}
-#endif
-
-ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
- : mapped_zip(map),
- close_file(assume_ownership),
- directory_offset(0),
- central_directory(),
- directory_map(),
- num_entries(0) {
-#if defined(__BIONIC__)
- if (assume_ownership) {
- CHECK(mapped_zip.HasFd());
- android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
- }
-#endif
-}
-
-ZipArchive::ZipArchive(const void* address, size_t length)
- : mapped_zip(address, length),
- close_file(false),
- directory_offset(0),
- central_directory(),
- directory_map(),
- num_entries(0) {}
-
-ZipArchive::~ZipArchive() {
- if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
-#if defined(__BIONIC__)
- android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
-#else
- close(mapped_zip.GetFileDescriptor());
-#endif
- }
-}
-
-struct CentralDirectoryInfo {
- uint64_t num_records;
- // The size of the central directory (in bytes).
- uint64_t cd_size;
- // The offset of the start of the central directory, relative
- // to the start of the file.
- uint64_t cd_start_offset;
-};
-
-// Reads |T| at |readPtr| and increments |readPtr|. Returns std::nullopt if the boundary check
-// fails.
-template <typename T>
-static std::optional<T> TryConsumeUnaligned(uint8_t** readPtr, const uint8_t* bufStart,
- size_t bufSize) {
- if (bufSize < sizeof(T) || *readPtr - bufStart > bufSize - sizeof(T)) {
- ALOGW("Zip: %zu byte read exceeds the boundary of allocated buf, offset %zu, bufSize %zu",
- sizeof(T), *readPtr - bufStart, bufSize);
- return std::nullopt;
- }
- return ConsumeUnaligned<T>(readPtr);
-}
-
-static ZipError FindCentralDirectoryInfoForZip64(const char* debugFileName, ZipArchive* archive,
- off64_t eocdOffset, CentralDirectoryInfo* cdInfo) {
- if (eocdOffset <= sizeof(Zip64EocdLocator)) {
- ALOGW("Zip: %s: Not enough space for zip64 eocd locator", debugFileName);
- return kInvalidFile;
- }
- // We expect to find the zip64 eocd locator immediately before the zip eocd.
- const int64_t locatorOffset = eocdOffset - sizeof(Zip64EocdLocator);
- Zip64EocdLocator zip64EocdLocator{};
- if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>((&zip64EocdLocator)),
- sizeof(Zip64EocdLocator), locatorOffset)) {
- ALOGW("Zip: %s: Read %zu from offset %" PRId64 " failed %s", debugFileName,
- sizeof(Zip64EocdLocator), locatorOffset, debugFileName);
- return kIoError;
- }
-
- if (zip64EocdLocator.locator_signature != Zip64EocdLocator::kSignature) {
- ALOGW("Zip: %s: Zip64 eocd locator signature not found at offset %" PRId64, debugFileName,
- locatorOffset);
- return kInvalidFile;
- }
-
- const int64_t zip64EocdOffset = zip64EocdLocator.zip64_eocd_offset;
- if (locatorOffset <= sizeof(Zip64EocdRecord) ||
- zip64EocdOffset > locatorOffset - sizeof(Zip64EocdRecord)) {
- ALOGW("Zip: %s: Bad zip64 eocd offset %" PRId64 ", eocd locator offset %" PRId64, debugFileName,
- zip64EocdOffset, locatorOffset);
- return kInvalidOffset;
- }
-
- Zip64EocdRecord zip64EocdRecord{};
- if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&zip64EocdRecord),
- sizeof(Zip64EocdRecord), zip64EocdOffset)) {
- ALOGW("Zip: %s: read %zu from offset %" PRId64 " failed %s", debugFileName,
- sizeof(Zip64EocdLocator), zip64EocdOffset, debugFileName);
- return kIoError;
- }
-
- if (zip64EocdRecord.record_signature != Zip64EocdRecord::kSignature) {
- ALOGW("Zip: %s: Zip64 eocd record signature not found at offset %" PRId64, debugFileName,
- zip64EocdOffset);
- return kInvalidFile;
- }
-
- if (zip64EocdOffset <= zip64EocdRecord.cd_size ||
- zip64EocdRecord.cd_start_offset > zip64EocdOffset - zip64EocdRecord.cd_size) {
- ALOGW("Zip: %s: Bad offset for zip64 central directory. cd offset %" PRIu64 ", cd size %" PRIu64
- ", zip64 eocd offset %" PRIu64,
- debugFileName, zip64EocdRecord.cd_start_offset, zip64EocdRecord.cd_size, zip64EocdOffset);
- return kInvalidOffset;
- }
-
- *cdInfo = {.num_records = zip64EocdRecord.num_records,
- .cd_size = zip64EocdRecord.cd_size,
- .cd_start_offset = zip64EocdRecord.cd_start_offset};
-
- return kSuccess;
-}
-
-static ZipError FindCentralDirectoryInfo(const char* debug_file_name, ZipArchive* archive,
- off64_t file_length, uint32_t read_amount,
- CentralDirectoryInfo* cdInfo) {
- std::vector<uint8_t> scan_buffer(read_amount);
- const off64_t search_start = file_length - read_amount;
-
- if (!archive->mapped_zip.ReadAtOffset(scan_buffer.data(), read_amount, search_start)) {
- ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
- static_cast<int64_t>(search_start));
- return kIoError;
- }
-
- /*
- * Scan backward for the EOCD magic. In an archive without a trailing
- * comment, we'll find it on the first try. (We may want to consider
- * doing an initial minimal read; if we don't find it, retry with a
- * second read as above.)
- */
- CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
- int32_t i = read_amount - sizeof(EocdRecord);
- for (; i >= 0; i--) {
- if (scan_buffer[i] == 0x50) {
- uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
- if (android::base::get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
- ALOGV("+++ Found EOCD at buf+%d", i);
- break;
- }
- }
- }
- if (i < 0) {
- ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
- return kInvalidFile;
- }
-
- const off64_t eocd_offset = search_start + i;
- auto eocd = reinterpret_cast<const EocdRecord*>(scan_buffer.data() + i);
- /*
- * Verify that there's no trailing space at the end of the central directory
- * and its comment.
- */
- const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
- if (calculated_length != file_length) {
- ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
- static_cast<int64_t>(file_length - calculated_length));
- return kInvalidFile;
- }
-
- // One of the field is 0xFFFFFFFF, look for the zip64 EOCD instead.
- if (eocd->cd_size == UINT32_MAX || eocd->cd_start_offset == UINT32_MAX) {
- ALOGV("Looking for the zip64 EOCD, cd_size: %" PRIu32 "cd_start_offset: %" PRId32,
- eocd->cd_size, eocd->cd_start_offset);
- return FindCentralDirectoryInfoForZip64(debug_file_name, archive, eocd_offset, cdInfo);
- }
-
- /*
- * Grab the CD offset and size, and the number of entries in the
- * archive and verify that they look reasonable.
- */
- if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
- ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
- eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
- return kInvalidOffset;
- }
-
- *cdInfo = {.num_records = eocd->num_records,
- .cd_size = eocd->cd_size,
- .cd_start_offset = eocd->cd_start_offset};
- return kSuccess;
-}
-
-/*
- * Find the zip Central Directory and memory-map it.
- *
- * On success, returns kSuccess after populating fields from the EOCD area:
- * directory_offset
- * directory_ptr
- * num_entries
- */
-static ZipError MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
- // Test file length. We use lseek64 to make sure the file is small enough to be a zip file.
- off64_t file_length = archive->mapped_zip.GetFileLength();
- if (file_length == -1) {
- return kInvalidFile;
- }
-
- if (file_length > kMaxFileLength) {
- ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
- return kInvalidFile;
- }
-
- if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
- ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
- return kInvalidFile;
- }
-
- /*
- * Perform the traditional EOCD snipe hunt.
- *
- * We're searching for the End of Central Directory magic number,
- * which appears at the start of the EOCD block. It's followed by
- * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
- * need to read the last part of the file into a buffer, dig through
- * it to find the magic number, parse some values out, and use those
- * to determine the extent of the CD.
- *
- * We start by pulling in the last part of the file.
- */
- uint32_t read_amount = kMaxEOCDSearch;
- if (file_length < read_amount) {
- read_amount = static_cast<uint32_t>(file_length);
- }
-
- CentralDirectoryInfo cdInfo = {};
- if (auto result =
- FindCentralDirectoryInfo(debug_file_name, archive, file_length, read_amount, &cdInfo);
- result != kSuccess) {
- return result;
- }
-
- if (cdInfo.num_records == 0) {
-#if defined(__ANDROID__)
- ALOGW("Zip: empty archive?");
-#endif
- return kEmptyArchive;
- }
-
- if (cdInfo.cd_size >= SIZE_MAX) {
- ALOGW("Zip: The size of central directory doesn't fit in range of size_t: %" PRIu64,
- cdInfo.cd_size);
- return kInvalidFile;
- }
-
- ALOGV("+++ num_entries=%" PRIu64 " dir_size=%" PRIu64 " dir_offset=%" PRIu64, cdInfo.num_records,
- cdInfo.cd_size, cdInfo.cd_start_offset);
-
- // It all looks good. Create a mapping for the CD, and set the fields in archive.
- if (!archive->InitializeCentralDirectory(static_cast<off64_t>(cdInfo.cd_start_offset),
- static_cast<size_t>(cdInfo.cd_size))) {
- return kMmapFailed;
- }
-
- archive->num_entries = cdInfo.num_records;
- archive->directory_offset = cdInfo.cd_start_offset;
-
- return kSuccess;
-}
-
-static ZipError ParseZip64ExtendedInfoInExtraField(
- const uint8_t* extraFieldStart, uint16_t extraFieldLength, uint32_t zip32UncompressedSize,
- uint32_t zip32CompressedSize, std::optional<uint32_t> zip32LocalFileHeaderOffset,
- Zip64ExtendedInfo* zip64Info) {
- if (extraFieldLength <= 4) {
- ALOGW("Zip: Extra field isn't large enough to hold zip64 info, size %" PRIu16,
- extraFieldLength);
- return kInvalidFile;
- }
-
- // Each header MUST consist of:
- // Header ID - 2 bytes
- // Data Size - 2 bytes
- uint16_t offset = 0;
- while (offset < extraFieldLength - 4) {
- auto readPtr = const_cast<uint8_t*>(extraFieldStart + offset);
- auto headerId = ConsumeUnaligned<uint16_t>(&readPtr);
- auto dataSize = ConsumeUnaligned<uint16_t>(&readPtr);
-
- offset += 4;
- if (dataSize > extraFieldLength - offset) {
- ALOGW("Zip: Data size exceeds the boundary of extra field, data size %" PRIu16, dataSize);
- return kInvalidOffset;
- }
-
- // Skip the other types of extensible data fields. Details in
- // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT section 4.5
- if (headerId != Zip64ExtendedInfo::kHeaderId) {
- offset += dataSize;
- continue;
- }
-
- std::optional<uint64_t> uncompressedFileSize;
- std::optional<uint64_t> compressedFileSize;
- std::optional<uint64_t> localHeaderOffset;
- if (zip32UncompressedSize == UINT32_MAX) {
- uncompressedFileSize =
- TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
- if (!uncompressedFileSize.has_value()) return kInvalidOffset;
- }
- if (zip32CompressedSize == UINT32_MAX) {
- compressedFileSize =
- TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
- if (!compressedFileSize.has_value()) return kInvalidOffset;
- }
- if (zip32LocalFileHeaderOffset == UINT32_MAX) {
- localHeaderOffset =
- TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
- if (!localHeaderOffset.has_value()) return kInvalidOffset;
- }
-
- // calculate how many bytes we read after the data size field.
- size_t bytesRead = readPtr - (extraFieldStart + offset);
- if (bytesRead == 0) {
- ALOGW("Zip: Data size should not be 0 in zip64 extended field");
- return kInvalidFile;
- }
-
- if (dataSize != bytesRead) {
- auto localOffsetString = zip32LocalFileHeaderOffset.has_value()
- ? std::to_string(zip32LocalFileHeaderOffset.value())
- : "missing";
- ALOGW("Zip: Invalid data size in zip64 extended field, expect %zu , get %" PRIu16
- ", uncompressed size %" PRIu32 ", compressed size %" PRIu32 ", local header offset %s",
- bytesRead, dataSize, zip32UncompressedSize, zip32CompressedSize,
- localOffsetString.c_str());
- return kInvalidFile;
- }
-
- zip64Info->uncompressed_file_size = uncompressedFileSize;
- zip64Info->compressed_file_size = compressedFileSize;
- zip64Info->local_header_offset = localHeaderOffset;
- return kSuccess;
- }
-
- ALOGW("Zip: zip64 extended info isn't found in the extra field.");
- return kInvalidFile;
-}
-
-/*
- * Parses the Zip archive's Central Directory. Allocates and populates the
- * hash table.
- *
- * Returns 0 on success.
- */
-static ZipError ParseZipArchive(ZipArchive* archive) {
- const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
- const size_t cd_length = archive->central_directory.GetMapLength();
- const uint64_t num_entries = archive->num_entries;
-
- if (num_entries <= UINT16_MAX) {
- archive->cd_entry_map = CdEntryMapZip32::Create(static_cast<uint16_t>(num_entries));
- } else {
- archive->cd_entry_map = CdEntryMapZip64::Create();
- }
- if (archive->cd_entry_map == nullptr) {
- return kAllocationFailed;
- }
-
- /*
- * Walk through the central directory, adding entries to the hash
- * table and verifying values.
- */
- const uint8_t* const cd_end = cd_ptr + cd_length;
- const uint8_t* ptr = cd_ptr;
- for (uint64_t i = 0; i < num_entries; i++) {
- if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
- ALOGW("Zip: ran off the end (item #%" PRIu64 ", %zu bytes of central directory)", i,
- cd_length);
-#if defined(__ANDROID__)
- android_errorWriteLog(0x534e4554, "36392138");
-#endif
- return kInvalidFile;
- }
-
- auto cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
- if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
- ALOGW("Zip: missed a central dir sig (at %" PRIu64 ")", i);
- return kInvalidFile;
- }
-
- const uint16_t file_name_length = cdr->file_name_length;
- const uint16_t extra_length = cdr->extra_field_length;
- const uint16_t comment_length = cdr->comment_length;
- const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
-
- if (file_name_length >= cd_length || file_name > cd_end - file_name_length) {
- ALOGW("Zip: file name for entry %" PRIu64
- " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
- i, file_name_length, cd_length);
- return kInvalidEntryName;
- }
-
- const uint8_t* extra_field = file_name + file_name_length;
- if (extra_length >= cd_length || extra_field > cd_end - extra_length) {
- ALOGW("Zip: extra field for entry %" PRIu64
- " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
- i, extra_length, cd_length);
- return kInvalidFile;
- }
-
- off64_t local_header_offset = cdr->local_file_header_offset;
- if (local_header_offset == UINT32_MAX) {
- Zip64ExtendedInfo zip64_info{};
- if (auto status = ParseZip64ExtendedInfoInExtraField(
- extra_field, extra_length, cdr->uncompressed_size, cdr->compressed_size,
- cdr->local_file_header_offset, &zip64_info);
- status != kSuccess) {
- return status;
- }
- CHECK(zip64_info.local_header_offset.has_value());
- local_header_offset = zip64_info.local_header_offset.value();
- }
-
- if (local_header_offset >= archive->directory_offset) {
- ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu64,
- static_cast<int64_t>(local_header_offset), i);
- return kInvalidFile;
- }
-
- // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
- if (!IsValidEntryName(file_name, file_name_length)) {
- ALOGW("Zip: invalid file name at entry %" PRIu64, i);
- return kInvalidEntryName;
- }
-
- // Add the CDE filename to the hash table.
- std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
- if (auto add_result =
- archive->cd_entry_map->AddToMap(entry_name, archive->central_directory.GetBasePtr());
- add_result != 0) {
- ALOGW("Zip: Error adding entry to hash table %d", add_result);
- return add_result;
- }
-
- ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
- if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
- ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu64, ptr - cd_ptr, cd_length, i);
- return kInvalidFile;
- }
- }
-
- uint32_t lfh_start_bytes;
- if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
- sizeof(uint32_t), 0)) {
- ALOGW("Zip: Unable to read header for entry at offset == 0.");
- return kInvalidFile;
- }
-
- if (lfh_start_bytes != LocalFileHeader::kSignature) {
- ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
-#if defined(__ANDROID__)
- android_errorWriteLog(0x534e4554, "64211847");
-#endif
- return kInvalidFile;
- }
-
- ALOGV("+++ zip good scan %" PRIu64 " entries", num_entries);
-
- return kSuccess;
-}
-
-static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
- int32_t result = MapCentralDirectory(debug_file_name, archive);
- return result != kSuccess ? result : ParseZipArchive(archive);
-}
-
-int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
- bool assume_ownership) {
- ZipArchive* archive = new ZipArchive(MappedZipFile(fd), assume_ownership);
- *handle = archive;
- return OpenArchiveInternal(archive, debug_file_name);
-}
-
-int32_t OpenArchiveFdRange(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
- off64_t length, off64_t offset, bool assume_ownership) {
- ZipArchive* archive = new ZipArchive(MappedZipFile(fd, length, offset), assume_ownership);
- *handle = archive;
-
- if (length < 0) {
- ALOGW("Invalid zip length %" PRId64, length);
- return kIoError;
- }
-
- if (offset < 0) {
- ALOGW("Invalid zip offset %" PRId64, offset);
- return kIoError;
- }
-
- return OpenArchiveInternal(archive, debug_file_name);
-}
-
-int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
- const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
- ZipArchive* archive = new ZipArchive(MappedZipFile(fd), true);
- *handle = archive;
-
- if (fd < 0) {
- ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
- return kIoError;
- }
-
- return OpenArchiveInternal(archive, fileName);
-}
-
-int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
- ZipArchiveHandle* handle) {
- ZipArchive* archive = new ZipArchive(address, length);
- *handle = archive;
- return OpenArchiveInternal(archive, debug_file_name);
-}
-
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
- ZipArchiveInfo result;
- result.archive_size = archive->mapped_zip.GetFileLength();
- result.entry_count = archive->num_entries;
- return result;
-}
-
-/*
- * Close a ZipArchive, closing the file and freeing the contents.
- */
-void CloseArchive(ZipArchiveHandle archive) {
- ALOGV("Closing archive %p", archive);
- delete archive;
-}
-
-static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, const ZipEntry64* entry) {
- // Maximum possible size for data descriptor: 2 * 4 + 2 * 8 = 24 bytes
- // The zip format doesn't specify the size of data descriptor. But we won't read OOB here even
- // if the descriptor isn't present. Because the size cd + eocd in the end of the zipfile is
- // larger than 24 bytes. And if the descriptor contains invalid data, we'll abort due to
- // kInconsistentInformation.
- uint8_t ddBuf[24];
- off64_t offset = entry->offset;
- if (entry->method != kCompressStored) {
- offset += entry->compressed_length;
- } else {
- offset += entry->uncompressed_length;
- }
-
- if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
- return kIoError;
- }
-
- const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
- uint8_t* ddReadPtr = (ddSignature == DataDescriptor::kOptSignature) ? ddBuf + 4 : ddBuf;
- DataDescriptor descriptor{};
- descriptor.crc32 = ConsumeUnaligned<uint32_t>(&ddReadPtr);
- if (entry->zip64_format_size) {
- descriptor.compressed_size = ConsumeUnaligned<uint64_t>(&ddReadPtr);
- descriptor.uncompressed_size = ConsumeUnaligned<uint64_t>(&ddReadPtr);
- } else {
- descriptor.compressed_size = ConsumeUnaligned<uint32_t>(&ddReadPtr);
- descriptor.uncompressed_size = ConsumeUnaligned<uint32_t>(&ddReadPtr);
- }
-
- // Validate that the values in the data descriptor match those in the central
- // directory.
- if (entry->compressed_length != descriptor.compressed_size ||
- entry->uncompressed_length != descriptor.uncompressed_size ||
- entry->crc32 != descriptor.crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
- "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
- entry->compressed_length, entry->uncompressed_length, entry->crc32,
- descriptor.compressed_size, descriptor.uncompressed_size, descriptor.crc32);
- return kInconsistentInformation;
- }
-
- return 0;
-}
-
-static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName,
- const uint64_t nameOffset, ZipEntry64* data) {
- // Recover the start of the central directory entry from the filename
- // pointer. The filename is the first entry past the fixed-size data,
- // so we can just subtract back from that.
- const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
- const uint8_t* ptr = base_ptr + nameOffset;
- ptr -= sizeof(CentralDirectoryRecord);
-
- // This is the base of our mmapped region, we have to sanity check that
- // the name that's in the hash table is a pointer to a location within
- // this mapped region.
- if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
- ALOGW("Zip: Invalid entry pointer");
- return kInvalidOffset;
- }
-
- auto cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
-
- // The offset of the start of the central directory in the zipfile.
- // We keep this lying around so that we can sanity check all our lengths
- // and our per-file structures.
- const off64_t cd_offset = archive->directory_offset;
-
- // Fill out the compression method, modification time, crc32
- // and other interesting attributes from the central directory. These
- // will later be compared against values from the local file header.
- data->method = cdr->compression_method;
- data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
- data->crc32 = cdr->crc32;
- data->compressed_length = cdr->compressed_size;
- data->uncompressed_length = cdr->uncompressed_size;
-
- // Figure out the local header offset from the central directory. The
- // actual file data will begin after the local header and the name /
- // extra comments.
- off64_t local_header_offset = cdr->local_file_header_offset;
- // One of the info field is UINT32_MAX, try to parse the real value in the zip64 extended info in
- // the extra field.
- if (cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX ||
- cdr->local_file_header_offset == UINT32_MAX) {
- const uint8_t* extra_field = ptr + sizeof(CentralDirectoryRecord) + cdr->file_name_length;
- Zip64ExtendedInfo zip64_info{};
- if (auto status = ParseZip64ExtendedInfoInExtraField(
- extra_field, cdr->extra_field_length, cdr->uncompressed_size, cdr->compressed_size,
- cdr->local_file_header_offset, &zip64_info);
- status != kSuccess) {
- return status;
- }
-
- data->uncompressed_length = zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size);
- data->compressed_length = zip64_info.compressed_file_size.value_or(cdr->compressed_size);
- local_header_offset = zip64_info.local_header_offset.value_or(local_header_offset);
- data->zip64_format_size =
- cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX;
- }
-
- if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
- ALOGW("Zip: bad local hdr offset in zip");
- return kInvalidOffset;
- }
-
- uint8_t lfh_buf[sizeof(LocalFileHeader)];
- if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
- ALOGW("Zip: failed reading lfh name from offset %" PRId64,
- static_cast<int64_t>(local_header_offset));
- return kIoError;
- }
-
- auto lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
- if (lfh->lfh_signature != LocalFileHeader::kSignature) {
- ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
- static_cast<int64_t>(local_header_offset));
- return kInvalidOffset;
- }
-
- // Check that the local file header name matches the declared name in the central directory.
- CHECK_LE(entryName.size(), UINT16_MAX);
- auto nameLen = static_cast<uint16_t>(entryName.size());
- if (lfh->file_name_length != nameLen) {
- ALOGW("Zip: lfh name length did not match central directory for %s: %" PRIu16 " %" PRIu16,
- std::string(entryName).c_str(), lfh->file_name_length, nameLen);
- return kInconsistentInformation;
- }
- const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
- if (name_offset > cd_offset - lfh->file_name_length) {
- ALOGW("Zip: lfh name has invalid declared length");
- return kInvalidOffset;
- }
-
- std::vector<uint8_t> name_buf(nameLen);
- if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
- ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
- return kIoError;
- }
- if (memcmp(entryName.data(), name_buf.data(), nameLen) != 0) {
- ALOGW("Zip: lfh name did not match central directory");
- return kInconsistentInformation;
- }
-
- uint64_t lfh_uncompressed_size = lfh->uncompressed_size;
- uint64_t lfh_compressed_size = lfh->compressed_size;
- if (lfh_uncompressed_size == UINT32_MAX || lfh_compressed_size == UINT32_MAX) {
- if (lfh_uncompressed_size != UINT32_MAX || lfh_compressed_size != UINT32_MAX) {
- ALOGW(
- "Zip: The zip64 extended field in the local header MUST include BOTH original and "
- "compressed file size fields.");
- return kInvalidFile;
- }
-
- const off64_t lfh_extra_field_offset = name_offset + lfh->file_name_length;
- const uint16_t lfh_extra_field_size = lfh->extra_field_length;
- if (lfh_extra_field_offset > cd_offset - lfh_extra_field_size) {
- ALOGW("Zip: extra field has a bad size for entry %s", std::string(entryName).c_str());
- return kInvalidOffset;
- }
-
- std::vector<uint8_t> local_extra_field(lfh_extra_field_size);
- if (!archive->mapped_zip.ReadAtOffset(local_extra_field.data(), lfh_extra_field_size,
- lfh_extra_field_offset)) {
- ALOGW("Zip: failed reading lfh extra field from offset %" PRId64, lfh_extra_field_offset);
- return kIoError;
- }
-
- Zip64ExtendedInfo zip64_info{};
- if (auto status = ParseZip64ExtendedInfoInExtraField(
- local_extra_field.data(), lfh_extra_field_size, lfh->uncompressed_size,
- lfh->compressed_size, std::nullopt, &zip64_info);
- status != kSuccess) {
- return status;
- }
-
- CHECK(zip64_info.uncompressed_file_size.has_value());
- CHECK(zip64_info.compressed_file_size.has_value());
- lfh_uncompressed_size = zip64_info.uncompressed_file_size.value();
- lfh_compressed_size = zip64_info.compressed_file_size.value();
- }
-
- // Paranoia: Match the values specified in the local file header
- // to those specified in the central directory.
-
- // Warn if central directory and local file header don't agree on the use
- // of a trailing Data Descriptor. The reference implementation is inconsistent
- // and appears to use the LFH value during extraction (unzip) but the CD value
- // while displayng information about archives (zipinfo). The spec remains
- // silent on this inconsistency as well.
- //
- // For now, always use the version from the LFH but make sure that the values
- // specified in the central directory match those in the data descriptor.
- //
- // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
- // bit 11 (EFS: The language encoding flag, marking that filename and comment are
- // encoded using UTF-8). This implementation does not check for the presence of
- // that flag and always enforces that entry names are valid UTF-8.
- if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
- ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
- cdr->gpb_flags, lfh->gpb_flags);
- }
-
- // If there is no trailing data descriptor, verify that the central directory and local file
- // header agree on the crc, compressed, and uncompressed sizes of the entry.
- if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
- data->has_data_descriptor = 0;
- if (data->compressed_length != lfh_compressed_size ||
- data->uncompressed_length != lfh_uncompressed_size || data->crc32 != lfh->crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
- "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
- data->compressed_length, data->uncompressed_length, data->crc32, lfh_compressed_size,
- lfh_uncompressed_size, lfh->crc32);
- return kInconsistentInformation;
- }
- } else {
- data->has_data_descriptor = 1;
- }
-
- // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
- data->version_made_by = cdr->version_made_by;
- data->external_file_attributes = cdr->external_file_attributes;
- if ((data->version_made_by >> 8) == 3) {
- data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
- } else {
- data->unix_mode = 0777;
- }
-
- // 4.4.4: general purpose bit flags.
- data->gpbf = lfh->gpb_flags;
-
- // 4.4.14: the lowest bit of the internal file attributes field indicates text.
- // Currently only needed to implement zipinfo.
- data->is_text = (cdr->internal_file_attributes & 1);
-
- const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
- lfh->file_name_length + lfh->extra_field_length;
- if (data_offset > cd_offset) {
- ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
- return kInvalidOffset;
- }
-
- if (data->compressed_length > cd_offset - data_offset) {
- ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->compressed_length,
- static_cast<int64_t>(cd_offset));
- return kInvalidOffset;
- }
-
- if (data->method == kCompressStored && data->uncompressed_length > cd_offset - data_offset) {
- ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->uncompressed_length,
- static_cast<int64_t>(cd_offset));
- return kInvalidOffset;
- }
-
- data->offset = data_offset;
- return 0;
-}
-
-struct IterationHandle {
- ZipArchive* archive;
-
- std::function<bool(std::string_view)> matcher;
-
- uint32_t position = 0;
-
- IterationHandle(ZipArchive* archive, std::function<bool(std::string_view)> in_matcher)
- : archive(archive), matcher(std::move(in_matcher)) {}
-
- bool Match(std::string_view entry_name) const { return matcher(entry_name); }
-};
-
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const std::string_view optional_prefix,
- const std::string_view optional_suffix) {
- if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
- optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
- ALOGW("Zip: prefix/suffix too long");
- return kInvalidEntryName;
- }
- auto matcher = [prefix = std::string(optional_prefix),
- suffix = std::string(optional_suffix)](std::string_view name) mutable {
- return android::base::StartsWith(name, prefix) && android::base::EndsWith(name, suffix);
- };
- return StartIteration(archive, cookie_ptr, std::move(matcher));
-}
-
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- std::function<bool(std::string_view)> matcher) {
- if (archive == nullptr || archive->cd_entry_map == nullptr) {
- ALOGW("Zip: Invalid ZipArchiveHandle");
- return kInvalidHandle;
- }
-
- archive->cd_entry_map->ResetIteration();
- *cookie_ptr = new IterationHandle(archive, matcher);
- return 0;
-}
-
-void EndIteration(void* cookie) {
- delete reinterpret_cast<IterationHandle*>(cookie);
-}
-
-int32_t ZipEntry::CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src) {
- if (src->compressed_length > UINT32_MAX || src->uncompressed_length > UINT32_MAX) {
- ALOGW(
- "Zip: the entry size is too large to fit into the 32 bits ZipEntry, uncompressed "
- "length %" PRIu64 ", compressed length %" PRIu64,
- src->uncompressed_length, src->compressed_length);
- return kUnsupportedEntrySize;
- }
-
- *dst = *src;
- dst->uncompressed_length = static_cast<uint32_t>(src->uncompressed_length);
- dst->compressed_length = static_cast<uint32_t>(src->compressed_length);
- return kSuccess;
-}
-
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
- ZipEntry* data) {
- ZipEntry64 entry64;
- if (auto status = FindEntry(archive, entryName, &entry64); status != kSuccess) {
- return status;
- }
-
- return ZipEntry::CopyFromZipEntry64(data, &entry64);
-}
-
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
- ZipEntry64* data) {
- if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
- ALOGW("Zip: Invalid filename of length %zu", entryName.size());
- return kInvalidEntryName;
- }
-
- const auto [result, offset] =
- archive->cd_entry_map->GetCdEntryOffset(entryName, archive->central_directory.GetBasePtr());
- if (result != 0) {
- ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
- return static_cast<int32_t>(result); // kEntryNotFound is safe to truncate.
- }
- // We know there are at most hash_table_size entries, safe to truncate.
- return FindEntry(archive, entryName, offset, data);
-}
-
-int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
- ZipEntry64 entry64;
- if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
- return status;
- }
-
- return ZipEntry::CopyFromZipEntry64(data, &entry64);
-}
-
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
- ZipEntry64 entry64;
- if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
- return status;
- }
-
- return ZipEntry::CopyFromZipEntry64(data, &entry64);
-}
-
-int32_t Next(void* cookie, ZipEntry64* data, std::string* name) {
- std::string_view sv;
- int32_t result = Next(cookie, data, &sv);
- if (result == 0 && name) {
- *name = std::string(sv);
- }
- return result;
-}
-
-int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name) {
- IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
- if (handle == nullptr) {
- ALOGW("Zip: Null ZipArchiveHandle");
- return kInvalidHandle;
- }
-
- ZipArchive* archive = handle->archive;
- if (archive == nullptr || archive->cd_entry_map == nullptr) {
- ALOGW("Zip: Invalid ZipArchiveHandle");
- return kInvalidHandle;
- }
-
- auto entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr());
- while (entry != std::pair<std::string_view, uint64_t>()) {
- const auto [entry_name, offset] = entry;
- if (handle->Match(entry_name)) {
- const int error = FindEntry(archive, entry_name, offset, data);
- if (!error && name) {
- *name = entry_name;
- }
- return error;
- }
- entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr());
- }
-
- archive->cd_entry_map->ResetIteration();
- return kIterationEnd;
-}
-
-// A Writer that writes data to a fixed size memory region.
-// The size of the memory region must be equal to the total size of
-// the data appended to it.
-class MemoryWriter : public zip_archive::Writer {
- public:
- static std::unique_ptr<MemoryWriter> Create(uint8_t* buf, size_t size, const ZipEntry64* entry) {
- const uint64_t declared_length = entry->uncompressed_length;
- if (declared_length > size) {
- ALOGW("Zip: file size %" PRIu64 " is larger than the buffer size %zu.", declared_length,
- size);
- return nullptr;
- }
-
- return std::unique_ptr<MemoryWriter>(new MemoryWriter(buf, size));
- }
-
- virtual bool Append(uint8_t* buf, size_t buf_size) override {
- if (size_ < buf_size || bytes_written_ > size_ - buf_size) {
- ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
- bytes_written_ + buf_size);
- return false;
- }
-
- memcpy(buf_ + bytes_written_, buf, buf_size);
- bytes_written_ += buf_size;
- return true;
- }
-
- private:
- MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
-
- uint8_t* const buf_{nullptr};
- const size_t size_;
- size_t bytes_written_;
-};
-
-// A Writer that appends data to a file |fd| at its current position.
-// The file will be truncated to the end of the written data.
-class FileWriter : public zip_archive::Writer {
- public:
- // Creates a FileWriter for |fd| and prepare to write |entry| to it,
- // guaranteeing that the file descriptor is valid and that there's enough
- // space on the volume to write out the entry completely and that the file
- // is truncated to the correct length (no truncation if |fd| references a
- // block device).
- //
- // Returns a valid FileWriter on success, |nullptr| if an error occurred.
- static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry64* entry) {
- const uint64_t declared_length = entry->uncompressed_length;
- const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
- if (current_offset == -1) {
- ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
- return nullptr;
- }
-
- if (declared_length > SIZE_MAX || declared_length > INT64_MAX) {
- ALOGW("Zip: file size %" PRIu64 " is too large to extract.", declared_length);
- return nullptr;
- }
-
-#if defined(__linux__)
- if (declared_length > 0) {
- // Make sure we have enough space on the volume to extract the compressed
- // entry. Note that the call to ftruncate below will change the file size but
- // will not allocate space on disk and this call to fallocate will not
- // change the file size.
- // Note: fallocate is only supported by the following filesystems -
- // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
- // EOPNOTSUPP error when issued in other filesystems.
- // Hence, check for the return error code before concluding that the
- // disk does not have enough space.
- long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
- if (result == -1 && errno == ENOSPC) {
- ALOGW("Zip: unable to allocate %" PRIu64 " bytes at offset %" PRId64 ": %s",
- declared_length, static_cast<int64_t>(current_offset), strerror(errno));
- return nullptr;
- }
- }
-#endif // __linux__
-
- struct stat sb;
- if (fstat(fd, &sb) == -1) {
- ALOGW("Zip: unable to fstat file: %s", strerror(errno));
- return nullptr;
- }
-
- // Block device doesn't support ftruncate(2).
- if (!S_ISBLK(sb.st_mode)) {
- long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
- if (result == -1) {
- ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
- static_cast<int64_t>(declared_length + current_offset), strerror(errno));
- return nullptr;
- }
- }
-
- return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
- }
-
- FileWriter(FileWriter&& other) noexcept
- : fd_(other.fd_),
- declared_length_(other.declared_length_),
- total_bytes_written_(other.total_bytes_written_) {
- other.fd_ = -1;
- }
-
- virtual bool Append(uint8_t* buf, size_t buf_size) override {
- if (declared_length_ < buf_size || total_bytes_written_ > declared_length_ - buf_size) {
- ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
- total_bytes_written_ + buf_size);
- return false;
- }
-
- const bool result = android::base::WriteFully(fd_, buf, buf_size);
- if (result) {
- total_bytes_written_ += buf_size;
- } else {
- ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
- }
-
- return result;
- }
-
- private:
- explicit FileWriter(const int fd = -1, const uint64_t declared_length = 0)
- : Writer(),
- fd_(fd),
- declared_length_(static_cast<size_t>(declared_length)),
- total_bytes_written_(0) {
- CHECK_LE(declared_length, SIZE_MAX);
- }
-
- int fd_;
- const size_t declared_length_;
- size_t total_bytes_written_;
-};
-
-class EntryReader : public zip_archive::Reader {
- public:
- EntryReader(const MappedZipFile& zip_file, const ZipEntry64* entry)
- : Reader(), zip_file_(zip_file), entry_(entry) {}
-
- virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
- return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
- }
-
- virtual ~EntryReader() {}
-
- private:
- const MappedZipFile& zip_file_;
- const ZipEntry64* entry_;
-};
-
-// This method is using libz macros with old-style-casts
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
- return inflateInit2(stream, window_bits);
-}
-#pragma GCC diagnostic pop
-
-namespace zip_archive {
-
-// Moved out of line to avoid -Wweak-vtables.
-Reader::~Reader() {}
-Writer::~Writer() {}
-
-int32_t Inflate(const Reader& reader, const uint64_t compressed_length,
- const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
- const size_t kBufSize = 32768;
- std::vector<uint8_t> read_buf(kBufSize);
- std::vector<uint8_t> write_buf(kBufSize);
- z_stream zstream;
- int zerr;
-
- /*
- * Initialize the zlib stream struct.
- */
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
- zstream.next_out = &write_buf[0];
- zstream.avail_out = kBufSize;
- zstream.data_type = Z_UNKNOWN;
-
- /*
- * Use the undocumented "negative window bits" feature to tell zlib
- * that there's no zlib header waiting for it.
- */
- zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
- } else {
- ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
- }
-
- return kZlibError;
- }
-
- auto zstream_deleter = [](z_stream* stream) {
- inflateEnd(stream); /* free up any allocated structures */
- };
-
- std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
-
- const bool compute_crc = (crc_out != nullptr);
- uLong crc = 0;
- uint64_t remaining_bytes = compressed_length;
- uint64_t total_output = 0;
- do {
- /* read as much as we can */
- if (zstream.avail_in == 0) {
- const uint32_t read_size =
- (remaining_bytes > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining_bytes);
- const off64_t offset = (compressed_length - remaining_bytes);
- // Make sure to read at offset to ensure concurrent access to the fd.
- if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
- ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
- return kIoError;
- }
-
- remaining_bytes -= read_size;
-
- zstream.next_in = &read_buf[0];
- zstream.avail_in = read_size;
- }
-
- /* uncompress the data */
- zerr = inflate(&zstream, Z_NO_FLUSH);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
- zstream.avail_in, zstream.next_out, zstream.avail_out);
- return kZlibError;
- }
-
- /* write when we're full or when we're done */
- if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
- const size_t write_size = zstream.next_out - &write_buf[0];
- if (!writer->Append(&write_buf[0], write_size)) {
- return kIoError;
- } else if (compute_crc) {
- DCHECK_LE(write_size, kBufSize);
- crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
- }
-
- total_output += kBufSize - zstream.avail_out;
- zstream.next_out = &write_buf[0];
- zstream.avail_out = kBufSize;
- }
- } while (zerr == Z_OK);
-
- 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
- // doesn't bother calculating the checksum in that scenario. We just do
- // it ourselves above because there are no additional gains to be made by
- // having zlib calculate it for us, since they do it by calling crc32 in
- // the same manner that we have above.
- if (compute_crc) {
- *crc_out = crc;
- }
- if (total_output != uncompressed_length || remaining_bytes != 0) {
- ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu64 ")", zstream.total_out,
- uncompressed_length);
- return kInconsistentInformation;
- }
-
- return 0;
-}
-} // namespace zip_archive
-
-static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
- zip_archive::Writer* writer, uint64_t* crc_out) {
- const EntryReader reader(mapped_zip, entry);
-
- return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
- crc_out);
-}
-
-static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
- zip_archive::Writer* writer, uint64_t* crc_out) {
- static const uint32_t kBufSize = 32768;
- std::vector<uint8_t> buf(kBufSize);
-
- const uint64_t length = entry->uncompressed_length;
- uint64_t count = 0;
- uLong crc = 0;
- while (count < length) {
- uint64_t remaining = length - count;
- off64_t offset = entry->offset + count;
-
- // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
- const uint32_t block_size =
- (remaining > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining);
-
- // Make sure to read at offset to ensure concurrent access to the fd.
- if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
- ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
- block_size, static_cast<int64_t>(offset), strerror(errno));
- return kIoError;
- }
-
- if (!writer->Append(&buf[0], block_size)) {
- return kIoError;
- }
- if (crc_out) {
- crc = crc32(crc, &buf[0], block_size);
- }
- count += block_size;
- }
-
- if (crc_out) {
- *crc_out = crc;
- }
-
- return 0;
-}
-
-int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry,
- zip_archive::Writer* writer) {
- const uint16_t method = entry->method;
-
- // this should default to kUnknownCompressionMethod.
- int32_t return_value = -1;
- uint64_t crc = 0;
- if (method == kCompressStored) {
- return_value =
- CopyEntryToWriter(handle->mapped_zip, entry, writer, kCrcChecksEnabled ? &crc : nullptr);
- } else if (method == kCompressDeflated) {
- return_value =
- InflateEntryToWriter(handle->mapped_zip, entry, writer, kCrcChecksEnabled ? &crc : nullptr);
- }
-
- if (!return_value && entry->has_data_descriptor) {
- return_value = ValidateDataDescriptor(handle->mapped_zip, entry);
- if (return_value) {
- return return_value;
- }
- }
-
- // Validate that the CRC matches the calculated value.
- if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
- ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
- return kInconsistentInformation;
- }
-
- return return_value;
-}
-
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin,
- size_t size) {
- ZipEntry64 entry64(*entry);
- return ExtractToMemory(archive, &entry64, begin, size);
-}
-
-int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin,
- size_t size) {
- auto writer = MemoryWriter::Create(begin, size, entry);
- if (!writer) {
- return kIoError;
- }
-
- return ExtractToWriter(archive, entry, writer.get());
-}
-
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd) {
- ZipEntry64 entry64(*entry);
- return ExtractEntryToFile(archive, &entry64, fd);
-}
-
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd) {
- auto writer = FileWriter::Create(fd, entry);
- if (!writer) {
- return kIoError;
- }
-
- return ExtractToWriter(archive, entry, writer.get());
-}
-
-int GetFileDescriptor(const ZipArchiveHandle archive) {
- return archive->mapped_zip.GetFileDescriptor();
-}
-
-off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive) {
- return archive->mapped_zip.GetFileOffset();
-}
-
-#if !defined(_WIN32)
-class ProcessWriter : public zip_archive::Writer {
- public:
- ProcessWriter(ProcessZipEntryFunction func, void* cookie)
- : Writer(), proc_function_(func), cookie_(cookie) {}
-
- virtual bool Append(uint8_t* buf, size_t buf_size) override {
- return proc_function_(buf, buf_size, cookie_);
- }
-
- private:
- ProcessZipEntryFunction proc_function_;
- void* cookie_;
-};
-
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry,
- ProcessZipEntryFunction func, void* cookie) {
- ZipEntry64 entry64(*entry);
- return ProcessZipEntryContents(archive, &entry64, func, cookie);
-}
-
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry,
- ProcessZipEntryFunction func, void* cookie) {
- ProcessWriter writer(func, cookie);
- return ExtractToWriter(archive, entry, &writer);
-}
-
-#endif //! defined(_WIN32)
-
-int MappedZipFile::GetFileDescriptor() const {
- if (!has_fd_) {
- ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
- return -1;
- }
- return fd_;
-}
-
-const void* MappedZipFile::GetBasePtr() const {
- if (has_fd_) {
- ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
- return nullptr;
- }
- return base_ptr_;
-}
-
-off64_t MappedZipFile::GetFileOffset() const {
- return fd_offset_;
-}
-
-off64_t MappedZipFile::GetFileLength() const {
- if (has_fd_) {
- if (data_length_ != -1) {
- return data_length_;
- }
- data_length_ = lseek64(fd_, 0, SEEK_END);
- if (data_length_ == -1) {
- ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
- }
- return data_length_;
- } else {
- if (base_ptr_ == nullptr) {
- ALOGE("Zip: invalid file map");
- return -1;
- }
- return data_length_;
- }
-}
-
-// Attempts to read |len| bytes into |buf| at offset |off|.
-bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
- if (has_fd_) {
- if (off < 0) {
- ALOGE("Zip: invalid offset %" PRId64, off);
- return false;
- }
-
- off64_t read_offset;
- if (__builtin_add_overflow(fd_offset_, off, &read_offset)) {
- ALOGE("Zip: invalid read offset %" PRId64 " overflows, fd offset %" PRId64, off, fd_offset_);
- return false;
- }
-
- if (data_length_ != -1) {
- off64_t read_end;
- if (len > std::numeric_limits<off64_t>::max() ||
- __builtin_add_overflow(off, static_cast<off64_t>(len), &read_end)) {
- ALOGE("Zip: invalid read length %" PRId64 " overflows, offset %" PRId64,
- static_cast<off64_t>(len), off);
- return false;
- }
-
- if (read_end > data_length_) {
- ALOGE("Zip: invalid read length %" PRId64 " exceeds data length %" PRId64 ", offset %"
- PRId64, static_cast<off64_t>(len), data_length_, off);
- return false;
- }
- }
-
- if (!android::base::ReadFullyAtOffset(fd_, buf, len, read_offset)) {
- ALOGE("Zip: failed to read at offset %" PRId64, off);
- return false;
- }
- } else {
- if (off < 0 || data_length_ < len || off > data_length_ - len) {
- ALOGE("Zip: invalid offset: %" PRId64 ", read length: %zu, data length: %" PRId64, off, len,
- data_length_);
- return false;
- }
- memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
- }
- return true;
-}
-
-void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_offset,
- size_t cd_size) {
- base_ptr_ = static_cast<const uint8_t*>(map_base_ptr) + cd_start_offset;
- length_ = cd_size;
-}
-
-bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
- if (mapped_zip.HasFd()) {
- directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
- mapped_zip.GetFileOffset() + cd_start_offset,
- cd_size, PROT_READ);
- if (!directory_map) {
- ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
- cd_start_offset, cd_size, strerror(errno));
- return false;
- }
-
- 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");
- return false;
- }
- if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
- mapped_zip.GetFileLength()) {
- ALOGE(
- "Zip: Failed to map central directory, offset exceeds mapped memory region ("
- "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
- static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
- return false;
- }
-
- central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
- }
- return true;
-}
-
-// This function returns the embedded timestamp as is; and doesn't perform validations.
-tm ZipEntryCommon::GetModificationTime() const {
- tm t = {};
-
- t.tm_hour = (mod_time >> 11) & 0x1f;
- t.tm_min = (mod_time >> 5) & 0x3f;
- t.tm_sec = (mod_time & 0x1f) << 1;
-
- t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
- t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
- t.tm_mday = (mod_time >> 16) & 0x1f;
-
- return t;
-}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
deleted file mode 100644
index cfa5912..0000000
--- a/libziparchive/zip_archive_benchmark.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <string>
-#include <tuple>
-#include <vector>
-
-#include <android-base/test_utils.h>
-#include <benchmark/benchmark.h>
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-#include <ziparchive/zip_writer.h>
-
-static std::unique_ptr<TemporaryFile> CreateZip(int size = 4, int count = 1000) {
- auto result = std::make_unique<TemporaryFile>();
- FILE* fp = fdopen(result->fd, "w");
-
- ZipWriter writer(fp);
- std::string lastName = "file";
- for (size_t i = 0; i < count; i++) {
- // Make file names longer and longer.
- lastName = lastName + std::to_string(i);
- writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
- while (size > 0) {
- writer.WriteBytes("helo", 4);
- size -= 4;
- }
- writer.FinishEntry();
- }
- writer.Finish();
- fclose(fp);
-
- return result;
-}
-
-static void FindEntry_no_match(benchmark::State& state) {
- // Create a temporary zip archive.
- std::unique_ptr<TemporaryFile> temp_file(CreateZip());
- ZipArchiveHandle handle;
- ZipEntry data;
-
- // In order to walk through all file names in the archive, look for a name
- // that does not exist in the archive.
- std::string_view name("thisFileNameDoesNotExist");
-
- // Start the benchmark.
- for (auto _ : state) {
- OpenArchive(temp_file->path, &handle);
- FindEntry(handle, name, &data);
- CloseArchive(handle);
- }
-}
-BENCHMARK(FindEntry_no_match);
-
-static void Iterate_all_files(benchmark::State& state) {
- std::unique_ptr<TemporaryFile> temp_file(CreateZip());
- ZipArchiveHandle handle;
- void* iteration_cookie;
- ZipEntry data;
- std::string name;
-
- for (auto _ : state) {
- OpenArchive(temp_file->path, &handle);
- StartIteration(handle, &iteration_cookie);
- while (Next(iteration_cookie, &data, &name) == 0) {
- }
- EndIteration(iteration_cookie);
- CloseArchive(handle);
- }
-}
-BENCHMARK(Iterate_all_files);
-
-static void StartAlignedEntry(benchmark::State& state) {
- TemporaryFile file;
- FILE* fp = fdopen(file.fd, "w");
-
- ZipWriter writer(fp);
-
- auto alignment = uint32_t(state.range(0));
- std::string name = "name";
- int counter = 0;
- for (auto _ : state) {
- writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
- state.PauseTiming();
- writer.WriteBytes("hola", 4);
- writer.FinishEntry();
- state.ResumeTiming();
- }
-
- writer.Finish();
- fclose(fp);
-}
-BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
-
-static void ExtractEntry(benchmark::State& state) {
- std::unique_ptr<TemporaryFile> temp_file(CreateZip(1024 * 1024, 1));
-
- ZipArchiveHandle handle;
- ZipEntry data;
- if (OpenArchive(temp_file->path, &handle)) {
- state.SkipWithError("Failed to open archive");
- }
- if (FindEntry(handle, "file0", &data)) {
- state.SkipWithError("Failed to find archive entry");
- }
-
- std::vector<uint8_t> buffer(1024 * 1024);
- for (auto _ : state) {
- if (ExtractToMemory(handle, &data, buffer.data(), uint32_t(buffer.size()))) {
- state.SkipWithError("Failed to extract archive entry");
- break;
- }
- }
- CloseArchive(handle);
-}
-
-BENCHMARK(ExtractEntry)->Arg(2)->Arg(16)->Arg(1024);
-
-BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
deleted file mode 100644
index d461856..0000000
--- a/libziparchive/zip_archive_common.h
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
-#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
-
-#include "android-base/macros.h"
-
-#include <inttypes.h>
-
-#include <optional>
-
-// The "end of central directory" (EOCD) record. Each archive
-// contains exactly once such record which appears at the end of
-// the archive. It contains archive wide information like the
-// number of entries in the archive and the offset to the central
-// directory of the offset.
-struct EocdRecord {
- static const uint32_t kSignature = 0x06054b50;
-
- // End of central directory signature, should always be
- // |kSignature|.
- uint32_t eocd_signature;
- // The number of the current "disk", i.e, the "disk" that this
- // central directory is on.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that disk_num == 1.
- uint16_t disk_num;
- // The disk where the central directory starts.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that cd_start_disk == 1.
- uint16_t cd_start_disk;
- // The number of central directory records on this disk.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that num_records_on_disk == num_records.
- uint16_t num_records_on_disk;
- // The total number of central directory records.
- uint16_t num_records;
- // The size of the central directory (in bytes).
- uint32_t cd_size;
- // The offset of the start of the central directory, relative
- // to the start of the file.
- uint32_t cd_start_offset;
- // Length of the central directory comment.
- uint16_t comment_length;
-
- private:
- EocdRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(EocdRecord);
-} __attribute__((packed));
-
-// A structure representing the fixed length fields for a single
-// record in the central directory of the archive. In addition to
-// the fixed length fields listed here, each central directory
-// record contains a variable length "file_name" and "extra_field"
-// whose lengths are given by |file_name_length| and |extra_field_length|
-// respectively.
-struct CentralDirectoryRecord {
- static const uint32_t kSignature = 0x02014b50;
-
- // The start of record signature. Must be |kSignature|.
- uint32_t record_signature;
- // Source tool version. Top byte gives source OS.
- uint16_t version_made_by;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
- // The length of the entry comment (in bytes). This data will
- // appear immediately after the extra field.
- uint16_t comment_length;
- // The start disk for this entry. Ignored by this implementation).
- uint16_t file_start_disk;
- // File attributes. Ignored by this implementation.
- uint16_t internal_file_attributes;
- // File attributes. For archives created on Unix, the top bits are the mode.
- uint32_t external_file_attributes;
- // The offset to the local file header for this entry, from the
- // beginning of this archive.
- uint32_t local_file_header_offset;
-
- private:
- CentralDirectoryRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-} __attribute__((packed));
-
-// The local file header for a given entry. This duplicates information
-// present in the central directory of the archive. It is an error for
-// the information here to be different from the central directory
-// information for a given entry.
-struct LocalFileHeader {
- static const uint32_t kSignature = 0x04034b50;
-
- // The local file header signature, must be |kSignature|.
- uint32_t lfh_signature;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
-
- private:
- LocalFileHeader() = default;
- DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-} __attribute__((packed));
-
-struct DataDescriptor {
- // The *optional* data descriptor start signature.
- static const uint32_t kOptSignature = 0x08074b50;
-
- // CRC-32 checksum of the entry.
- uint32_t crc32;
-
- // For ZIP64 format archives, the compressed and uncompressed sizes are 8
- // bytes each. Also, the ZIP64 format MAY be used regardless of the size
- // of a file. When extracting, if the zip64 extended information extra field
- // is present for the file the compressed and uncompressed sizes will be 8
- // byte values.
-
- // Compressed size of the entry, the field can be either 4 bytes or 8 bytes
- // in the zip file.
- uint64_t compressed_size;
- // Uncompressed size of the entry, the field can be either 4 bytes or 8 bytes
- // in the zip file.
- uint64_t uncompressed_size;
-
- private:
- DataDescriptor() = default;
- DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-};
-
-// The zip64 end of central directory locator helps to find the zip64 EOCD.
-struct Zip64EocdLocator {
- static constexpr uint32_t kSignature = 0x07064b50;
-
- // The signature of zip64 eocd locator, must be |kSignature|
- uint32_t locator_signature;
- // The start disk of the zip64 eocd. This implementation assumes that each
- // archive spans a single disk only.
- uint32_t eocd_start_disk;
- // The offset offset of the zip64 end of central directory record.
- uint64_t zip64_eocd_offset;
- // The total number of disks. This implementation assumes that each archive
- // spans a single disk only.
- uint32_t num_of_disks;
-
- private:
- Zip64EocdLocator() = default;
- DISALLOW_COPY_AND_ASSIGN(Zip64EocdLocator);
-} __attribute__((packed));
-
-// The optional zip64 EOCD. If one of the fields in the end of central directory
-// record is too small to hold required data, the field SHOULD be set to -1
-// (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record SHOULD be created.
-struct Zip64EocdRecord {
- static constexpr uint32_t kSignature = 0x06064b50;
-
- // The signature of zip64 eocd record, must be |kSignature|
- uint32_t record_signature;
- // Size of zip64 end of central directory record. It SHOULD be the size of the
- // remaining record and SHOULD NOT include the leading 12 bytes.
- uint64_t record_size;
- // The version of the tool that make this archive.
- uint16_t version_made_by;
- // Tool version needed to extract this archive.
- uint16_t version_needed;
- // Number of this disk.
- uint32_t disk_num;
- // Number of the disk with the start of the central directory.
- uint32_t cd_start_disk;
- // Total number of entries in the central directory on this disk.
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that num_records_on_disk == num_records.
- uint64_t num_records_on_disk;
- // The total number of central directory records.
- uint64_t num_records;
- // The size of the central directory in bytes.
- uint64_t cd_size;
- // The offset of the start of the central directory, relative to the start of
- // the file.
- uint64_t cd_start_offset;
-
- private:
- Zip64EocdRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(Zip64EocdRecord);
-} __attribute__((packed));
-
-// The possible contents of the Zip64 Extended Information Extra Field. It may appear in
-// the 'extra' field of a central directory record or local file header. The order of
-// the fields in the zip64 extended information record is fixed, but the fields MUST
-// only appear if the corresponding local or central directory record field is set to
-// 0xFFFF or 0xFFFFFFFF. And this entry in the Local header MUST include BOTH original
-// and compressed file size fields.
-struct Zip64ExtendedInfo {
- static constexpr uint16_t kHeaderId = 0x0001;
- // The header tag for this 'extra' block, should be |kHeaderId|.
- uint16_t header_id;
- // The size in bytes of the remaining data (excluding the top 4 bytes).
- uint16_t data_size;
- // Size in bytes of the uncompressed file.
- std::optional<uint64_t> uncompressed_file_size;
- // Size in bytes of the compressed file.
- std::optional<uint64_t> compressed_file_size;
- // Local file header offset relative to the start of the zip file.
- std::optional<uint64_t> local_header_offset;
-
- // This implementation assumes that each archive spans a single disk only. So
- // the disk_number is not used.
- // uint32_t disk_num;
- private:
- Zip64ExtendedInfo() = default;
- DISALLOW_COPY_AND_ASSIGN(Zip64ExtendedInfo);
-};
-
-// mask value that signifies that the entry has a DD
-static const uint32_t kGPBDDFlagMask = 0x0008;
-
-// The maximum size of a central directory or a file
-// comment in bytes.
-static const uint32_t kMaxCommentLen = 65535;
-
-#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
deleted file mode 100644
index 4b43cba..0000000
--- a/libziparchive/zip_archive_private.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2008 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 <ziparchive/zip_archive.h>
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "android-base/macros.h"
-#include "android-base/mapped_file.h"
-#include "android-base/memory.h"
-#include "zip_cd_entry_map.h"
-#include "zip_error.h"
-
-class MappedZipFile {
- public:
- explicit MappedZipFile(const int fd)
- : has_fd_(true), fd_(fd), fd_offset_(0), base_ptr_(nullptr), data_length_(-1) {}
-
- explicit MappedZipFile(const int fd, off64_t length, off64_t offset)
- : has_fd_(true), fd_(fd), fd_offset_(offset), base_ptr_(nullptr), data_length_(length) {}
-
- explicit MappedZipFile(const void* address, size_t length)
- : has_fd_(false), fd_(-1), fd_offset_(0), base_ptr_(address),
- data_length_(static_cast<off64_t>(length)) {}
-
- bool HasFd() const { return has_fd_; }
-
- int GetFileDescriptor() const;
-
- const void* GetBasePtr() const;
-
- off64_t GetFileOffset() const;
-
- off64_t GetFileLength() const;
-
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
-
- private:
- // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
- // from the file. Otherwise, we're opening the archive from a memory mapped
- // file. In that case, base_ptr_ points to the start of the memory region and
- // data_length_ defines the file length.
- const bool has_fd_;
-
- const int fd_;
- const off64_t fd_offset_;
-
- const void* const base_ptr_;
- mutable off64_t data_length_;
-};
-
-class CentralDirectory {
- public:
- CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
-
- const uint8_t* GetBasePtr() const { return base_ptr_; }
-
- size_t GetMapLength() const { return length_; }
-
- void Initialize(const void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
-
- private:
- const uint8_t* base_ptr_;
- size_t length_;
-};
-
-struct ZipArchive {
- // open Zip archive
- mutable MappedZipFile mapped_zip;
- const bool close_file;
-
- // mapped central directory area
- off64_t directory_offset;
- CentralDirectory central_directory;
- std::unique_ptr<android::base::MappedFile> directory_map;
-
- // number of entries in the Zip archive
- uint64_t num_entries;
- std::unique_ptr<CdEntryMapInterface> cd_entry_map;
-
- ZipArchive(MappedZipFile&& map, bool assume_ownership);
- ZipArchive(const void* address, size_t length);
- ~ZipArchive();
-
- bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
-};
-
-int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry,
- zip_archive::Writer* writer);
-
-// Reads the unaligned data of type |T| and auto increment the offset.
-template <typename T>
-static T ConsumeUnaligned(uint8_t** address) {
- auto ret = android::base::get_unaligned<T>(*address);
- *address += sizeof(T);
- return ret;
-}
-
-// Writes the unaligned data of type |T| and auto increment the offset.
-template <typename T>
-void EmitUnaligned(uint8_t** address, T data) {
- android::base::put_unaligned<T>(*address, data);
- *address += sizeof(T);
-}
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
deleted file mode 100644
index 1ec95b6..0000000
--- a/libziparchive/zip_archive_stream_entry.cc
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ZIPARCHIVE"
-
-// Read-only stream access to Zip Archive entries.
-#include <errno.h>
-#include <inttypes.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <log/log.h>
-
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-#include <zlib.h>
-
-#include "zip_archive_private.h"
-
-static constexpr size_t kBufSize = 65535;
-
-bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
- crc32_ = entry.crc32;
- offset_ = entry.offset;
- return true;
-}
-
-class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
- public:
- explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
- : ZipArchiveStreamEntry(handle) {}
- virtual ~ZipArchiveStreamEntryUncompressed() {}
-
- const std::vector<uint8_t>* Read() override;
-
- bool Verify() override;
-
- protected:
- bool Init(const ZipEntry& entry) override;
-
- uint32_t length_ = 0u;
-
- private:
- std::vector<uint8_t> data_;
- uint32_t computed_crc32_ = 0u;
-};
-
-bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
- if (!ZipArchiveStreamEntry::Init(entry)) {
- return false;
- }
-
- length_ = entry.uncompressed_length;
-
- data_.resize(kBufSize);
- computed_crc32_ = 0;
-
- return true;
-}
-
-const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
- // Simple sanity check. The vector should *only* be handled by this code. A caller
- // should not const-cast and modify the capacity. This may invalidate next_out.
- //
- // Note: it would be better to store the results of data() across Read calls.
- CHECK_EQ(data_.capacity(), kBufSize);
-
- if (length_ == 0) {
- return nullptr;
- }
-
- size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
- errno = 0;
- if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
- if (errno != 0) {
- ALOGE("Error reading from archive fd: %s", strerror(errno));
- } else {
- ALOGE("Short read of zip file, possibly corrupted zip?");
- }
- length_ = 0;
- return nullptr;
- }
-
- if (bytes < data_.size()) {
- data_.resize(bytes);
- }
- computed_crc32_ = static_cast<uint32_t>(
- crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
- length_ -= bytes;
- offset_ += bytes;
- return &data_;
-}
-
-bool ZipArchiveStreamEntryUncompressed::Verify() {
- return length_ == 0 && crc32_ == computed_crc32_;
-}
-
-class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
- public:
- explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
- : ZipArchiveStreamEntry(handle) {}
- virtual ~ZipArchiveStreamEntryCompressed();
-
- const std::vector<uint8_t>* Read() override;
-
- bool Verify() override;
-
- protected:
- bool Init(const ZipEntry& entry) override;
-
- private:
- bool z_stream_init_ = false;
- z_stream z_stream_;
- std::vector<uint8_t> in_;
- std::vector<uint8_t> out_;
- uint32_t uncompressed_length_ = 0u;
- uint32_t compressed_length_ = 0u;
- uint32_t computed_crc32_ = 0u;
-};
-
-// This method is using libz macros with old-style-casts
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
- return inflateInit2(stream, window_bits);
-}
-#pragma GCC diagnostic pop
-
-bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
- if (!ZipArchiveStreamEntry::Init(entry)) {
- return false;
- }
-
- // Initialize the zlib stream struct.
- memset(&z_stream_, 0, sizeof(z_stream_));
- z_stream_.zalloc = Z_NULL;
- z_stream_.zfree = Z_NULL;
- z_stream_.opaque = Z_NULL;
- z_stream_.next_in = nullptr;
- z_stream_.avail_in = 0;
- z_stream_.avail_out = 0;
- z_stream_.data_type = Z_UNKNOWN;
-
- // Use the undocumented "negative window bits" feature to tell zlib
- // that there's no zlib header waiting for it.
- int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
- } else {
- ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
- }
-
- return false;
- }
-
- z_stream_init_ = true;
-
- uncompressed_length_ = entry.uncompressed_length;
- compressed_length_ = entry.compressed_length;
-
- out_.resize(kBufSize);
- in_.resize(kBufSize);
-
- computed_crc32_ = 0;
-
- return true;
-}
-
-ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
- if (z_stream_init_) {
- inflateEnd(&z_stream_);
- z_stream_init_ = false;
- }
-}
-
-bool ZipArchiveStreamEntryCompressed::Verify() {
- return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
- crc32_ == computed_crc32_;
-}
-
-const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
- // Simple sanity check. The vector should *only* be handled by this code. A caller
- // should not const-cast and modify the capacity. This may invalidate next_out.
- //
- // Note: it would be better to store the results of data() across Read calls.
- CHECK_EQ(out_.capacity(), kBufSize);
-
- if (z_stream_.avail_out == 0) {
- z_stream_.next_out = out_.data();
- z_stream_.avail_out = static_cast<uint32_t>(out_.size());
- ;
- }
-
- while (true) {
- if (z_stream_.avail_in == 0) {
- if (compressed_length_ == 0) {
- return nullptr;
- }
- DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k.
- uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
- : compressed_length_;
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
- errno = 0;
- if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
- if (errno != 0) {
- ALOGE("Error reading from archive fd: %s", strerror(errno));
- } else {
- ALOGE("Short read of zip file, possibly corrupted zip?");
- }
- return nullptr;
- }
-
- compressed_length_ -= bytes;
- offset_ += bytes;
- z_stream_.next_in = in_.data();
- z_stream_.avail_in = bytes;
- }
-
- int zerr = inflate(&z_stream_, Z_NO_FLUSH);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
- z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
- return nullptr;
- }
-
- if (z_stream_.avail_out == 0) {
- uncompressed_length_ -= out_.size();
- computed_crc32_ = static_cast<uint32_t>(
- crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
- return &out_;
- }
- if (zerr == Z_STREAM_END) {
- if (z_stream_.avail_out != 0) {
- // Resize the vector down to the actual size of the data.
- out_.resize(out_.size() - z_stream_.avail_out);
- computed_crc32_ = static_cast<uint32_t>(
- crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
- uncompressed_length_ -= out_.size();
- return &out_;
- }
- return nullptr;
- }
- }
- return nullptr;
-}
-
-class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
- public:
- explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
- : ZipArchiveStreamEntryUncompressed(handle) {}
- virtual ~ZipArchiveStreamEntryRawCompressed() {}
-
- bool Verify() override;
-
- protected:
- bool Init(const ZipEntry& entry) override;
-};
-
-bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
- if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
- return false;
- }
- length_ = entry.compressed_length;
-
- return true;
-}
-
-bool ZipArchiveStreamEntryRawCompressed::Verify() {
- return length_ == 0;
-}
-
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
- const ZipEntry& entry) {
- ZipArchiveStreamEntry* stream = nullptr;
- if (entry.method != kCompressStored) {
- stream = new ZipArchiveStreamEntryCompressed(handle);
- } else {
- stream = new ZipArchiveStreamEntryUncompressed(handle);
- }
- if (stream && !stream->Init(entry)) {
- delete stream;
- stream = nullptr;
- }
-
- return stream;
-}
-
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
- const ZipEntry& entry) {
- ZipArchiveStreamEntry* stream = nullptr;
- if (entry.method == kCompressStored) {
- // Not compressed, don't need to do anything special.
- stream = new ZipArchiveStreamEntryUncompressed(handle);
- } else {
- stream = new ZipArchiveStreamEntryRawCompressed(handle);
- }
- if (stream && !stream->Init(entry)) {
- delete stream;
- stream = nullptr;
- }
- return stream;
-}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
deleted file mode 100644
index f5429be..0000000
--- a/libziparchive/zip_archive_test.cc
+++ /dev/null
@@ -1,1327 +0,0 @@
-/*
- * 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.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string_view>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/mapped_file.h>
-#include <android-base/memory.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-
-#include "zip_archive_common.h"
-#include "zip_archive_private.h"
-
-static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
-
-static const std::string kValidZip = "valid.zip";
-static const std::string kLargeZip = "large.zip";
-static const std::string kBadCrcZip = "bad_crc.zip";
-
-static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
- 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-
-static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K',
- 207, 'H', 132, 210, '\\', '\0'};
-
-static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-
-static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
- const std::string abs_path = test_data_dir + "/" + name;
- return OpenArchive(abs_path.c_str(), handle);
-}
-
-class CdEntryMapTest : public ::testing::Test {
- protected:
- void SetUp() override {
- names_ = {
- "a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt",
- };
- separator_ = "separator";
- header_ = "metadata";
- joined_names_ = header_ + android::base::Join(names_, separator_);
- base_ptr_ = reinterpret_cast<uint8_t*>(&joined_names_[0]);
-
- entry_maps_.emplace_back(CdEntryMapZip32::Create(static_cast<uint16_t>(names_.size())));
- entry_maps_.emplace_back(CdEntryMapZip64::Create());
- for (auto& cd_map : entry_maps_) {
- ASSERT_NE(nullptr, cd_map);
- size_t offset = header_.size();
- for (const auto& name : names_) {
- auto status = cd_map->AddToMap(
- std::string_view{joined_names_.c_str() + offset, name.size()}, base_ptr_);
- ASSERT_EQ(0, status);
- offset += name.size() + separator_.size();
- }
- }
- }
-
- std::vector<std::string> names_;
- // A continuous region of memory serves as a mock of the central directory.
- std::string joined_names_;
- // We expect some metadata at the beginning of the central directory and between filenames.
- std::string header_;
- std::string separator_;
-
- std::vector<std::unique_ptr<CdEntryMapInterface>> entry_maps_;
- uint8_t* base_ptr_{nullptr}; // Points to the start of the central directory.
-};
-
-TEST_F(CdEntryMapTest, AddDuplicatedEntry) {
- for (auto& cd_map : entry_maps_) {
- std::string_view name = "b.txt";
- ASSERT_NE(0, cd_map->AddToMap(name, base_ptr_));
- }
-}
-
-TEST_F(CdEntryMapTest, FindEntry) {
- for (auto& cd_map : entry_maps_) {
- uint64_t expected_offset = header_.size();
- for (const auto& name : names_) {
- auto [status, offset] = cd_map->GetCdEntryOffset(name, base_ptr_);
- ASSERT_EQ(status, kSuccess);
- ASSERT_EQ(offset, expected_offset);
- expected_offset += name.size() + separator_.size();
- }
- }
-}
-
-TEST_F(CdEntryMapTest, Iteration) {
- std::set<std::string_view> expected(names_.begin(), names_.end());
- for (auto& cd_map : entry_maps_) {
- cd_map->ResetIteration();
- std::set<std::string_view> entry_set;
- auto ret = cd_map->Next(base_ptr_);
- while (ret != std::pair<std::string_view, uint64_t>{}) {
- auto [it, insert_status] = entry_set.insert(ret.first);
- ASSERT_TRUE(insert_status);
- ret = cd_map->Next(base_ptr_);
- }
- ASSERT_EQ(expected, entry_set);
- }
-}
-
-TEST(ziparchive, Open) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
- CloseArchive(handle);
-
- ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, OutOfBound) {
- ZipArchiveHandle handle;
- ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, EmptyArchive) {
- ZipArchiveHandle handle;
- ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, ZeroSizeCentralDirectory) {
- ZipArchiveHandle handle;
- ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, OpenMissing) {
- ZipArchiveHandle handle;
- ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
-
- // Confirm the file descriptor is not going to be mistaken for a valid one.
- ASSERT_EQ(-1, GetFileDescriptor(handle));
-}
-
-TEST(ziparchive, OpenAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
- ASSERT_NE(-1, fd);
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
- CloseArchive(handle);
- ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
- ASSERT_EQ(EBADF, errno);
-}
-
-TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
- ASSERT_NE(-1, fd);
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
- CloseArchive(handle);
- ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
- close(fd);
-}
-
-TEST(ziparchive, OpenAssumeFdRangeOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
- ASSERT_NE(-1, fd);
- const off64_t length = lseek64(fd, 0, SEEK_END);
- ASSERT_NE(-1, length);
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
- static_cast<size_t>(length), 0));
- CloseArchive(handle);
- ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
- ASSERT_EQ(EBADF, errno);
-}
-
-TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
- ASSERT_NE(-1, fd);
- const off64_t length = lseek(fd, 0, SEEK_END);
- ASSERT_NE(-1, length);
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
- static_cast<size_t>(length), 0, false));
- CloseArchive(handle);
- ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
- close(fd);
-}
-
-TEST(ziparchive, Iteration_std_string_view) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
-
- ZipEntry64 data;
- std::vector<std::string_view> names;
- std::string_view name;
- while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
-
- // Assert that the names are as expected.
- std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
- std::sort(names.begin(), names.end());
- ASSERT_EQ(expected_names, names);
-
- CloseArchive(handle);
-}
-
-static void AssertIterationNames(void* iteration_cookie,
- const std::vector<std::string>& expected_names_sorted) {
- ZipEntry64 data;
- std::vector<std::string> names;
- std::string_view name;
- for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
- ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
- names.push_back(std::string(name));
- }
- // End of iteration.
- ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
- // Assert that the names are as expected.
- std::sort(names.begin(), names.end());
- ASSERT_EQ(expected_names_sorted, names);
-}
-
-static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
- const std::vector<std::string>& expected_names_sorted) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
- AssertIterationNames(iteration_cookie, expected_names_sorted);
- CloseArchive(handle);
-}
-
-static void AssertIterationOrderWithMatcher(std::function<bool(std::string_view)> matcher,
- const std::vector<std::string>& expected_names_sorted) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, matcher));
- AssertIterationNames(iteration_cookie, expected_names_sorted);
- CloseArchive(handle);
-}
-
-TEST(ziparchive, Iteration) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
- "b/d.txt"};
-
- AssertIterationOrder("", "", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithPrefix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
-
- AssertIterationOrder("b/", "", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithSuffix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
- "b/d.txt"};
-
- AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithPrefixAndSuffix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
-
- AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithAdditionalMatchesExactly) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt"};
- auto matcher = [](std::string_view name) { return name == "a.txt"; };
- AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithAdditionalMatchesWithSuffix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
- "b/d.txt"};
- auto matcher = [](std::string_view name) {
- return name == "a.txt" || android::base::EndsWith(name, ".txt");
- };
- AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithAdditionalMatchesWithPrefixAndSuffix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b/c.txt", "b/d.txt"};
- auto matcher = [](std::string_view name) {
- return name == "a.txt" ||
- (android::base::EndsWith(name, ".txt") && android::base::StartsWith(name, "b/"));
- };
- AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
-
- ZipEntry64 data;
- std::string_view name;
-
- // End of iteration.
- ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- ZipEntry64 data;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
-
- // Known facts about a.txt, from zipinfo -v.
- ASSERT_EQ(63, data.offset);
- ASSERT_EQ(kCompressDeflated, data.method);
- ASSERT_EQ(17u, data.uncompressed_length);
- ASSERT_EQ(13u, data.compressed_length);
- ASSERT_EQ(0x950821c5, data.crc32);
- ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
-
- // An entry that doesn't exist. Should be a negative return code.
- ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry_empty) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- ZipEntry64 data;
- ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry_too_long) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- std::string very_long_name(65536, 'x');
- ZipEntry64 data;
- ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, TestInvalidDeclaredLength) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
-
- std::string_view name;
- ZipEntry64 data;
-
- ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
- ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, OpenArchiveFdRange) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
-
- const std::string leading_garbage(21, 'x');
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
- leading_garbage.size()));
-
- std::string valid_content;
- ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
-
- const std::string ending_garbage(42, 'x');
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
- ending_garbage.size()));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
- ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
- valid_content.size(),
- static_cast<off64_t>(leading_garbage.size())));
-
- // An entry that's deflated.
- ZipEntry64 data;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
- const auto a_size = static_cast<size_t>(data.uncompressed_length);
- ASSERT_EQ(a_size, kATxtContents.size());
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
- ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
- ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
-
- // An entry that's stored.
- ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
- const auto b_size = static_cast<size_t>(data.uncompressed_length);
- ASSERT_EQ(b_size, kBTxtContents.size());
- buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
- ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
- ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, ExtractToMemory) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- // An entry that's deflated.
- ZipEntry64 data;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
- const auto a_size = static_cast<size_t>(data.uncompressed_length);
- ASSERT_EQ(a_size, kATxtContents.size());
- uint8_t* buffer = new uint8_t[a_size];
- ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
- ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
- delete[] buffer;
-
- // An entry that's stored.
- ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
- const auto b_size = static_cast<size_t>(data.uncompressed_length);
- ASSERT_EQ(b_size, kBTxtContents.size());
- buffer = new uint8_t[b_size];
- ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
- ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
- delete[] buffer;
-
- CloseArchive(handle);
-}
-
-static const uint32_t kEmptyEntriesZip[] = {
- 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
- 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
- 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
- 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
- 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
- 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
-
-// This is a zip file containing a single entry (ab.txt) that contains
-// 90072 repetitions of the string "ab\n" and has an uncompressed length
-// of 270216 bytes.
-static const uint16_t kAbZip[] = {
- 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
- 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
- 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
- 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
- 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
- 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
- 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
- 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
-
-static const std::string kAbTxtName("ab.txt");
-static const size_t kAbUncompressedSize = 270216;
-
-TEST(ziparchive, EmptyEntries) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
-
- ZipEntry64 entry;
- ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
- ASSERT_EQ(0u, entry.uncompressed_length);
- // Extraction to a 1 byte buffer should succeed.
- uint8_t buffer[1];
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
- // Extraction to an empty buffer should succeed.
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, nullptr, 0));
-
- TemporaryFile tmp_output_file;
- ASSERT_NE(-1, tmp_output_file.fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
-
- struct stat stat_buf;
- ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
- ASSERT_EQ(0, stat_buf.st_size);
-}
-
-TEST(ziparchive, EntryLargerThan32K) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
- sizeof(kAbZip) - 1));
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
-
- ZipEntry64 entry;
- ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
- ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
-
- // Extract the entry to memory.
- std::vector<uint8_t> buffer(kAbUncompressedSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
-
- // Extract the entry to a file.
- TemporaryFile tmp_output_file;
- ASSERT_NE(-1, tmp_output_file.fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
-
- // Make sure the extracted file size is as expected.
- struct stat stat_buf;
- ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
- ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
-
- // 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, 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);
-
- for (int i = 0; i < 90072; ++i) {
- const uint8_t* line = &file_contents[0] + (3 * i);
- ASSERT_EQ('a', line[0]);
- ASSERT_EQ('b', line[1]);
- ASSERT_EQ('\n', line[2]);
- }
-}
-
-TEST(ziparchive, TrailerAfterEOCD) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
-
- // Create a file with 8 bytes of random garbage.
- static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
-
- ZipArchiveHandle handle;
- ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
-}
-
-TEST(ziparchive, ExtractToFile) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
- const size_t data_size = sizeof(data);
-
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- ZipEntry64 entry;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
-
- // Assert that the first 8 bytes of the file haven't been clobbered.
- uint8_t read_buffer[data_size];
- 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));
-
- // Assert that the remainder of the file contains the incompressed data.
- std::vector<uint8_t> uncompressed_data(static_cast<size_t>(entry.uncompressed_length));
- ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
- static_cast<size_t>(entry.uncompressed_length)));
- ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
-
- // Assert that the total length of the file is sane
- ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
- lseek(tmp_file.fd, 0, SEEK_END));
-}
-
-#if !defined(_WIN32)
-TEST(ziparchive, OpenFromMemory) {
- const std::string zip_path = test_data_dir + "/dummy-update.zip";
- android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
- ASSERT_NE(-1, fd);
- struct stat sb;
- ASSERT_EQ(0, fstat(fd, &sb));
-
- // Memory map the file first and open the archive from the memory region.
- auto file_map{
- android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
- ZipArchiveHandle handle;
- ASSERT_EQ(0,
- OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
-
- // Assert one entry can be found and extracted correctly.
- ZipEntry64 binary_entry;
- ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
- TemporaryFile tmp_binary;
- ASSERT_NE(-1, tmp_binary.fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
-}
-#endif
-
-static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
- bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
- ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
- std::unique_ptr<ZipArchiveStreamEntry> stream;
- if (raw) {
- stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
- if (entry->method == kCompressStored) {
- read_data->resize(static_cast<size_t>(entry->uncompressed_length));
- } else {
- read_data->resize(static_cast<size_t>(entry->compressed_length));
- }
- } else {
- stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
- read_data->resize(static_cast<size_t>(entry->uncompressed_length));
- }
- uint8_t* read_data_ptr = read_data->data();
- ASSERT_TRUE(stream.get() != nullptr);
- const std::vector<uint8_t>* data;
- uint64_t total_size = 0;
- while ((data = stream->Read()) != nullptr) {
- total_size += data->size();
- memcpy(read_data_ptr, data->data(), data->size());
- read_data_ptr += data->size();
- }
- ASSERT_EQ(verified, stream->Verify());
- ASSERT_EQ(total_size, read_data->size());
-}
-
-static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
- const std::string& entry_name,
- const std::vector<uint8_t>& contents, bool raw) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
-
- ASSERT_EQ(contents.size(), read_data.size());
- ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
-
- CloseArchive(handle);
-}
-
-static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
- const std::string& entry_name) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
-
- std::vector<uint8_t> cmp_data(static_cast<size_t>(entry.uncompressed_length));
- ASSERT_EQ(entry.uncompressed_length, read_data.size());
- ASSERT_EQ(
- 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
- ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, StreamCompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
-}
-
-TEST(ziparchive, StreamUncompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
-}
-
-TEST(ziparchive, StreamRawCompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
-}
-
-TEST(ziparchive, StreamRawUncompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
-}
-
-TEST(ziparchive, StreamLargeCompressed) {
- ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
-}
-
-TEST(ziparchive, StreamLargeUncompressed) {
- ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
-}
-
-TEST(ziparchive, StreamCompressedBadCrc) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, StreamUncompressedBadCrc) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
-
- CloseArchive(handle);
-}
-
-// Generated using the following Java program:
-// public static void main(String[] foo) throws Exception {
-// FileOutputStream fos = new
-// FileOutputStream("/tmp/data_descriptor.zip");
-// ZipOutputStream zos = new ZipOutputStream(fos);
-// ZipEntry64 ze = new ZipEntry64("name");
-// ze.setMethod(ZipEntry64.DEFLATED);
-// zos.putNextEntry(ze);
-// zos.write("abdcdefghijk".getBytes());
-// zos.closeEntry();
-// zos.close();
-// }
-//
-// cat /tmp/data_descriptor.zip | xxd -i
-//
-static const std::vector<uint8_t> kDataDescriptorZipFile{
- 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
- 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
- //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
- 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
- 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
- 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
- 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
- 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-// The offsets of the data descriptor in this file, so we can mess with
-// them later in the test.
-static constexpr uint32_t kDataDescriptorOffset = 48;
-static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
-static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
-
-static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
- std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
-
- // This function expects a variant of kDataDescriptorZipFile, for look for
- // an entry whose name is "name" and whose size is 12 (contents =
- // "abdcdefghijk").
- ZipEntry64 entry;
- ASSERT_EQ(0, FindEntry(handle, "name", &entry));
- ASSERT_EQ(12u, entry.uncompressed_length);
-
- entry_out->resize(12);
- (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, ValidDataDescriptors) {
- std::vector<uint8_t> entry;
- int32_t error_code = 0;
- ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
-
- ASSERT_EQ(0, error_code);
- ASSERT_EQ(12u, entry.size());
- ASSERT_EQ('a', entry[0]);
- ASSERT_EQ('k', entry[11]);
-}
-
-TEST(ziparchive, InvalidDataDescriptors_csize) {
- std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
- invalid_csize[kCSizeOffset] = 0xfe;
-
- std::vector<uint8_t> entry;
- int32_t error_code = 0;
- ExtractEntryToMemory(invalid_csize, &entry, &error_code);
-
- ASSERT_EQ(kInconsistentInformation, error_code);
-}
-
-TEST(ziparchive, InvalidDataDescriptors_size) {
- std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
- invalid_size[kSizeOffset] = 0xfe;
-
- std::vector<uint8_t> entry;
- int32_t error_code = 0;
- ExtractEntryToMemory(invalid_size, &entry, &error_code);
-
- ASSERT_EQ(kInconsistentInformation, error_code);
-}
-
-TEST(ziparchive, ErrorCodeString) {
- ASSERT_STREQ("Success", ErrorCodeString(0));
-
- // Out of bounds.
- ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
- ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
- ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
-
- ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
-}
-
-// A zip file whose local file header at offset zero is corrupted.
-//
-// ---------------
-// cat foo > a.txt
-// zip a.zip a.txt
-// cat a.zip | xxd -i
-//
-// Manual changes :
-// [2] = 0xff // Corrupt the LFH signature of entry 0.
-// [3] = 0xff // Corrupt the LFH signature of entry 0.
-static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
- //[lfh-sig-----------], [lfh contents---------------------------------
- 0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
- //--------------------------------------------------------------------
- 0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
- //-------------------------------] [file-name-----------------], [---
- 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
- // entry-contents------------------------------------------------------
- 0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
- //--------------------------------------------------------------------
- 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
- //-------------------------------------], [cd-record-sig-------], [---
- 0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
- // cd-record-----------------------------------------------------------
- 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
- //--------------------------------------------------------------------
- 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
- //--------------------------------------------------------------------
- 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
- //-] [lfh-file-header-off-], [file-name-----------------], [extra----
- 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
- //--------------------------------------------------------------------
- 0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
- //-------------------------------------------------------], [eocd-sig-
- 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
- //-------], [---------------------------------------------------------
- 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
- //-------------------------------------------]
- 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-TEST(ziparchive, BrokenLfhSignature) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
- kZipFileWithBrokenLfhSignature.size()));
- ZipArchiveHandle handle;
- ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
-}
-
-class VectorReader : public zip_archive::Reader {
- public:
- VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
-
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
- if ((offset + len) < input_.size()) {
- return false;
- }
-
- memcpy(buf, &input_[static_cast<size_t>(offset)], len);
- return true;
- }
-
- private:
- const std::vector<uint8_t>& input_;
-};
-
-class VectorWriter : public zip_archive::Writer {
- public:
- VectorWriter() : Writer() {}
-
- bool Append(uint8_t* buf, size_t size) {
- output_.insert(output_.end(), buf, buf + size);
- return true;
- }
-
- std::vector<uint8_t>& GetOutput() { return output_; }
-
- private:
- std::vector<uint8_t> output_;
-};
-
-class BadReader : public zip_archive::Reader {
- public:
- BadReader() : Reader() {}
-
- bool ReadAtOffset(uint8_t*, size_t, off64_t) const { return false; }
-};
-
-class BadWriter : public zip_archive::Writer {
- public:
- BadWriter() : Writer() {}
-
- bool Append(uint8_t*, size_t) { return false; }
-};
-
-TEST(ziparchive, Inflate) {
- const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
- const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
-
- const VectorReader reader(kATxtContentsCompressed);
- {
- VectorWriter writer;
- uint64_t crc_out = 0;
-
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(kATxtContents, writer.GetOutput());
- ASSERT_EQ(0x950821C5u, crc_out);
- }
-
- {
- VectorWriter writer;
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(kATxtContents, writer.GetOutput());
- }
-
- {
- BadWriter writer;
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
- ASSERT_EQ(kIoError, ret);
- }
-
- {
- BadReader reader;
- VectorWriter writer;
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
- ASSERT_EQ(kIoError, ret);
- ASSERT_EQ(0u, writer.GetOutput().size());
- }
-}
-
-// The class constructs a zipfile with zip64 format, and test the parsing logic.
-class Zip64ParseTest : public ::testing::Test {
- protected:
- struct LocalFileEntry {
- std::vector<uint8_t> local_file_header;
- std::string file_name;
- std::vector<uint8_t> extended_field;
- // Fake data to mimic the compressed bytes in the zipfile.
- std::vector<uint8_t> compressed_bytes;
- std::vector<uint8_t> data_descriptor;
-
- size_t GetSize() const {
- return local_file_header.size() + file_name.size() + extended_field.size() +
- compressed_bytes.size() + data_descriptor.size();
- }
-
- void CopyToOutput(std::vector<uint8_t>* output) const {
- std::copy(local_file_header.begin(), local_file_header.end(), std::back_inserter(*output));
- std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
- std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
- std::copy(compressed_bytes.begin(), compressed_bytes.end(), std::back_inserter(*output));
- std::copy(data_descriptor.begin(), data_descriptor.end(), std::back_inserter(*output));
- }
- };
-
- struct CdRecordEntry {
- std::vector<uint8_t> central_directory_record;
- std::string file_name;
- std::vector<uint8_t> extended_field;
-
- size_t GetSize() const {
- return central_directory_record.size() + file_name.size() + extended_field.size();
- }
-
- void CopyToOutput(std::vector<uint8_t>* output) const {
- std::copy(central_directory_record.begin(), central_directory_record.end(),
- std::back_inserter(*output));
- std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
- std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
- }
- };
-
- static void ConstructLocalFileHeader(const std::string& name, std::vector<uint8_t>* output,
- uint32_t uncompressed_size, uint32_t compressed_size) {
- LocalFileHeader lfh = {};
- lfh.lfh_signature = LocalFileHeader::kSignature;
- lfh.compressed_size = compressed_size;
- lfh.uncompressed_size = uncompressed_size;
- lfh.file_name_length = static_cast<uint16_t>(name.size());
- lfh.extra_field_length = 20;
- *output = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&lfh),
- reinterpret_cast<uint8_t*>(&lfh) + sizeof(LocalFileHeader));
- }
-
- // Put one zip64 extended info in the extended field.
- static void ConstructExtendedField(const std::vector<uint64_t>& zip64_fields,
- std::vector<uint8_t>* output) {
- ASSERT_FALSE(zip64_fields.empty());
- uint16_t data_size = 8 * static_cast<uint16_t>(zip64_fields.size());
- std::vector<uint8_t> extended_field(data_size + 4);
- android::base::put_unaligned(extended_field.data(), Zip64ExtendedInfo::kHeaderId);
- android::base::put_unaligned(extended_field.data() + 2, data_size);
- size_t offset = 4;
- for (const auto& field : zip64_fields) {
- android::base::put_unaligned(extended_field.data() + offset, field);
- offset += 8;
- }
-
- *output = std::move(extended_field);
- }
-
- static void ConstructCentralDirectoryRecord(const std::string& name, uint32_t uncompressed_size,
- uint32_t compressed_size, uint32_t local_offset,
- std::vector<uint8_t>* output) {
- CentralDirectoryRecord cdr = {};
- cdr.record_signature = CentralDirectoryRecord::kSignature;
- cdr.compressed_size = uncompressed_size;
- cdr.uncompressed_size = compressed_size;
- cdr.file_name_length = static_cast<uint16_t>(name.size());
- cdr.extra_field_length = local_offset == UINT32_MAX ? 28 : 20;
- cdr.local_file_header_offset = local_offset;
- *output =
- std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&cdr),
- reinterpret_cast<uint8_t*>(&cdr) + sizeof(CentralDirectoryRecord));
- }
-
- // Add an entry to the zipfile, construct the corresponding local header and cd entry.
- void AddEntry(const std::string& name, const std::vector<uint8_t>& content,
- bool uncompressed_size_in_extended, bool compressed_size_in_extended,
- bool local_offset_in_extended, bool include_data_descriptor = false) {
- auto uncompressed_size = static_cast<uint32_t>(content.size());
- auto compressed_size = static_cast<uint32_t>(content.size());
- uint32_t local_file_header_offset = 0;
- std::for_each(file_entries_.begin(), file_entries_.end(),
- [&local_file_header_offset](const LocalFileEntry& file_entry) {
- local_file_header_offset += file_entry.GetSize();
- });
-
- std::vector<uint64_t> zip64_fields;
- if (uncompressed_size_in_extended) {
- zip64_fields.push_back(uncompressed_size);
- uncompressed_size = UINT32_MAX;
- }
- if (compressed_size_in_extended) {
- zip64_fields.push_back(compressed_size);
- compressed_size = UINT32_MAX;
- }
- LocalFileEntry local_entry = {
- .local_file_header = {},
- .file_name = name,
- .extended_field = {},
- .compressed_bytes = content,
- };
- ConstructLocalFileHeader(name, &local_entry.local_file_header, uncompressed_size,
- compressed_size);
- ConstructExtendedField(zip64_fields, &local_entry.extended_field);
- if (include_data_descriptor) {
- size_t descriptor_size = compressed_size_in_extended ? 24 : 16;
- local_entry.data_descriptor.resize(descriptor_size);
- uint8_t* write_ptr = local_entry.data_descriptor.data();
- EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
- EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
- if (compressed_size_in_extended) {
- EmitUnaligned<uint64_t>(&write_ptr, compressed_size_in_extended);
- EmitUnaligned<uint64_t>(&write_ptr, uncompressed_size_in_extended);
- } else {
- EmitUnaligned<uint32_t>(&write_ptr, compressed_size_in_extended);
- EmitUnaligned<uint32_t>(&write_ptr, uncompressed_size_in_extended);
- }
- }
-
- file_entries_.push_back(std::move(local_entry));
-
- if (local_offset_in_extended) {
- zip64_fields.push_back(local_file_header_offset);
- local_file_header_offset = UINT32_MAX;
- }
- CdRecordEntry cd_entry = {
- .central_directory_record = {},
- .file_name = name,
- .extended_field = {},
- };
- ConstructCentralDirectoryRecord(name, uncompressed_size, compressed_size,
- local_file_header_offset, &cd_entry.central_directory_record);
- ConstructExtendedField(zip64_fields, &cd_entry.extended_field);
- cd_entries_.push_back(std::move(cd_entry));
- }
-
- void ConstructEocd() {
- ASSERT_EQ(file_entries_.size(), cd_entries_.size());
- Zip64EocdRecord zip64_eocd = {};
- zip64_eocd.record_signature = Zip64EocdRecord::kSignature;
- zip64_eocd.num_records = file_entries_.size();
- zip64_eocd.cd_size = 0;
- std::for_each(
- cd_entries_.begin(), cd_entries_.end(),
- [&zip64_eocd](const CdRecordEntry& cd_entry) { zip64_eocd.cd_size += cd_entry.GetSize(); });
- zip64_eocd.cd_start_offset = 0;
- std::for_each(file_entries_.begin(), file_entries_.end(),
- [&zip64_eocd](const LocalFileEntry& file_entry) {
- zip64_eocd.cd_start_offset += file_entry.GetSize();
- });
- zip64_eocd_record_ =
- std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_eocd),
- reinterpret_cast<uint8_t*>(&zip64_eocd) + sizeof(Zip64EocdRecord));
-
- Zip64EocdLocator zip64_locator = {};
- zip64_locator.locator_signature = Zip64EocdLocator::kSignature;
- zip64_locator.zip64_eocd_offset = zip64_eocd.cd_start_offset + zip64_eocd.cd_size;
- zip64_eocd_locator_ =
- std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_locator),
- reinterpret_cast<uint8_t*>(&zip64_locator) + sizeof(Zip64EocdLocator));
-
- EocdRecord eocd = {};
- eocd.eocd_signature = EocdRecord::kSignature,
- eocd.num_records = file_entries_.size() > UINT16_MAX
- ? UINT16_MAX
- : static_cast<uint16_t>(file_entries_.size());
- eocd.cd_size = UINT32_MAX;
- eocd.cd_start_offset = UINT32_MAX;
- eocd_record_ = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&eocd),
- reinterpret_cast<uint8_t*>(&eocd) + sizeof(EocdRecord));
- }
-
- // Concatenate all the local file entries, cd entries, and eocd metadata.
- void ConstructZipFile() {
- for (const auto& file_entry : file_entries_) {
- file_entry.CopyToOutput(&zip_content_);
- }
- for (const auto& cd_entry : cd_entries_) {
- cd_entry.CopyToOutput(&zip_content_);
- }
- std::copy(zip64_eocd_record_.begin(), zip64_eocd_record_.end(),
- std::back_inserter(zip_content_));
- std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
- std::back_inserter(zip_content_));
- std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
- }
-
- std::vector<uint8_t> zip_content_;
-
- std::vector<LocalFileEntry> file_entries_;
- std::vector<CdRecordEntry> cd_entries_;
- std::vector<uint8_t> zip64_eocd_record_;
- std::vector<uint8_t> zip64_eocd_locator_;
- std::vector<uint8_t> eocd_record_;
-};
-
-TEST_F(Zip64ParseTest, openFile) {
- AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- ASSERT_EQ(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, openFilelocalOffsetInExtendedField) {
- AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, true);
- AddEntry("b.txt", std::vector<uint8_t>(200, 'b'), true, true, true);
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- ASSERT_EQ(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, openFileCompressedNotInExtendedField) {
- AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, false, false);
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- // Zip64 extended fields must include both uncompressed and compressed size.
- ASSERT_NE(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, findEntry) {
- AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
- AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, false);
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- ASSERT_EQ(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry64 entry;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
- ASSERT_EQ(200, entry.uncompressed_length);
- ASSERT_EQ(200, entry.compressed_length);
-
- ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
- ASSERT_EQ(300, entry.uncompressed_length);
- ASSERT_EQ(300, entry.compressed_length);
- CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, openFileIncorrectDataSizeInLocalExtendedField) {
- AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
- ASSERT_EQ(1, file_entries_.size());
- auto& extended_field = file_entries_[0].extended_field;
- // data size exceeds the extended field size in local header.
- android::base::put_unaligned<uint16_t>(extended_field.data() + 2, 30);
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- ASSERT_EQ(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry64 entry;
- ASSERT_NE(0, FindEntry(handle, "a.txt", &entry));
-
- CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, iterates) {
- std::set<std::string_view> names{"a.txt", "b.txt", "c.txt", "d.txt", "e.txt"};
- for (const auto& name : names) {
- AddEntry(std::string(name), std::vector<uint8_t>(100, name[0]), true, true, true);
- }
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- ASSERT_EQ(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
- std::set<std::string_view> result;
- std::string_view name;
- ZipEntry64 entry;
- while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name);
- ASSERT_EQ(names, result);
-
- CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, zip64EocdWrongLocatorOffset) {
- AddEntry("a.txt", std::vector<uint8_t>(1, 'a'), true, true, true);
- ConstructEocd();
- zip_content_.resize(20, 'a');
- std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
- std::back_inserter(zip_content_));
- std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
-
- ZipArchiveHandle handle;
- ASSERT_NE(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- CloseArchive(handle);
-}
-
-TEST_F(Zip64ParseTest, extract) {
- std::vector<uint8_t> content(200, 'a');
- AddEntry("a.txt", content, true, true, true);
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- ASSERT_EQ(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry64 entry;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
-
- VectorWriter writer;
- ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
- ASSERT_EQ(content, writer.GetOutput());
-}
-
-TEST_F(Zip64ParseTest, extractWithDataDescriptor) {
- std::vector<uint8_t> content(300, 'b');
- AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
- AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
- ConstructEocd();
- ConstructZipFile();
-
- ZipArchiveHandle handle;
- ASSERT_EQ(
- 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry64 entry;
- ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
-
- VectorWriter writer;
- ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
- ASSERT_EQ(content, writer.GetOutput());
-}
diff --git a/libziparchive/zip_cd_entry_map.cc b/libziparchive/zip_cd_entry_map.cc
deleted file mode 100644
index f187c06..0000000
--- a/libziparchive/zip_cd_entry_map.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2020 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 "zip_cd_entry_map.h"
-
-#include <android-base/logging.h>
-#include <log/log.h>
-
-/*
- * Round up to the next highest power of 2.
- *
- * Found on http://graphics.stanford.edu/~seander/bithacks.html.
- */
-static uint32_t RoundUpPower2(uint32_t val) {
- val--;
- val |= val >> 1;
- val |= val >> 2;
- val |= val >> 4;
- val |= val >> 8;
- val |= val >> 16;
- val++;
-
- return val;
-}
-
-static uint32_t ComputeHash(std::string_view name) {
- return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
-}
-
-// Convert a ZipEntry to a hash table index, verifying that it's in a valid range.
-std::pair<ZipError, uint64_t> CdEntryMapZip32::GetCdEntryOffset(std::string_view name,
- const uint8_t* start) const {
- const uint32_t hash = ComputeHash(name);
-
- // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
- uint32_t ent = hash & (hash_table_size_ - 1);
- while (hash_table_[ent].name_offset != 0) {
- if (hash_table_[ent].ToStringView(start) == name) {
- return {kSuccess, hash_table_[ent].name_offset};
- }
- ent = (ent + 1) & (hash_table_size_ - 1);
- }
-
- ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
- return {kEntryNotFound, 0};
-}
-
-ZipError CdEntryMapZip32::AddToMap(std::string_view name, const uint8_t* start) {
- const uint64_t hash = ComputeHash(name);
- uint32_t ent = hash & (hash_table_size_ - 1);
-
- /*
- * We over-allocated the table, so we're guaranteed to find an empty slot.
- * Further, we guarantee that the hashtable size is not 0.
- */
- while (hash_table_[ent].name_offset != 0) {
- if (hash_table_[ent].ToStringView(start) == name) {
- // We've found a duplicate entry. We don't accept duplicates.
- ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
- return kDuplicateEntry;
- }
- ent = (ent + 1) & (hash_table_size_ - 1);
- }
-
- // `name` has already been validated before entry.
- const char* start_char = reinterpret_cast<const char*>(start);
- hash_table_[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
- hash_table_[ent].name_length = static_cast<uint16_t>(name.size());
- return kSuccess;
-}
-
-void CdEntryMapZip32::ResetIteration() {
- current_position_ = 0;
-}
-
-std::pair<std::string_view, uint64_t> CdEntryMapZip32::Next(const uint8_t* cd_start) {
- while (current_position_ < hash_table_size_) {
- const auto& entry = hash_table_[current_position_];
- current_position_ += 1;
-
- if (entry.name_offset != 0) {
- return {entry.ToStringView(cd_start), entry.name_offset};
- }
- }
- // We have reached the end of the hash table.
- return {};
-}
-
-CdEntryMapZip32::CdEntryMapZip32(uint16_t num_entries) {
- /*
- * Create hash table. We have a minimum 75% load factor, possibly as
- * low as 50% after we round off to a power of 2. There must be at
- * least one unused entry to avoid an infinite loop during creation.
- */
- hash_table_size_ = RoundUpPower2(1 + (num_entries * 4) / 3);
- hash_table_ = {
- reinterpret_cast<ZipStringOffset*>(calloc(hash_table_size_, sizeof(ZipStringOffset))), free};
-}
-
-std::unique_ptr<CdEntryMapInterface> CdEntryMapZip32::Create(uint16_t num_entries) {
- auto entry_map = new CdEntryMapZip32(num_entries);
- CHECK(entry_map->hash_table_ != nullptr)
- << "Zip: unable to allocate the " << entry_map->hash_table_size_
- << " entry hash_table, entry size: " << sizeof(ZipStringOffset);
- return std::unique_ptr<CdEntryMapInterface>(entry_map);
-}
-
-std::unique_ptr<CdEntryMapInterface> CdEntryMapZip64::Create() {
- return std::unique_ptr<CdEntryMapInterface>(new CdEntryMapZip64());
-}
-
-ZipError CdEntryMapZip64::AddToMap(std::string_view name, const uint8_t* start) {
- const auto [it, added] =
- entry_table_.insert({name, name.data() - reinterpret_cast<const char*>(start)});
- if (!added) {
- ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
- return kDuplicateEntry;
- }
- return kSuccess;
-}
-
-std::pair<ZipError, uint64_t> CdEntryMapZip64::GetCdEntryOffset(std::string_view name,
- const uint8_t* /*cd_start*/) const {
- const auto it = entry_table_.find(name);
- if (it == entry_table_.end()) {
- ALOGV("Zip: Could not find entry %.*s", static_cast<int>(name.size()), name.data());
- return {kEntryNotFound, 0};
- }
-
- return {kSuccess, it->second};
-}
-
-void CdEntryMapZip64::ResetIteration() {
- iterator_ = entry_table_.begin();
-}
-
-std::pair<std::string_view, uint64_t> CdEntryMapZip64::Next(const uint8_t* /*cd_start*/) {
- if (iterator_ == entry_table_.end()) {
- return {};
- }
-
- return *iterator_++;
-}
diff --git a/libziparchive/zip_cd_entry_map.h b/libziparchive/zip_cd_entry_map.h
deleted file mode 100644
index 4957f75..0000000
--- a/libziparchive/zip_cd_entry_map.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2020 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 <map>
-#include <memory>
-#include <string_view>
-#include <utility>
-
-#include "zip_error.h"
-
-// This class is the interface of the central directory entries map. The map
-// helps to locate a particular cd entry based on the filename.
-class CdEntryMapInterface {
- public:
- virtual ~CdEntryMapInterface() = default;
- // Adds an entry to the map. The |name| should internally points to the
- // filename field of a cd entry. And |start| points to the beginning of the
- // central directory. Returns 0 on success.
- virtual ZipError AddToMap(std::string_view name, const uint8_t* start) = 0;
- // For the zip entry |entryName|, finds the offset of its filename field in
- // the central directory. Returns a pair of [status, offset]. The value of
- // the status is 0 on success.
- virtual std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
- const uint8_t* cd_start) const = 0;
- // Resets the iterator to the beginning of the map.
- virtual void ResetIteration() = 0;
- // Returns the [name, cd offset] of the current element. Also increments the
- // iterator to points to the next element. Returns an empty pair we have read
- // past boundary.
- virtual std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) = 0;
-};
-
-/**
- * More space efficient string representation of strings in an mmaped zipped
- * file than std::string_view. Using std::string_view as an entry in the
- * ZipArchive hash table wastes space. std::string_view stores a pointer to a
- * string (on 64 bit, 8 bytes) and the length to read from that pointer,
- * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
- * 6 bytes.
- *
- * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
- * mapped file instead of the entire address, consuming 8 bytes with alignment.
- */
-struct ZipStringOffset {
- uint32_t name_offset;
- uint16_t name_length;
-
- const std::string_view ToStringView(const uint8_t* start) const {
- return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
- }
-};
-
-// This implementation of CdEntryMap uses an array hash table. It uses less
-// memory than std::map; and it's used as the default implementation for zip
-// archives without zip64 extension.
-class CdEntryMapZip32 : public CdEntryMapInterface {
- public:
- static std::unique_ptr<CdEntryMapInterface> Create(uint16_t num_entries);
-
- ZipError AddToMap(std::string_view name, const uint8_t* start) override;
- std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
- const uint8_t* cd_start) const override;
- void ResetIteration() override;
- std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) override;
-
- private:
- explicit CdEntryMapZip32(uint16_t num_entries);
-
- // We know how many entries are in the Zip archive, so we can have a
- // fixed-size hash table. We define a load factor of 0.75 and over
- // allocate so the maximum number entries can never be higher than
- // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
- uint32_t hash_table_size_{0};
- std::unique_ptr<ZipStringOffset[], decltype(&free)> hash_table_{nullptr, free};
-
- // The position of element for the current iteration.
- uint32_t current_position_{0};
-};
-
-// This implementation of CdEntryMap uses a std::map
-class CdEntryMapZip64 : public CdEntryMapInterface {
- public:
- static std::unique_ptr<CdEntryMapInterface> Create();
-
- ZipError AddToMap(std::string_view name, const uint8_t* start) override;
- std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
- const uint8_t* cd_start) const override;
- void ResetIteration() override;
- std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) override;
-
- private:
- CdEntryMapZip64() = default;
-
- std::map<std::string_view, uint64_t> entry_table_;
-
- std::map<std::string_view, uint64_t>::iterator iterator_;
-};
diff --git a/libziparchive/zip_error.cpp b/libziparchive/zip_error.cpp
deleted file mode 100644
index 14e49bb..0000000
--- a/libziparchive/zip_error.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 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 "zip_error.h"
-
-#include <android-base/macros.h>
-
-static const char* kErrorMessages[] = {
- "Success",
- "Iteration ended",
- "Zlib error",
- "Invalid file",
- "Invalid handle",
- "Duplicate entries in archive",
- "Empty archive",
- "Entry not found",
- "Invalid offset",
- "Inconsistent information",
- "Invalid entry name",
- "I/O error",
- "File mapping failed",
- "Allocation failed",
- "Unsupported zip entry size",
-};
-
-const char* ErrorCodeString(int32_t error_code) {
- // Make sure that the number of entries in kErrorMessages and the ZipError
- // enum match.
- static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
- "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
-
- const uint32_t idx = -error_code;
- if (idx < arraysize(kErrorMessages)) {
- return kErrorMessages[idx];
- }
-
- return "Unknown return code";
-}
diff --git a/libziparchive/zip_error.h b/libziparchive/zip_error.h
deleted file mode 100644
index 3d7285d..0000000
--- a/libziparchive/zip_error.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 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>
-
-enum ZipError : int32_t {
- kSuccess = 0,
-
- kIterationEnd = -1,
-
- // We encountered a Zlib error when inflating a stream from this file.
- // Usually indicates file corruption.
- kZlibError = -2,
-
- // The input file cannot be processed as a zip archive. Usually because
- // it's too small, too large or does not have a valid signature.
- kInvalidFile = -3,
-
- // An invalid iteration / ziparchive handle was passed in as an input
- // argument.
- kInvalidHandle = -4,
-
- // The zip archive contained two (or possibly more) entries with the same
- // name.
- kDuplicateEntry = -5,
-
- // The zip archive contains no entries.
- kEmptyArchive = -6,
-
- // The specified entry was not found in the archive.
- kEntryNotFound = -7,
-
- // The zip archive contained an invalid local file header pointer.
- kInvalidOffset = -8,
-
- // The zip archive contained inconsistent entry information. This could
- // be because the central directory & local file header did not agree, or
- // if the actual uncompressed length or crc32 do not match their declared
- // values.
- kInconsistentInformation = -9,
-
- // An invalid entry name was encountered.
- kInvalidEntryName = -10,
-
- // An I/O related system call (read, lseek, ftruncate, map) failed.
- kIoError = -11,
-
- // We were not able to mmap the central directory or entry contents.
- kMmapFailed = -12,
-
- // An allocation failed.
- kAllocationFailed = -13,
-
- // The compressed or uncompressed size is larger than UINT32_MAX and
- // doesn't fit into the 32 bits zip entry.
- kUnsupportedEntrySize = -14,
-
- kLastErrorCode = kUnsupportedEntrySize,
-};
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
deleted file mode 100644
index 25b1da4..0000000
--- a/libziparchive/zip_writer.cc
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ziparchive/zip_writer.h"
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <zlib.h>
-#include <cstdio>
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
-
-#include <memory>
-#include <vector>
-
-#include "android-base/logging.h"
-
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
-
-#undef powerof2
-#define powerof2(x) \
- ({ \
- __typeof__(x) _x = (x); \
- __typeof__(x) _x2; \
- __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
- })
-
-/* Zip compression methods we support */
-enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
-};
-
-// Size of the output buffer used for compression.
-static const size_t kBufSize = 32768u;
-
-// No error, operation completed successfully.
-static const int32_t kNoError = 0;
-
-// The ZipWriter is in a bad state.
-static const int32_t kInvalidState = -1;
-
-// There was an IO error while writing to disk.
-static const int32_t kIoError = -2;
-
-// The zip entry name was invalid.
-static const int32_t kInvalidEntryName = -3;
-
-// An error occurred in zlib.
-static const int32_t kZlibError = -4;
-
-// The start aligned function was called with the aligned flag.
-static const int32_t kInvalidAlign32Flag = -5;
-
-// The alignment parameter is not a power of 2.
-static const int32_t kInvalidAlignment = -6;
-
-static const char* sErrorCodes[] = {
- "Invalid state", "IO error", "Invalid entry name", "Zlib error",
-};
-
-const char* ZipWriter::ErrorCodeString(int32_t error_code) {
- if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
- return sErrorCodes[-error_code];
- }
- return nullptr;
-}
-
-static void DeleteZStream(z_stream* stream) {
- deflateEnd(stream);
- delete stream;
-}
-
-ZipWriter::ZipWriter(FILE* f)
- : file_(f),
- seekable_(false),
- current_offset_(0),
- state_(State::kWritingZip),
- z_stream_(nullptr, DeleteZStream),
- buffer_(kBufSize) {
- // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
- // will fail as well.
- struct stat file_stats;
- if (fstat(fileno(f), &file_stats) == 0) {
- seekable_ = S_ISREG(file_stats.st_mode);
- }
-}
-
-ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
- : file_(writer.file_),
- seekable_(writer.seekable_),
- current_offset_(writer.current_offset_),
- state_(writer.state_),
- files_(std::move(writer.files_)),
- z_stream_(std::move(writer.z_stream_)),
- buffer_(std::move(writer.buffer_)) {
- writer.file_ = nullptr;
- writer.state_ = State::kError;
-}
-
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
- file_ = writer.file_;
- seekable_ = writer.seekable_;
- current_offset_ = writer.current_offset_;
- state_ = writer.state_;
- files_ = std::move(writer.files_);
- z_stream_ = std::move(writer.z_stream_);
- buffer_ = std::move(writer.buffer_);
- writer.file_ = nullptr;
- writer.state_ = State::kError;
- return *this;
-}
-
-int32_t ZipWriter::HandleError(int32_t error_code) {
- state_ = State::kError;
- z_stream_.reset();
- return error_code;
-}
-
-int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
- uint32_t alignment = 0;
- if (flags & kAlign32) {
- flags &= ~kAlign32;
- alignment = 4;
- }
- return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
-}
-
-int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
- return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
-}
-
-int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
- uint32_t alignment = 0;
- if (flags & kAlign32) {
- flags &= ~kAlign32;
- alignment = 4;
- }
- return StartAlignedEntryWithTime(path, flags, time, alignment);
-}
-
-static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
- /* round up to an even number of seconds */
- when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
-
- struct tm* ptm;
-#if !defined(_WIN32)
- struct tm tm_result;
- ptm = localtime_r(&when, &tm_result);
-#else
- ptm = localtime(&when);
-#endif
-
- int year = ptm->tm_year;
- if (year < 80) {
- year = 80;
- }
-
- *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
- *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
-}
-
-static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
- LocalFileHeader* dst) {
- dst->lfh_signature = LocalFileHeader::kSignature;
- if (use_data_descriptor) {
- // Set this flag to denote that a DataDescriptor struct will appear after the data,
- // containing the crc and size fields.
- dst->gpb_flags |= kGPBDDFlagMask;
-
- // The size and crc fields must be 0.
- dst->compressed_size = 0u;
- dst->uncompressed_size = 0u;
- dst->crc32 = 0u;
- } else {
- dst->compressed_size = src.compressed_size;
- dst->uncompressed_size = src.uncompressed_size;
- dst->crc32 = src.crc32;
- }
- dst->compression_method = src.compression_method;
- dst->last_mod_time = src.last_mod_time;
- dst->last_mod_date = src.last_mod_date;
- DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
- dst->file_name_length = static_cast<uint16_t>(src.path.size());
- dst->extra_field_length = src.padding_length;
-}
-
-int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
- uint32_t alignment) {
- if (state_ != State::kWritingZip) {
- return kInvalidState;
- }
-
- // Can only have 16535 entries because of zip records.
- if (files_.size() == std::numeric_limits<uint16_t>::max()) {
- return HandleError(kIoError);
- }
-
- if (flags & kAlign32) {
- return kInvalidAlign32Flag;
- }
-
- if (powerof2(alignment) == 0) {
- return kInvalidAlignment;
- }
- if (alignment > std::numeric_limits<uint16_t>::max()) {
- return kInvalidAlignment;
- }
-
- FileEntry file_entry = {};
- file_entry.local_file_header_offset = current_offset_;
- file_entry.path = path;
- // No support for larger than 4GB files.
- if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) {
- return HandleError(kIoError);
- }
-
- if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
- file_entry.path.size())) {
- return kInvalidEntryName;
- }
-
- if (flags & ZipWriter::kCompress) {
- file_entry.compression_method = kCompressDeflated;
-
- int32_t result = PrepareDeflate();
- if (result != kNoError) {
- return result;
- }
- } else {
- file_entry.compression_method = kCompressStored;
- }
-
- ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
-
- off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
- // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
- static constexpr auto kPageSize = 4096;
- static constexpr char kSmallZeroPadding[kPageSize] = {};
- // use this buffer if our preallocated one is too small
- std::vector<char> zero_padding_big;
- const char* zero_padding = nullptr;
-
- if (alignment != 0 && (offset & (alignment - 1))) {
- // Pad the extra field so the data will be aligned.
- uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
- file_entry.padding_length = padding;
- offset += padding;
- if (padding <= std::size(kSmallZeroPadding)) {
- zero_padding = kSmallZeroPadding;
- } else {
- zero_padding_big.resize(padding, 0);
- zero_padding = zero_padding_big.data();
- }
- }
-
- LocalFileHeader header = {};
- // Always start expecting a data descriptor. When the data has finished being written,
- // if it is possible to seek back, the GPB flag will reset and the sizes written.
- CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
-
- if (fwrite(&header, sizeof(header), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
- return HandleError(kIoError);
- }
-
- if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
- file_) != file_entry.padding_length) {
- return HandleError(kIoError);
- }
-
- current_file_entry_ = std::move(file_entry);
- current_offset_ = offset;
- state_ = State::kWritingEntry;
- return kNoError;
-}
-
-int32_t ZipWriter::DiscardLastEntry() {
- if (state_ != State::kWritingZip || files_.empty()) {
- return kInvalidState;
- }
-
- FileEntry& last_entry = files_.back();
- current_offset_ = last_entry.local_file_header_offset;
- if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
- return HandleError(kIoError);
- }
- files_.pop_back();
- return kNoError;
-}
-
-int32_t ZipWriter::GetLastEntry(FileEntry* out_entry) {
- CHECK(out_entry != nullptr);
-
- if (files_.empty()) {
- return kInvalidState;
- }
- *out_entry = files_.back();
- return kNoError;
-}
-
-int32_t ZipWriter::PrepareDeflate() {
- CHECK(state_ == State::kWritingZip);
-
- // Initialize the z_stream for compression.
- z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
- int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
- DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
-#pragma GCC diagnostic pop
-
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
- return HandleError(kZlibError);
- } else {
- LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
- return HandleError(kZlibError);
- }
- }
-
- z_stream_->next_out = buffer_.data();
- DCHECK_EQ(buffer_.size(), kBufSize);
- z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
- return kNoError;
-}
-
-int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
- if (state_ != State::kWritingEntry) {
- return HandleError(kInvalidState);
- }
- // Need to be able to mark down data correctly.
- if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) >
- std::numeric_limits<uint32_t>::max()) {
- return HandleError(kIoError);
- }
- uint32_t len32 = static_cast<uint32_t>(len);
-
- int32_t result = kNoError;
- if (current_file_entry_.compression_method & kCompressDeflated) {
- result = CompressBytes(¤t_file_entry_, data, len32);
- } else {
- result = StoreBytes(¤t_file_entry_, data, len32);
- }
-
- if (result != kNoError) {
- return result;
- }
-
- current_file_entry_.crc32 = static_cast<uint32_t>(
- crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32));
- current_file_entry_.uncompressed_size += len32;
- return kNoError;
-}
-
-int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
- CHECK(state_ == State::kWritingEntry);
-
- if (fwrite(data, 1, len, file_) != len) {
- return HandleError(kIoError);
- }
- file->compressed_size += len;
- current_offset_ += len;
- return kNoError;
-}
-
-int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
- CHECK(state_ == State::kWritingEntry);
- CHECK(z_stream_);
- CHECK(z_stream_->next_out != nullptr);
- CHECK(z_stream_->avail_out != 0);
-
- // Prepare the input.
- z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
- z_stream_->avail_in = len;
-
- while (z_stream_->avail_in > 0) {
- // We have more data to compress.
- int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
- if (zerr != Z_OK) {
- return HandleError(kZlibError);
- }
-
- if (z_stream_->avail_out == 0) {
- // The output is full, let's write it to disk.
- size_t write_bytes = z_stream_->next_out - buffer_.data();
- if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
- return HandleError(kIoError);
- }
- file->compressed_size += write_bytes;
- current_offset_ += write_bytes;
-
- // Reset the output buffer for the next input.
- z_stream_->next_out = buffer_.data();
- DCHECK_EQ(buffer_.size(), kBufSize);
- z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
- }
- }
- return kNoError;
-}
-
-int32_t ZipWriter::FlushCompressedBytes(FileEntry* file) {
- CHECK(state_ == State::kWritingEntry);
- CHECK(z_stream_);
- CHECK(z_stream_->next_out != nullptr);
- CHECK(z_stream_->avail_out != 0);
-
- // Keep deflating while there isn't enough space in the buffer to
- // to complete the compress.
- int zerr;
- while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
- CHECK(z_stream_->avail_out == 0);
- size_t write_bytes = z_stream_->next_out - buffer_.data();
- if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
- return HandleError(kIoError);
- }
- file->compressed_size += write_bytes;
- current_offset_ += write_bytes;
-
- z_stream_->next_out = buffer_.data();
- DCHECK_EQ(buffer_.size(), kBufSize);
- z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
- }
- if (zerr != Z_STREAM_END) {
- return HandleError(kZlibError);
- }
-
- size_t write_bytes = z_stream_->next_out - buffer_.data();
- if (write_bytes != 0) {
- if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
- return HandleError(kIoError);
- }
- file->compressed_size += write_bytes;
- current_offset_ += write_bytes;
- }
- z_stream_.reset();
- return kNoError;
-}
-
-bool ZipWriter::ShouldUseDataDescriptor() const {
- // Only use a trailing "data descriptor" if the output isn't seekable.
- return !seekable_;
-}
-
-int32_t ZipWriter::FinishEntry() {
- if (state_ != State::kWritingEntry) {
- return kInvalidState;
- }
-
- if (current_file_entry_.compression_method & kCompressDeflated) {
- int32_t result = FlushCompressedBytes(¤t_file_entry_);
- if (result != kNoError) {
- return result;
- }
- }
-
- if (ShouldUseDataDescriptor()) {
- // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
- // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
- // We haven't supported zip64 format yet. Write both uncompressed size and compressed
- // size as uint32_t.
- std::vector<uint32_t> dataDescriptor = {
- DataDescriptor::kOptSignature, current_file_entry_.crc32,
- current_file_entry_.compressed_size, current_file_entry_.uncompressed_size};
- if (fwrite(dataDescriptor.data(), dataDescriptor.size() * sizeof(uint32_t), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- current_offset_ += sizeof(uint32_t) * dataDescriptor.size();
- } else {
- // Seek back to the header and rewrite to include the size.
- if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
- return HandleError(kIoError);
- }
-
- LocalFileHeader header = {};
- CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
-
- if (fwrite(&header, sizeof(header), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
- return HandleError(kIoError);
- }
- }
-
- files_.emplace_back(std::move(current_file_entry_));
- state_ = State::kWritingZip;
- return kNoError;
-}
-
-int32_t ZipWriter::Finish() {
- if (state_ != State::kWritingZip) {
- return kInvalidState;
- }
-
- off_t startOfCdr = current_offset_;
- for (FileEntry& file : files_) {
- CentralDirectoryRecord cdr = {};
- cdr.record_signature = CentralDirectoryRecord::kSignature;
- if (ShouldUseDataDescriptor()) {
- cdr.gpb_flags |= kGPBDDFlagMask;
- }
- cdr.compression_method = file.compression_method;
- cdr.last_mod_time = file.last_mod_time;
- cdr.last_mod_date = file.last_mod_date;
- cdr.crc32 = file.crc32;
- cdr.compressed_size = file.compressed_size;
- cdr.uncompressed_size = file.uncompressed_size;
- // Checked in IsValidEntryName.
- DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max());
- cdr.file_name_length = static_cast<uint16_t>(file.path.size());
- // Checked in StartAlignedEntryWithTime.
- DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max());
- cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
- if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
- return HandleError(kIoError);
- }
-
- current_offset_ += sizeof(cdr) + file.path.size();
- }
-
- EocdRecord er = {};
- er.eocd_signature = EocdRecord::kSignature;
- er.disk_num = 0;
- er.cd_start_disk = 0;
- // Checked when adding entries.
- DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max());
- er.num_records_on_disk = static_cast<uint16_t>(files_.size());
- er.num_records = static_cast<uint16_t>(files_.size());
- if (current_offset_ > std::numeric_limits<uint32_t>::max()) {
- return HandleError(kIoError);
- }
- er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr);
- er.cd_start_offset = static_cast<uint32_t>(startOfCdr);
-
- if (fwrite(&er, sizeof(er), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- current_offset_ += sizeof(er);
-
- // Since we can BackUp() and potentially finish writing at an offset less than one we had
- // already written at, we must truncate the file.
-
- if (ftruncate(fileno(file_), current_offset_) != 0) {
- return HandleError(kIoError);
- }
-
- if (fflush(file_) != 0) {
- return HandleError(kIoError);
- }
-
- state_ = State::kDone;
- return kNoError;
-}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
deleted file mode 100644
index d324d4b..0000000
--- a/libziparchive/zip_writer_test.cc
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ziparchive/zip_writer.h"
-#include "ziparchive/zip_archive.h"
-
-#include <android-base/test_utils.h>
-#include <gtest/gtest.h>
-#include <time.h>
-#include <memory>
-#include <vector>
-
-static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
- ZipArchiveHandle handle,
- ZipEntry* zip_entry);
-
-struct zipwriter : public ::testing::Test {
- TemporaryFile* temp_file_;
- int fd_;
- FILE* file_;
-
- void SetUp() override {
- temp_file_ = new TemporaryFile();
- fd_ = temp_file_->fd;
- file_ = fdopen(fd_, "w");
- ASSERT_NE(file_, nullptr);
- }
-
- void TearDown() override {
- fclose(file_);
- delete temp_file_;
- }
-};
-
-TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
- ZipWriter writer(file_);
-
- const char* expected = "hello";
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.WriteBytes("llo", 3));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(0u, data.has_data_descriptor);
- EXPECT_EQ(strlen(expected), data.compressed_length);
- ASSERT_EQ(strlen(expected), data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes("llo", 3));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
-
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(2u, data.compressed_length);
- ASSERT_EQ(2u, data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
-
- ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(3u, data.compressed_length);
- ASSERT_EQ(3u, data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
-
- ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(0u, data.compressed_length);
- EXPECT_EQ(0u, data.uncompressed_length);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0x03);
-
- CloseArchive(handle);
-}
-
-static struct tm MakeTm() {
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- tm.tm_year = 2001 - 1900;
- tm.tm_mon = 1;
- tm.tm_mday = 12;
- tm.tm_hour = 18;
- tm.tm_min = 30;
- tm.tm_sec = 20;
- return tm;
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
- ZipWriter writer(file_);
-
- struct tm tm = MakeTm();
- time_t time = mktime(&tm);
- ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0x03);
-
- struct tm mod = data.GetModificationTime();
- EXPECT_EQ(tm.tm_sec, mod.tm_sec);
- EXPECT_EQ(tm.tm_min, mod.tm_min);
- EXPECT_EQ(tm.tm_hour, mod.tm_hour);
- EXPECT_EQ(tm.tm_mday, mod.tm_mday);
- EXPECT_EQ(tm.tm_mon, mod.tm_mon);
- EXPECT_EQ(tm.tm_year, mod.tm_year);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0xfff);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
- ZipWriter writer(file_);
-
- struct tm tm = MakeTm();
- time_t time = mktime(&tm);
- ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0xfff);
-
- struct tm mod = data.GetModificationTime();
- EXPECT_EQ(tm.tm_sec, mod.tm_sec);
- EXPECT_EQ(tm.tm_min, mod.tm_min);
- EXPECT_EQ(tm.tm_hour, mod.tm_hour);
- EXPECT_EQ(tm.tm_mday, mod.tm_mday);
- EXPECT_EQ(tm.tm_mon, mod.tm_mon);
- EXPECT_EQ(tm.tm_year, mod.tm_year);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
- ASSERT_EQ(0, writer.WriteBytes("helo", 4));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressDeflated, data.method);
- EXPECT_EQ(0u, data.has_data_descriptor);
- ASSERT_EQ(4u, data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteCompressedZipFlushFull) {
- // This exact data will cause the Finish() to require multiple calls
- // to deflate() because the ZipWriter buffer isn't big enough to hold
- // the entire compressed data buffer.
- constexpr size_t kBufSize = 10000000;
- std::vector<uint8_t> buffer(kBufSize);
- size_t prev = 1;
- for (size_t i = 0; i < kBufSize; i++) {
- buffer[i] = static_cast<uint8_t>(i + prev);
- prev = i;
- }
-
- ZipWriter writer(file_);
- ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
- ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressDeflated, data.method);
- EXPECT_EQ(kBufSize, data.uncompressed_length);
-
- std::vector<uint8_t> decompress(kBufSize);
- memset(decompress.data(), 0, kBufSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
- static_cast<uint32_t>(decompress.size())));
- EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
- << "Input buffer and output buffer are different.";
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, CheckStartEntryErrors) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
- ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
-}
-
-TEST_F(zipwriter, BackupRemovesTheLastFile) {
- ZipWriter writer(file_);
-
- const char* kKeepThis = "keep this";
- const char* kDropThis = "drop this";
- const char* kReplaceWithThis = "replace with this";
-
- ZipWriter::FileEntry entry;
- EXPECT_LT(writer.GetLastEntry(&entry), 0);
-
- ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("keep.txt", entry.path);
-
- ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("drop.txt", entry.path);
-
- ASSERT_EQ(0, writer.DiscardLastEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("keep.txt", entry.path);
-
- ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("replace.txt", entry.path);
-
- ASSERT_EQ(0, writer.Finish());
-
- // Verify that "drop.txt" does not exist.
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
- ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
-
- ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
-
- ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
- ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteToUnseekableFile) {
- const char* expected = "hello";
- ZipWriter writer(file_);
- writer.seekable_ = false;
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(1u, data.has_data_descriptor);
- EXPECT_EQ(strlen(expected), data.compressed_length);
- ASSERT_EQ(strlen(expected), data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, TruncateFileAfterBackup) {
- ZipWriter writer(file_);
-
- const char* kSmall = "small";
-
- ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
- std::vector<uint8_t> data;
- data.resize(1024 * 1024, 0xef);
- ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
- ASSERT_EQ(0, writer.FinishEntry());
-
- off_t before_len = ftello(file_);
-
- ZipWriter::FileEntry entry;
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- ASSERT_EQ(0, writer.DiscardLastEntry());
-
- ASSERT_EQ(0, writer.Finish());
-
- off_t after_len = ftello(file_);
-
- ASSERT_GT(before_len, after_len);
-}
-
-static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
- ZipArchiveHandle handle,
- ZipEntry* zip_entry) {
- if (expected.size() != zip_entry->uncompressed_length) {
- return ::testing::AssertionFailure()
- << "uncompressed entry size " << zip_entry->uncompressed_length
- << " does not match expected size " << expected.size();
- }
-
- std::string actual;
- actual.resize(expected.size());
-
- uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
- if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
- return ::testing::AssertionFailure() << "failed to extract entry";
- }
-
- if (expected != actual) {
- return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
- << "' does not match expected '" << expected << "'";
- }
- return ::testing::AssertionSuccess();
-}
diff --git a/libziparchive/ziptool-tests.xml b/libziparchive/ziptool-tests.xml
deleted file mode 100644
index 211119f..0000000
--- a/libziparchive/ziptool-tests.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Config for running ziptool-tests through Atest or in Infra">
- <option name="test-suite-tag" value="ziptool-tests" />
- <!-- This test requires a device, so it's not annotated with a null-device. -->
- <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
- <option name="binary" value="run-ziptool-tests-on-android.sh" />
- <!-- Test script assumes a relative path with the cli-tests/ folders. -->
- <option name="relative-path-execution" value="true" />
- <!-- Tests shouldn't be that long but set 15m to be safe. -->
- <option name="per-binary-timeout" value="15m" />
- </test>
-</configuration>
diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp
deleted file mode 100644
index a261535..0000000
--- a/libziparchive/ziptool.cpp
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <set>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <ziparchive/zip_archive.h>
-
-using android::base::EndsWith;
-using android::base::StartsWith;
-
-enum OverwriteMode {
- kAlways,
- kNever,
- kPrompt,
-};
-
-enum Role {
- kUnzip,
- kZipinfo,
-};
-
-static Role role;
-static OverwriteMode overwrite_mode = kPrompt;
-static bool flag_1 = false;
-static std::string flag_d;
-static bool flag_l = false;
-static bool flag_p = false;
-static bool flag_q = false;
-static bool flag_v = false;
-static bool flag_x = false;
-static const char* archive_name = nullptr;
-static std::set<std::string> includes;
-static std::set<std::string> excludes;
-static uint64_t total_uncompressed_length = 0;
-static uint64_t total_compressed_length = 0;
-static size_t file_count = 0;
-
-static const char* g_progname;
-
-static void die(int error, const char* fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
- fprintf(stderr, "%s: ", g_progname);
- vfprintf(stderr, fmt, ap);
- if (error != 0) fprintf(stderr, ": %s", strerror(error));
- fprintf(stderr, "\n");
- va_end(ap);
- exit(1);
-}
-
-static bool ShouldInclude(const std::string& name) {
- // Explicitly excluded?
- if (!excludes.empty()) {
- for (const auto& exclude : excludes) {
- if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
- }
- }
-
- // Implicitly included?
- if (includes.empty()) return true;
-
- // Explicitly included?
- for (const auto& include : includes) {
- if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
- }
- return false;
-}
-
-static bool MakeDirectoryHierarchy(const std::string& path) {
- // stat rather than lstat because a symbolic link to a directory is fine too.
- struct stat sb;
- if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
-
- // Ensure the parent directories exist first.
- if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
-
- // Then try to create this directory.
- return (mkdir(path.c_str(), 0777) != -1);
-}
-
-static float CompressionRatio(int64_t uncompressed, int64_t compressed) {
- if (uncompressed == 0) return 0;
- return static_cast<float>(100LL * (uncompressed - compressed)) /
- static_cast<float>(uncompressed);
-}
-
-static void MaybeShowHeader(ZipArchiveHandle zah) {
- if (role == kUnzip) {
- // unzip has three formats.
- if (!flag_q) printf("Archive: %s\n", archive_name);
- if (flag_v) {
- printf(
- " Length Method Size Cmpr Date Time CRC-32 Name\n"
- "-------- ------ ------- ---- ---------- ----- -------- ----\n");
- } else if (flag_l) {
- printf(
- " Length Date Time Name\n"
- "--------- ---------- ----- ----\n");
- }
- } else {
- // zipinfo.
- if (!flag_1 && includes.empty() && excludes.empty()) {
- ZipArchiveInfo info{GetArchiveInfo(zah)};
- printf("Archive: %s\n", archive_name);
- printf("Zip file size: %" PRId64 " bytes, number of entries: %" PRIu64 "\n",
- info.archive_size, info.entry_count);
- }
- }
-}
-
-static void MaybeShowFooter() {
- if (role == kUnzip) {
- if (flag_v) {
- printf(
- "-------- ------- --- -------\n"
- "%8" PRId64 " %8" PRId64 " %3.0f%% %zu file%s\n",
- total_uncompressed_length, total_compressed_length,
- CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
- (file_count == 1) ? "" : "s");
- } else if (flag_l) {
- printf(
- "--------- -------\n"
- "%9" PRId64 " %zu file%s\n",
- total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
- }
- } else {
- if (!flag_1 && includes.empty() && excludes.empty()) {
- printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %.1f%%\n",
- file_count, total_uncompressed_length, total_compressed_length,
- CompressionRatio(total_uncompressed_length, total_compressed_length));
- }
- }
-}
-
-static bool PromptOverwrite(const std::string& dst) {
- // TODO: [r]ename not implemented because it doesn't seem useful.
- printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
- fflush(stdout);
- while (true) {
- char* line = nullptr;
- size_t n;
- if (getline(&line, &n, stdin) == -1) {
- die(0, "(EOF/read error; assuming [N]one...)");
- overwrite_mode = kNever;
- return false;
- }
- if (n == 0) continue;
- char cmd = line[0];
- free(line);
- switch (cmd) {
- case 'y':
- return true;
- case 'n':
- return false;
- case 'A':
- overwrite_mode = kAlways;
- return true;
- case 'N':
- overwrite_mode = kNever;
- return false;
- }
- }
-}
-
-static void ExtractToPipe(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
- // We need to extract to memory because ExtractEntryToFile insists on
- // being able to seek and truncate, and you can't do that with stdout.
- if (entry.uncompressed_length > SIZE_MAX) {
- die(0, "entry size %" PRIu64 " is too large to extract.", entry.uncompressed_length);
- }
- auto uncompressed_length = static_cast<size_t>(entry.uncompressed_length);
- uint8_t* buffer = new uint8_t[uncompressed_length];
- int err = ExtractToMemory(zah, &entry, buffer, uncompressed_length);
- if (err < 0) {
- die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
- }
- if (!android::base::WriteFully(1, buffer, uncompressed_length)) {
- die(errno, "failed to write %s to stdout", name.c_str());
- }
- delete[] buffer;
-}
-
-static void ExtractOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
- // Bad filename?
- if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
- die(0, "bad filename %s", name.c_str());
- }
-
- // Where are we actually extracting to (for human-readable output)?
- // flag_d is the empty string if -d wasn't used, or has a trailing '/'
- // otherwise.
- std::string dst = flag_d + name;
-
- // Ensure the directory hierarchy exists.
- if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
- die(errno, "couldn't create directory hierarchy for %s", dst.c_str());
- }
-
- // An entry in a zip file can just be a directory itself.
- if (EndsWith(name, "/")) {
- if (mkdir(name.c_str(), entry.unix_mode) == -1) {
- // If the directory already exists, that's fine.
- if (errno == EEXIST) {
- struct stat sb;
- if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
- }
- die(errno, "couldn't extract directory %s", dst.c_str());
- }
- return;
- }
-
- // Create the file.
- int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
- if (fd == -1 && errno == EEXIST) {
- if (overwrite_mode == kNever) return;
- if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
- // Either overwrite_mode is kAlways or the user consented to this specific case.
- fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
- }
- if (fd == -1) die(errno, "couldn't create file %s", dst.c_str());
-
- // Actually extract into the file.
- if (!flag_q) printf(" inflating: %s\n", dst.c_str());
- int err = ExtractEntryToFile(zah, &entry, fd);
- if (err < 0) die(0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
- close(fd);
-}
-
-static void ListOne(const ZipEntry64& entry, const std::string& name) {
- tm t = entry.GetModificationTime();
- char time[32];
- snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
- t.tm_mday, t.tm_hour, t.tm_min);
- if (flag_v) {
- printf("%8" PRIu64 " %s %8" PRIu64 " %3.0f%% %s %08x %s\n", entry.uncompressed_length,
- (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
- CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
- name.c_str());
- } else {
- printf("%9" PRIu64 " %s %s\n", entry.uncompressed_length, time, name.c_str());
- }
-}
-
-static void InfoOne(const ZipEntry64& entry, const std::string& name) {
- if (flag_1) {
- // "android-ndk-r19b/sources/android/NOTICE"
- printf("%s\n", name.c_str());
- return;
- }
-
- int version = entry.version_made_by & 0xff;
- int os = (entry.version_made_by >> 8) & 0xff;
-
- // TODO: Support suid/sgid? Non-Unix/non-FAT host file system attributes?
- const char* src_fs = "???";
- char mode[] = "??? ";
- if (os == 0) {
- src_fs = "fat";
- // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
- int attrs = entry.external_file_attributes & 0xff;
- mode[0] = (attrs & 0x10) ? 'd' : '-';
- mode[1] = 'r';
- mode[2] = (attrs & 0x01) ? '-' : 'w';
- // The man page also mentions ".btm", but that seems to be obsolete?
- mode[3] = EndsWith(name, ".exe") || EndsWith(name, ".com") || EndsWith(name, ".bat") ||
- EndsWith(name, ".cmd")
- ? 'x'
- : '-';
- mode[4] = (attrs & 0x20) ? 'a' : '-';
- mode[5] = (attrs & 0x02) ? 'h' : '-';
- mode[6] = (attrs & 0x04) ? 's' : '-';
- } else if (os == 3) {
- src_fs = "unx";
- mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
- mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
- mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
- mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
- mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
- mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
- mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
- mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
- mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
- mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
- }
-
- char method[5] = "stor";
- if (entry.method == kCompressDeflated) {
- snprintf(method, sizeof(method), "def%c", "NXFS"[(entry.gpbf >> 1) & 0x3]);
- }
-
- // TODO: zipinfo (unlike unzip) sometimes uses time zone?
- // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
- tm t = entry.GetModificationTime();
- char time[32];
- snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
- t.tm_mday, t.tm_hour, t.tm_min);
-
- // "-rw-r--r-- 3.0 unx 577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
- printf("%s %2d.%d %s %8" PRIu64 " %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
- entry.uncompressed_length, entry.is_text ? 't' : 'b',
- entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
-}
-
-static void ProcessOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
- if (role == kUnzip) {
- if (flag_l || flag_v) {
- // -l or -lv or -lq or -v.
- ListOne(entry, name);
- } else {
- // Actually extract.
- if (flag_p) {
- ExtractToPipe(zah, entry, name);
- } else {
- ExtractOne(zah, entry, name);
- }
- }
- } else {
- // zipinfo or zipinfo -1.
- InfoOne(entry, name);
- }
- total_uncompressed_length += entry.uncompressed_length;
- total_compressed_length += entry.compressed_length;
- ++file_count;
-}
-
-static void ProcessAll(ZipArchiveHandle zah) {
- MaybeShowHeader(zah);
-
- // libziparchive iteration order doesn't match the central directory.
- // We could sort, but that would cost extra and wouldn't match either.
- void* cookie;
- int err = StartIteration(zah, &cookie);
- if (err != 0) {
- die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
- }
-
- ZipEntry64 entry;
- std::string name;
- while ((err = Next(cookie, &entry, &name)) >= 0) {
- if (ShouldInclude(name)) ProcessOne(zah, entry, name);
- }
-
- if (err < -1) die(0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
- EndIteration(cookie);
-
- MaybeShowFooter();
-}
-
-static void ShowHelp(bool full) {
- if (role == kUnzip) {
- fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
- if (!full) exit(EXIT_FAILURE);
-
- printf(
- "\n"
- "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
- "exclude (-x) lists use shell glob patterns.\n"
- "\n"
- "-d DIR Extract into DIR\n"
- "-l List contents (-lq excludes archive name, -lv is verbose)\n"
- "-n Never overwrite files (default: prompt)\n"
- "-o Always overwrite files\n"
- "-p Pipe to stdout\n"
- "-q Quiet\n"
- "-v List contents verbosely\n"
- "-x FILE Exclude files\n");
- } else {
- fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
- if (!full) exit(EXIT_FAILURE);
-
- printf(
- "\n"
- "Show information about FILEs from ZIP archive. Default is all files.\n"
- "Both the include and exclude (-x) lists use shell glob patterns.\n"
- "\n"
- "-1 Show filenames only, one per line\n"
- "-x FILE Exclude files\n");
- }
- exit(EXIT_SUCCESS);
-}
-
-static void HandleCommonOption(int opt) {
- switch (opt) {
- case 'h':
- ShowHelp(true);
- break;
- case 'x':
- flag_x = true;
- break;
- case 1:
- // -x swallows all following arguments, so we use '-' in the getopt
- // string and collect files here.
- if (!archive_name) {
- archive_name = optarg;
- } else if (flag_x) {
- excludes.insert(optarg);
- } else {
- includes.insert(optarg);
- }
- break;
- default:
- ShowHelp(false);
- break;
- }
-}
-
-int main(int argc, char* argv[]) {
- // Who am I, and what am I doing?
- g_progname = basename(argv[0]);
- if (!strcmp(g_progname, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
- if (!strcmp(g_progname, "unzip")) {
- role = kUnzip;
- } else if (!strcmp(g_progname, "zipinfo")) {
- role = kZipinfo;
- } else {
- die(0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
- }
-
- static const struct option opts[] = {
- {"help", no_argument, 0, 'h'},
- {},
- };
-
- if (role == kUnzip) {
- // `unzip -Z` is "zipinfo mode", so in that case just restart...
- if (argc > 1 && !strcmp(argv[1], "-Z")) {
- argv[1] = const_cast<char*>("zipinfo");
- return main(argc - 1, argv + 1);
- }
-
- int opt;
- while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
- switch (opt) {
- case 'd':
- flag_d = optarg;
- if (!EndsWith(flag_d, "/")) flag_d += '/';
- break;
- case 'l':
- flag_l = true;
- break;
- case 'n':
- overwrite_mode = kNever;
- break;
- case 'o':
- overwrite_mode = kAlways;
- break;
- case 'p':
- flag_p = flag_q = true;
- break;
- case 'q':
- flag_q = true;
- break;
- case 'v':
- flag_v = true;
- break;
- default:
- HandleCommonOption(opt);
- break;
- }
- }
- } else {
- int opt;
- while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
- switch (opt) {
- case '1':
- flag_1 = true;
- break;
- default:
- HandleCommonOption(opt);
- break;
- }
- }
- }
-
- if (!archive_name) die(0, "missing archive filename");
-
- // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
- ZipArchiveHandle zah;
- int32_t err;
- if ((err = OpenArchive(archive_name, &zah)) != 0) {
- die(0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
- }
-
- // Implement -d by changing into that directory.
- // We'll create implicit directories based on paths in the zip file, and we'll create
- // the -d directory itself, but we require that *parents* of the -d directory already exists.
- // This is pretty arbitrary, but it's the behavior of the original unzip.
- if (!flag_d.empty()) {
- if (mkdir(flag_d.c_str(), 0777) == -1 && errno != EEXIST) {
- die(errno, "couldn't created %s", flag_d.c_str());
- }
- if (chdir(flag_d.c_str()) == -1) {
- die(errno, "couldn't chdir to %s", flag_d.c_str());
- }
- }
-
- ProcessAll(zah);
-
- CloseArchive(zah);
- return 0;
-}
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 8185f01..13023f2 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -459,7 +459,7 @@
closedir);
if (!dir.get()) return retval;
- log_time now(android_log_clockid());
+ log_time now(CLOCK_REALTIME);
size_t len = strlen(file);
log_time modulo(0, NS_PER_SEC);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index b32b437..f3fdfd7 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -174,11 +174,6 @@
}
TEST(logcat, year) {
- if (android_log_clockid() == CLOCK_MONOTONIC) {
- fprintf(stderr, "Skipping test, logd is monotonic time\n");
- return;
- }
-
int count;
int tries = 3; // in case run too soon after system start or buffer clear
@@ -249,11 +244,6 @@
}
TEST(logcat, tz) {
- if (android_log_clockid() == CLOCK_MONOTONIC) {
- fprintf(stderr, "Skipping test, logd is monotonic time\n");
- return;
- }
-
int tries = 4; // in case run too soon after system start or buffer clear
int count;
@@ -485,8 +475,8 @@
continue;
}
- log_time tx((const char*)&t);
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(&t);
+ if (ts == *tx) {
++count;
}
}
@@ -531,8 +521,8 @@
continue;
}
- log_time tx((const char*)&t);
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(&t);
+ if (ts == *tx) {
++count;
}
}
diff --git a/logd/Android.bp b/logd/Android.bp
index 2663271..1f6ab34 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -28,38 +28,54 @@
"-DLIBLOG_LOG_TAG=1006",
]
+cc_defaults {
+ name: "logd_defaults",
+
+ shared_libs: ["libbase"],
+ cflags: [
+ "-Wextra",
+ "-Wthread-safety",
+ ] + event_flag,
+
+ lto: {
+ thin: true,
+ },
+}
+
cc_library_static {
name: "liblogd",
-
+ defaults: ["logd_defaults"],
+ host_supported: true,
srcs: [
- "LogCommand.cpp",
- "CommandListener.cpp",
- "LogListener.cpp",
- "LogReader.cpp",
- "LogBuffer.cpp",
+ "ChattyLogBuffer.cpp",
+ "LogReaderList.cpp",
+ "LogReaderThread.cpp",
"LogBufferElement.cpp",
- "LogTimes.cpp",
"LogStatistics.cpp",
"LogWhiteBlackList.cpp",
- "libaudit.c",
- "LogAudit.cpp",
- "LogKlog.cpp",
"LogTags.cpp",
+ "SimpleLogBuffer.cpp",
],
logtags: ["event.logtags"],
- shared_libs: ["libbase"],
-
export_include_dirs: ["."],
-
- cflags: ["-Werror"] + event_flag,
}
cc_binary {
name: "logd",
+ defaults: ["logd_defaults"],
init_rc: ["logd.rc"],
- srcs: ["main.cpp"],
+ srcs: [
+ "main.cpp",
+ "LogPermissions.cpp",
+ "CommandListener.cpp",
+ "LogListener.cpp",
+ "LogReader.cpp",
+ "LogAudit.cpp",
+ "LogKlog.cpp",
+ "libaudit.cpp",
+ ],
static_libs: [
"liblog",
@@ -69,31 +85,24 @@
shared_libs: [
"libsysutils",
"libcutils",
- "libbase",
"libpackagelistparser",
"libprocessgroup",
"libcap",
],
-
- cflags: ["-Werror"],
}
cc_binary {
name: "auditctl",
- srcs: ["auditctl.cpp"],
-
- static_libs: [
- "liblogd",
+ srcs: [
+ "auditctl.cpp",
+ "libaudit.cpp",
],
shared_libs: ["libbase"],
cflags: [
- "-Wall",
"-Wextra",
- "-Werror",
- "-Wconversion"
],
}
@@ -102,3 +111,59 @@
src: "logtagd.rc",
sub_dir: "init",
}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "logd-unit-test-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ] + event_flag,
+
+ srcs: [
+ "ChattyLogBufferTest.cpp",
+ "logd_test.cpp",
+ "LogBufferTest.cpp",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "liblogd",
+ "libselinux",
+ ],
+}
+
+// Build tests for the logger. Run with:
+// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+ name: "logd-unit-tests",
+ host_supported: true,
+ defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+ name: "CtsLogdTestCases",
+ defaults: ["logd-unit-test-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: [
+ "cts",
+ "vts10",
+ ],
+}
diff --git a/logd/tests/AndroidTest.xml b/logd/AndroidTest.xml
similarity index 100%
rename from logd/tests/AndroidTest.xml
rename to logd/AndroidTest.xml
diff --git a/logd/ChattyLogBuffer.cpp b/logd/ChattyLogBuffer.cpp
new file mode 100644
index 0000000..0ba6025
--- /dev/null
+++ b/logd/ChattyLogBuffer.cpp
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2012-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.
+ */
+// for manual checking of stale entries during ChattyLogBuffer::erase()
+//#define DEBUG_CHECK_FOR_STALE_ENTRIES
+
+#include "ChattyLogBuffer.h"
+
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/user.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <limits>
+#include <unordered_map>
+#include <utility>
+
+#include <private/android_logger.h>
+
+#include "LogUtils.h"
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+ChattyLogBuffer::ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+ LogStatistics* stats)
+ : SimpleLogBuffer(reader_list, tags, stats), prune_(prune) {}
+
+ChattyLogBuffer::~ChattyLogBuffer() {}
+
+enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
+
+static enum match_type Identical(LogBufferElement* elem, LogBufferElement* last) {
+ // is it mostly identical?
+ // if (!elem) return DIFFERENT;
+ ssize_t lenl = elem->getMsgLen();
+ if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
+ // if (!last) return DIFFERENT;
+ ssize_t lenr = last->getMsgLen();
+ if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
+ // if (elem->getLogId() != last->getLogId()) return DIFFERENT;
+ if (elem->getUid() != last->getUid()) return DIFFERENT;
+ if (elem->getPid() != last->getPid()) return DIFFERENT;
+ if (elem->getTid() != last->getTid()) return DIFFERENT;
+
+ // last is more than a minute old, stop squashing identical messages
+ if (elem->getRealTime().nsec() > (last->getRealTime().nsec() + 60 * NS_PER_SEC))
+ return DIFFERENT;
+
+ // Identical message
+ const char* msgl = elem->getMsg();
+ const char* msgr = last->getMsg();
+ if (lenl == lenr) {
+ if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
+ // liblog tagged messages (content gets summed)
+ if (elem->getLogId() == LOG_ID_EVENTS && lenl == sizeof(android_log_event_int_t) &&
+ !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+ elem->getTag() == LIBLOG_LOG_TAG) {
+ return SAME_LIBLOG;
+ }
+ }
+
+ // audit message (except sequence number) identical?
+ if (last->isBinary() && lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t)) &&
+ lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t))) {
+ if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) - sizeof(int32_t))) {
+ return DIFFERENT;
+ }
+ msgl += sizeof(android_log_event_string_t);
+ lenl -= sizeof(android_log_event_string_t);
+ msgr += sizeof(android_log_event_string_t);
+ lenr -= sizeof(android_log_event_string_t);
+ }
+ static const char avc[] = "): avc: ";
+ const char* avcl = android::strnstr(msgl, lenl, avc);
+ if (!avcl) return DIFFERENT;
+ lenl -= avcl - msgl;
+ const char* avcr = android::strnstr(msgr, lenr, avc);
+ if (!avcr) return DIFFERENT;
+ lenr -= avcr - msgr;
+ if (lenl != lenr) return DIFFERENT;
+ if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc), lenl - strlen(avc))) {
+ return DIFFERENT;
+ }
+ return SAME;
+}
+
+void ChattyLogBuffer::LogInternal(LogBufferElement&& elem) {
+ // b/137093665: don't coalesce security messages.
+ if (elem.getLogId() == LOG_ID_SECURITY) {
+ SimpleLogBuffer::LogInternal(std::move(elem));
+ return;
+ }
+ int log_id = elem.getLogId();
+
+ // Initialize last_logged_elements_ to a copy of elem if logging the first element for a log_id.
+ if (!last_logged_elements_[log_id]) {
+ last_logged_elements_[log_id].emplace(elem);
+ SimpleLogBuffer::LogInternal(std::move(elem));
+ return;
+ }
+
+ LogBufferElement& current_last = *last_logged_elements_[log_id];
+ enum match_type match = Identical(&elem, ¤t_last);
+
+ if (match == DIFFERENT) {
+ if (duplicate_elements_[log_id]) {
+ // If we previously had 3+ identical messages, log the chatty message.
+ if (duplicate_elements_[log_id]->getDropped() > 0) {
+ SimpleLogBuffer::LogInternal(std::move(*duplicate_elements_[log_id]));
+ }
+ duplicate_elements_[log_id].reset();
+ // Log the saved copy of the last identical message seen.
+ SimpleLogBuffer::LogInternal(std::move(current_last));
+ }
+ last_logged_elements_[log_id].emplace(elem);
+ SimpleLogBuffer::LogInternal(std::move(elem));
+ return;
+ }
+
+ // 2 identical message: set duplicate_elements_ appropriately.
+ if (!duplicate_elements_[log_id]) {
+ duplicate_elements_[log_id].emplace(std::move(current_last));
+ last_logged_elements_[log_id].emplace(std::move(elem));
+ return;
+ }
+
+ // 3+ identical LIBLOG event messages: coalesce them into last_logged_elements_.
+ if (match == SAME_LIBLOG) {
+ const android_log_event_int_t* current_last_event =
+ reinterpret_cast<const android_log_event_int_t*>(current_last.getMsg());
+ int64_t current_last_count = current_last_event->payload.data;
+ android_log_event_int_t* elem_event =
+ reinterpret_cast<android_log_event_int_t*>(const_cast<char*>(elem.getMsg()));
+ int64_t elem_count = elem_event->payload.data;
+
+ int64_t total = current_last_count + elem_count;
+ if (total > std::numeric_limits<int32_t>::max()) {
+ SimpleLogBuffer::LogInternal(std::move(current_last));
+ last_logged_elements_[log_id].emplace(std::move(elem));
+ return;
+ }
+ stats()->AddTotal(current_last.getLogId(), current_last.getMsgLen());
+ elem_event->payload.data = total;
+ last_logged_elements_[log_id].emplace(std::move(elem));
+ return;
+ }
+
+ // 3+ identical messages (not LIBLOG) messages: increase the drop count.
+ uint16_t dropped_count = duplicate_elements_[log_id]->getDropped();
+ if (dropped_count == std::numeric_limits<uint16_t>::max()) {
+ SimpleLogBuffer::LogInternal(std::move(*duplicate_elements_[log_id]));
+ dropped_count = 0;
+ }
+ // We're dropping the current_last log so add its stats to the total.
+ stats()->AddTotal(current_last.getLogId(), current_last.getMsgLen());
+ // Use current_last for tracking the dropped count to always use the latest timestamp.
+ current_last.setDropped(dropped_count + 1);
+ duplicate_elements_[log_id].emplace(std::move(current_last));
+ last_logged_elements_[log_id].emplace(std::move(elem));
+}
+
+LogBufferElementCollection::iterator ChattyLogBuffer::Erase(LogBufferElementCollection::iterator it,
+ bool coalesce) {
+ LogBufferElement& element = *it;
+ log_id_t id = element.getLogId();
+
+ // Remove iterator references in the various lists that will become stale
+ // after the element is erased from the main logging list.
+
+ { // start of scope for found iterator
+ int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.getTag()
+ : element.getUid();
+ LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
+ if ((found != mLastWorst[id].end()) && (it == found->second)) {
+ mLastWorst[id].erase(found);
+ }
+ }
+
+ { // start of scope for pid found iterator
+ // element->getUid() may not be AID_SYSTEM for next-best-watermark.
+ // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
+ // long term code stability, find() check should be fast for those ids.
+ LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(element.getPid());
+ if (found != mLastWorstPidOfSystem[id].end() && it == found->second) {
+ mLastWorstPidOfSystem[id].erase(found);
+ }
+ }
+
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ LogBufferElementCollection::iterator bad = it;
+ int key =
+ (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->getTag() : element->getUid();
+#endif
+
+ if (coalesce) {
+ stats()->Erase(&element);
+ } else {
+ stats()->Subtract(&element);
+ }
+
+ it = SimpleLogBuffer::Erase(it);
+
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ log_id_for_each(i) {
+ for (auto b : mLastWorst[i]) {
+ if (bad == b.second) {
+ android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i, b.first, key);
+ }
+ }
+ for (auto b : mLastWorstPidOfSystem[i]) {
+ if (bad == b.second) {
+ android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i, b.first);
+ }
+ }
+ }
+#endif
+ return it;
+}
+
+// Define a temporary mechanism to report the last LogBufferElement pointer
+// for the specified uid, pid and tid. Used below to help merge-sort when
+// pruning for worst UID.
+class LogBufferElementLast {
+ typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
+ LogBufferElementMap map;
+
+ public:
+ bool coalesce(LogBufferElement* element, uint16_t dropped) {
+ uint64_t key = LogBufferElementKey(element->getUid(), element->getPid(), element->getTid());
+ LogBufferElementMap::iterator it = map.find(key);
+ if (it != map.end()) {
+ LogBufferElement* found = it->second;
+ uint16_t moreDropped = found->getDropped();
+ if ((dropped + moreDropped) > USHRT_MAX) {
+ map.erase(it);
+ } else {
+ found->setDropped(dropped + moreDropped);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void add(LogBufferElement* element) {
+ uint64_t key = LogBufferElementKey(element->getUid(), element->getPid(), element->getTid());
+ map[key] = element;
+ }
+
+ void clear() { map.clear(); }
+
+ void clear(LogBufferElement* element) {
+ uint64_t current = element->getRealTime().nsec() - (EXPIRE_RATELIMIT * NS_PER_SEC);
+ for (LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
+ LogBufferElement* mapElement = it->second;
+ if (mapElement->getDropped() >= EXPIRE_THRESHOLD &&
+ current > mapElement->getRealTime().nsec()) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ private:
+ uint64_t LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid) {
+ return uint64_t(uid) << 32 | uint64_t(pid) << 16 | uint64_t(tid);
+ }
+};
+
+// prune "pruneRows" of type "id" from the buffer.
+//
+// This garbage collection task is used to expire log entries. It is called to
+// remove all logs (clear), all UID logs (unprivileged clear), or every
+// 256 or 10% of the total logs (whichever is less) to prune the logs.
+//
+// First there is a prep phase where we discover the reader region lock that
+// acts as a backstop to any pruning activity to stop there and go no further.
+//
+// There are three major pruning loops that follow. All expire from the oldest
+// entries. Since there are multiple log buffers, the Android logging facility
+// will appear to drop entries 'in the middle' when looking at multiple log
+// sources and buffers. This effect is slightly more prominent when we prune
+// the worst offender by logging source. Thus the logs slowly loose content
+// and value as you move back in time. This is preferred since chatty sources
+// invariably move the logs value down faster as less chatty sources would be
+// expired in the noise.
+//
+// The first loop performs blacklisting and worst offender pruning. Falling
+// through when there are no notable worst offenders and have not hit the
+// region lock preventing further worst offender pruning. This loop also looks
+// after managing the chatty log entries and merging to help provide
+// statistical basis for blame. The chatty entries are not a notification of
+// how much logs you may have, but instead represent how much logs you would
+// have had in a virtual log buffer that is extended to cover all the in-memory
+// logs without loss. They last much longer than the represented pruned logs
+// since they get multiplied by the gains in the non-chatty log sources.
+//
+// The second loop get complicated because an algorithm of watermarks and
+// history is maintained to reduce the order and keep processing time
+// down to a minimum at scale. These algorithms can be costly in the face
+// of larger log buffers, or severly limited processing time granted to a
+// background task at lowest priority.
+//
+// This second loop does straight-up expiration from the end of the logs
+// (again, remember for the specified log buffer id) but does some whitelist
+// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
+// spam filtration all take priority. This second loop also checks if a region
+// lock is causing us to buffer too much in the logs to help the reader(s),
+// and will tell the slowest reader thread to skip log entries, and if
+// persistent and hits a further threshold, kill the reader thread.
+//
+// The third thread is optional, and only gets hit if there was a whitelist
+// and more needs to be pruned against the backstop of the region lock.
+//
+bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+ LogReaderThread* oldest = nullptr;
+ bool busy = false;
+ bool clearAll = pruneRows == ULONG_MAX;
+
+ auto reader_threads_lock = std::lock_guard{reader_list()->reader_threads_lock()};
+
+ // Region locked?
+ for (const auto& reader_thread : reader_list()->reader_threads()) {
+ if (!reader_thread->IsWatching(id)) {
+ continue;
+ }
+ if (!oldest || oldest->start() > reader_thread->start() ||
+ (oldest->start() == reader_thread->start() &&
+ reader_thread->deadline().time_since_epoch().count() != 0)) {
+ oldest = reader_thread.get();
+ }
+ }
+
+ LogBufferElementCollection::iterator it;
+
+ if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
+ // Only here if clear all request from non system source, so chatty
+ // filter logistics is not required.
+ it = GetOldest(id);
+ while (it != logs().end()) {
+ LogBufferElement& element = *it;
+
+ if (element.getLogId() != id || element.getUid() != caller_uid) {
+ ++it;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.getSequence()) {
+ busy = true;
+ KickReader(oldest, id, pruneRows);
+ break;
+ }
+
+ it = Erase(it);
+ if (--pruneRows == 0) {
+ break;
+ }
+ }
+ return busy;
+ }
+
+ // prune by worst offenders; by blacklist, UID, and by PID of system UID
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
+ while (!clearAll && (pruneRows > 0)) {
+ // recalculate the worst offender on every batched pass
+ int worst = -1; // not valid for getUid() or getKey()
+ size_t worst_sizes = 0;
+ size_t second_worst_sizes = 0;
+ pid_t worstPid = 0; // POSIX guarantees PID != 0
+
+ if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
+ // Calculate threshold as 12.5% of available storage
+ size_t threshold = max_size(id) / 8;
+
+ if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
+ stats()->WorstTwoTags(threshold, &worst, &worst_sizes, &second_worst_sizes);
+ // per-pid filter for AID_SYSTEM sources is too complex
+ } else {
+ stats()->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
+
+ if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
+ stats()->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
+ }
+ }
+ }
+
+ // skip if we have neither worst nor naughty filters
+ if ((worst == -1) && !hasBlacklist) {
+ break;
+ }
+
+ bool kick = false;
+ bool leading = true; // true if starting from the oldest log entry, false if starting from
+ // a specific chatty entry.
+ // Perform at least one mandatory garbage collection cycle in following
+ // - clear leading chatty tags
+ // - coalesce chatty tags
+ // - check age-out of preserved logs
+ bool gc = pruneRows <= 1;
+ if (!gc && (worst != -1)) {
+ { // begin scope for worst found iterator
+ LogBufferIteratorMap::iterator found = mLastWorst[id].find(worst);
+ if (found != mLastWorst[id].end() && found->second != logs().end()) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ if (worstPid) { // begin scope for pid worst found iterator
+ // FYI: worstPid only set if !LOG_ID_EVENTS and
+ // !LOG_ID_SECURITY, not going to make that assumption ...
+ LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(worstPid);
+ if (found != mLastWorstPidOfSystem[id].end() && found->second != logs().end()) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ }
+ if (leading) {
+ it = GetOldest(id);
+ }
+ static const log_time too_old{EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
+ LogBufferElementCollection::iterator lastt;
+ lastt = logs().end();
+ --lastt;
+ LogBufferElementLast last;
+ while (it != logs().end()) {
+ LogBufferElement& element = *it;
+
+ if (oldest && oldest->start() <= element.getSequence()) {
+ busy = true;
+ // Do not let chatty eliding trigger any reader mitigation
+ break;
+ }
+
+ if (element.getLogId() != id) {
+ ++it;
+ continue;
+ }
+ // below this point element->getLogId() == id
+
+ uint16_t dropped = element.getDropped();
+
+ // remove any leading drops
+ if (leading && dropped) {
+ it = Erase(it);
+ continue;
+ }
+
+ if (dropped && last.coalesce(&element, dropped)) {
+ it = Erase(it, true);
+ continue;
+ }
+
+ int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.getTag()
+ : element.getUid();
+
+ if (hasBlacklist && prune_->naughty(&element)) {
+ last.clear(&element);
+ it = Erase(it);
+ if (dropped) {
+ continue;
+ }
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ if (key == worst) {
+ kick = true;
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= element.getMsgLen();
+ }
+ continue;
+ }
+
+ if (element.getRealTime() < (lastt->getRealTime() - too_old) ||
+ element.getRealTime() > lastt->getRealTime()) {
+ break;
+ }
+
+ if (dropped) {
+ last.add(&element);
+ if (worstPid && ((!gc && element.getPid() == worstPid) ||
+ mLastWorstPidOfSystem[id].find(element.getPid()) ==
+ mLastWorstPidOfSystem[id].end())) {
+ // element->getUid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not LOG_ID_EVENTS
+ // or LOG_ID_SECURITY because of worstPid check.
+ mLastWorstPidOfSystem[id][element.getPid()] = it;
+ }
+ if ((!gc && !worstPid && (key == worst)) ||
+ (mLastWorst[id].find(key) == mLastWorst[id].end())) {
+ mLastWorst[id][key] = it;
+ }
+ ++it;
+ continue;
+ }
+
+ if (key != worst || (worstPid && element.getPid() != worstPid)) {
+ leading = false;
+ last.clear(&element);
+ ++it;
+ continue;
+ }
+ // key == worst below here
+ // If worstPid set, then element->getPid() == worstPid below here
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ kick = true;
+
+ uint16_t len = element.getMsgLen();
+
+ // do not create any leading drops
+ if (leading) {
+ it = Erase(it);
+ } else {
+ stats()->Drop(&element);
+ element.setDropped(1);
+ if (last.coalesce(&element, 1)) {
+ it = Erase(it, true);
+ } else {
+ last.add(&element);
+ if (worstPid && (!gc || mLastWorstPidOfSystem[id].find(worstPid) ==
+ mLastWorstPidOfSystem[id].end())) {
+ // element->getUid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not
+ // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
+ mLastWorstPidOfSystem[id][worstPid] = it;
+ }
+ if ((!gc && !worstPid) || mLastWorst[id].find(worst) == mLastWorst[id].end()) {
+ mLastWorst[id][worst] = it;
+ }
+ ++it;
+ }
+ }
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= len;
+ }
+ last.clear();
+
+ if (!kick || !prune_->worstUidEnabled()) {
+ break; // the following loop will ask bad clients to skip/drop
+ }
+ }
+
+ bool whitelist = false;
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
+ it = GetOldest(id);
+ while ((pruneRows > 0) && (it != logs().end())) {
+ LogBufferElement& element = *it;
+
+ if (element.getLogId() != id) {
+ it++;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.getSequence()) {
+ busy = true;
+ if (!whitelist) KickReader(oldest, id, pruneRows);
+ break;
+ }
+
+ if (hasWhitelist && !element.getDropped() && prune_->nice(&element)) {
+ // WhiteListed
+ whitelist = true;
+ it++;
+ continue;
+ }
+
+ it = Erase(it);
+ pruneRows--;
+ }
+
+ // Do not save the whitelist if we are reader range limited
+ if (whitelist && (pruneRows > 0)) {
+ it = GetOldest(id);
+ while ((it != logs().end()) && (pruneRows > 0)) {
+ LogBufferElement& element = *it;
+
+ if (element.getLogId() != id) {
+ ++it;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.getSequence()) {
+ busy = true;
+ KickReader(oldest, id, pruneRows);
+ break;
+ }
+
+ it = Erase(it);
+ pruneRows--;
+ }
+ }
+
+ return (pruneRows > 0) && busy;
+}
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
new file mode 100644
index 0000000..6f60272
--- /dev/null
+++ b/logd/ChattyLogBuffer.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012-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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include <android-base/thread_annotations.h>
+#include <android/log.h>
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
+#include "LogWriter.h"
+#include "SimpleLogBuffer.h"
+#include "rwlock.h"
+
+typedef std::list<LogBufferElement> LogBufferElementCollection;
+
+class ChattyLogBuffer : public SimpleLogBuffer {
+ // watermark of any worst/chatty uid processing
+ typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator> LogBufferIteratorMap;
+ LogBufferIteratorMap mLastWorst[LOG_ID_MAX] GUARDED_BY(lock_);
+ // watermark of any worst/chatty pid of system processing
+ typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator> LogBufferPidIteratorMap;
+ LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX] GUARDED_BY(lock_);
+
+ public:
+ ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+ LogStatistics* stats);
+ ~ChattyLogBuffer();
+
+ protected:
+ bool Prune(log_id_t id, unsigned long pruneRows, uid_t uid) REQUIRES(lock_) override;
+ void LogInternal(LogBufferElement&& elem) REQUIRES(lock_) override;
+
+ private:
+ LogBufferElementCollection::iterator Erase(LogBufferElementCollection::iterator it,
+ bool coalesce = false) REQUIRES(lock_);
+
+ PruneList* prune_;
+
+ // This always contains a copy of the last message logged, for deduplication.
+ std::optional<LogBufferElement> last_logged_elements_[LOG_ID_MAX] GUARDED_BY(lock_);
+ // This contains an element if duplicate messages are seen.
+ // Its `dropped` count is `duplicates seen - 1`.
+ std::optional<LogBufferElement> duplicate_elements_[LOG_ID_MAX] GUARDED_BY(lock_);
+};
diff --git a/logd/ChattyLogBufferTest.cpp b/logd/ChattyLogBufferTest.cpp
new file mode 100644
index 0000000..2e0c947
--- /dev/null
+++ b/logd/ChattyLogBufferTest.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 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 "LogBufferTest.h"
+
+class ChattyLogBufferTest : public LogBufferTest {};
+
+TEST_P(ChattyLogBufferTest, deduplication_simple) {
+ auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
+ bool regex = false) -> LogMessage {
+ logger_entry entry = {
+ .pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
+ std::string message;
+ message.push_back(ANDROID_LOG_INFO);
+ message.append(tag);
+ message.push_back('\0');
+ message.append(msg);
+ message.push_back('\0');
+ return {entry, message, regex};
+ };
+
+ // clang-format off
+ std::vector<LogMessage> log_messages = {
+ make_message(0, "test_tag", "duplicate"),
+ make_message(1, "test_tag", "duplicate"),
+ make_message(2, "test_tag", "not_same"),
+ make_message(3, "test_tag", "duplicate"),
+ make_message(4, "test_tag", "duplicate"),
+ make_message(5, "test_tag", "not_same"),
+ make_message(6, "test_tag", "duplicate"),
+ make_message(7, "test_tag", "duplicate"),
+ make_message(8, "test_tag", "duplicate"),
+ make_message(9, "test_tag", "not_same"),
+ make_message(10, "test_tag", "duplicate"),
+ make_message(11, "test_tag", "duplicate"),
+ make_message(12, "test_tag", "duplicate"),
+ make_message(13, "test_tag", "duplicate"),
+ make_message(14, "test_tag", "duplicate"),
+ make_message(15, "test_tag", "duplicate"),
+ make_message(16, "test_tag", "not_same"),
+ make_message(100, "test_tag", "duplicate"),
+ make_message(200, "test_tag", "duplicate"),
+ make_message(300, "test_tag", "duplicate"),
+ };
+ // clang-format on
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ log_buffer_->FlushTo(test_writer.get(), 1, nullptr, nullptr);
+
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(0, "test_tag", "duplicate"),
+ make_message(1, "test_tag", "duplicate"),
+ make_message(2, "test_tag", "not_same"),
+ make_message(3, "test_tag", "duplicate"),
+ make_message(4, "test_tag", "duplicate"),
+ make_message(5, "test_tag", "not_same"),
+ // 3 duplicate logs together print the first, a 1 count chatty message, then the last.
+ make_message(6, "test_tag", "duplicate"),
+ make_message(7, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ expire 1 line", true),
+ make_message(8, "test_tag", "duplicate"),
+ make_message(9, "test_tag", "not_same"),
+ // 6 duplicate logs together print the first, a 4 count chatty message, then the last.
+ make_message(10, "test_tag", "duplicate"),
+ make_message(14, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ expire 4 lines", true),
+ make_message(15, "test_tag", "duplicate"),
+ make_message(16, "test_tag", "not_same"),
+ // duplicate logs > 1 minute apart are not deduplicated.
+ make_message(100, "test_tag", "duplicate"),
+ make_message(200, "test_tag", "duplicate"),
+ make_message(300, "test_tag", "duplicate"),
+ };
+ FixupMessages(&expected_log_messages);
+ CompareLogMessages(expected_log_messages, read_log_messages);
+};
+
+TEST_P(ChattyLogBufferTest, deduplication_overflow) {
+ auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
+ bool regex = false) -> LogMessage {
+ logger_entry entry = {
+ .pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
+ std::string message;
+ message.push_back(ANDROID_LOG_INFO);
+ message.append(tag);
+ message.push_back('\0');
+ message.append(msg);
+ message.push_back('\0');
+ return {entry, message, regex};
+ };
+
+ uint32_t sec = 0;
+ std::vector<LogMessage> log_messages = {
+ make_message(sec++, "test_tag", "normal"),
+ };
+ size_t expired_per_chatty_message = std::numeric_limits<uint16_t>::max();
+ for (size_t i = 0; i < expired_per_chatty_message + 3; ++i) {
+ log_messages.emplace_back(make_message(sec++, "test_tag", "duplicate"));
+ }
+ log_messages.emplace_back(make_message(sec++, "test_tag", "normal"));
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ log_buffer_->FlushTo(test_writer.get(), 1, nullptr, nullptr);
+
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(0, "test_tag", "normal"),
+ make_message(1, "test_tag", "duplicate"),
+ make_message(expired_per_chatty_message + 1, "chatty",
+ "uid=0\\([^\\)]+\\) [^ ]+ expire 65535 lines", true),
+ make_message(expired_per_chatty_message + 2, "chatty",
+ "uid=0\\([^\\)]+\\) [^ ]+ expire 1 line", true),
+ make_message(expired_per_chatty_message + 3, "test_tag", "duplicate"),
+ make_message(expired_per_chatty_message + 4, "test_tag", "normal"),
+ };
+ FixupMessages(&expected_log_messages);
+ CompareLogMessages(expected_log_messages, read_log_messages);
+}
+
+TEST_P(ChattyLogBufferTest, deduplication_liblog) {
+ auto make_message = [&](uint32_t sec, int32_t tag, int32_t count) -> LogMessage {
+ logger_entry entry = {
+ .pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_EVENTS, .uid = 0};
+ android_log_event_int_t liblog_event = {
+ .header.tag = tag, .payload.type = EVENT_TYPE_INT, .payload.data = count};
+ return {entry, std::string(reinterpret_cast<char*>(&liblog_event), sizeof(liblog_event)),
+ false};
+ };
+
+ // LIBLOG_LOG_TAG
+ std::vector<LogMessage> log_messages = {
+ make_message(0, 1234, 1),
+ make_message(1, LIBLOG_LOG_TAG, 3),
+ make_message(2, 1234, 2),
+ make_message(3, LIBLOG_LOG_TAG, 3),
+ make_message(4, LIBLOG_LOG_TAG, 4),
+ make_message(5, 1234, 223),
+ make_message(6, LIBLOG_LOG_TAG, 2),
+ make_message(7, LIBLOG_LOG_TAG, 3),
+ make_message(8, LIBLOG_LOG_TAG, 4),
+ make_message(9, 1234, 227),
+ make_message(10, LIBLOG_LOG_TAG, 1),
+ make_message(11, LIBLOG_LOG_TAG, 3),
+ make_message(12, LIBLOG_LOG_TAG, 2),
+ make_message(13, LIBLOG_LOG_TAG, 3),
+ make_message(14, LIBLOG_LOG_TAG, 5),
+ make_message(15, 1234, 227),
+ make_message(16, LIBLOG_LOG_TAG, 2),
+ make_message(17, LIBLOG_LOG_TAG, std::numeric_limits<int32_t>::max()),
+ make_message(18, LIBLOG_LOG_TAG, 3),
+ make_message(19, LIBLOG_LOG_TAG, 5),
+ make_message(20, 1234, 227),
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ log_buffer_->FlushTo(test_writer.get(), 1, nullptr, nullptr);
+
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(0, 1234, 1),
+ make_message(1, LIBLOG_LOG_TAG, 3),
+ make_message(2, 1234, 2),
+ make_message(3, LIBLOG_LOG_TAG, 3),
+ make_message(4, LIBLOG_LOG_TAG, 4),
+ make_message(5, 1234, 223),
+ // More than 2 liblog events (3 here), sum their value into the third message.
+ make_message(6, LIBLOG_LOG_TAG, 2),
+ make_message(8, LIBLOG_LOG_TAG, 7),
+ make_message(9, 1234, 227),
+ // More than 2 liblog events (5 here), sum their value into the third message.
+ make_message(10, LIBLOG_LOG_TAG, 1),
+ make_message(14, LIBLOG_LOG_TAG, 13),
+ make_message(15, 1234, 227),
+ // int32_t max is the max for a chatty message, beyond that we must use new messages.
+ make_message(16, LIBLOG_LOG_TAG, 2),
+ make_message(17, LIBLOG_LOG_TAG, std::numeric_limits<int32_t>::max()),
+ make_message(19, LIBLOG_LOG_TAG, 8),
+ make_message(20, 1234, 227),
+ };
+ FixupMessages(&expected_log_messages);
+ CompareLogMessages(expected_log_messages, read_log_messages);
+};
+
+INSTANTIATE_TEST_CASE_P(ChattyLogBufferTests, ChattyLogBufferTest, testing::Values("chatty"));
\ No newline at end of file
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 4044dc9..c6ab22d 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "CommandListener.h"
+
#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
@@ -35,12 +37,11 @@
#include <private/android_filesystem_config.h>
#include <sysutils/SocketClient.h>
-#include "CommandListener.h"
-#include "LogCommand.h"
-#include "LogUtils.h"
+#include "LogPermissions.h"
-CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
- : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
+ LogStatistics* stats)
+ : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune), stats_(stats) {
registerCmd(new ClearCmd(this));
registerCmd(new GetBufSizeCmd(this));
registerCmd(new SetBufSizeCmd(this));
@@ -80,7 +81,7 @@
return 0;
}
- cli->sendMsg(buf()->clear((log_id_t)id, uid) ? "busy" : "success");
+ cli->sendMsg(buf()->Clear((log_id_t)id, uid) ? "busy" : "success");
return 0;
}
@@ -98,7 +99,7 @@
return 0;
}
- unsigned long size = buf()->getSize((log_id_t)id);
+ unsigned long size = buf()->GetSize((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
@@ -125,7 +126,7 @@
}
unsigned long size = atol(argv[2]);
- if (buf()->setSize((log_id_t)id, size)) {
+ if (buf()->SetSize((log_id_t)id, size)) {
cli->sendMsg("Range Error");
return 0;
}
@@ -148,7 +149,7 @@
return 0;
}
- unsigned long size = buf()->getSizeUsed((log_id_t)id);
+ unsigned long size = stats()->Sizes((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
@@ -209,7 +210,7 @@
}
}
- cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
+ cli->sendMsg(PackageString(stats()->Format(uid, pid, logMask)).c_str());
return 0;
}
@@ -298,7 +299,7 @@
setname();
android::prdebug("logd reinit");
- buf()->init();
+ buf()->Init();
prune()->init(nullptr);
// This only works on userdebug and eng devices to re-read the
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index c90c247..a55a393 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -16,18 +16,18 @@
#pragma once
+#include <sysutils/FrameworkCommand.h>
#include <sysutils/FrameworkListener.h>
#include "LogBuffer.h"
-#include "LogCommand.h"
#include "LogListener.h"
-#include "LogReader.h"
+#include "LogStatistics.h"
#include "LogTags.h"
#include "LogWhiteBlackList.h"
class CommandListener : public FrameworkListener {
public:
- CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
+ CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune, LogStatistics* log_statistics);
virtual ~CommandListener() {}
private:
@@ -36,20 +36,22 @@
LogBuffer* buf_;
LogTags* tags_;
PruneList* prune_;
+ LogStatistics* stats_;
-#define LogCmd(name, command_string) \
- class name##Cmd : public LogCommand { \
- public: \
- explicit name##Cmd(CommandListener* parent) \
- : LogCommand(#command_string), parent_(parent) {} \
- virtual ~name##Cmd() {} \
- int runCommand(SocketClient* c, int argc, char** argv); \
- \
- private: \
- LogBuffer* buf() const { return parent_->buf_; } \
- LogTags* tags() const { return parent_->tags_; } \
- PruneList* prune() const { return parent_->prune_; } \
- CommandListener* parent_; \
+#define LogCmd(name, command_string) \
+ class name##Cmd : public FrameworkCommand { \
+ public: \
+ explicit name##Cmd(CommandListener* parent) \
+ : FrameworkCommand(#command_string), parent_(parent) {} \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient* c, int argc, char** argv); \
+ \
+ private: \
+ LogBuffer* buf() const { return parent_->buf_; } \
+ LogTags* tags() const { return parent_->tags_; } \
+ PruneList* prune() const { return parent_->prune_; } \
+ LogStatistics* stats() const { return parent_->stats_; } \
+ CommandListener* parent_; \
}
LogCmd(Clear, clear);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index d9cc0db..0ce9796 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogAudit.h"
+
#include <ctype.h>
#include <endian.h>
#include <errno.h>
@@ -34,10 +36,7 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "LogAudit.h"
-#include "LogBuffer.h"
#include "LogKlog.h"
-#include "LogReader.h"
#include "LogUtils.h"
#include "libaudit.h"
@@ -45,16 +44,14 @@
'<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
-LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg)
+LogAudit::LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats)
: SocketListener(getLogSocket(), false),
logbuf(buf),
- reader(reader),
fdDmesg(fdDmesg),
- main(__android_logger_property_get_bool("ro.logd.auditd.main",
- BOOL_DEFAULT_TRUE)),
- events(__android_logger_property_get_bool("ro.logd.auditd.events",
- BOOL_DEFAULT_TRUE)),
- initialized(false) {
+ main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
+ events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
+ initialized(false),
+ stats_(stats) {
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
'l',
'o',
@@ -211,9 +208,7 @@
++cp;
}
tid = pid;
- logbuf->wrlock();
- uid = logbuf->pidToUid(pid);
- logbuf->unlock();
+ uid = stats_->PidToUid(pid);
memmove(pidptr, cp, strlen(cp) + 1);
}
@@ -247,22 +242,10 @@
static const char audit_str[] = " audit(";
char* timeptr = strstr(str, audit_str);
- if (timeptr &&
- ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
+ if (timeptr && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
(*cp == ':')) {
memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
- if (!isMonotonic()) {
- if (android::isMonotonic(now)) {
- LogKlog::convertMonotonicToReal(now);
- }
- } else {
- if (!android::isMonotonic(now)) {
- LogKlog::convertRealToMonotonic(now);
- }
- }
- } else if (isMonotonic()) {
- now = log_time(CLOCK_MONOTONIC);
} else {
now = log_time(CLOCK_REALTIME);
}
@@ -277,7 +260,7 @@
: LOGGER_ENTRY_MAX_PAYLOAD;
size_t message_len = str_len + sizeof(android_log_event_string_t);
- log_mask_t notify = 0;
+ unsigned int notify = 0;
if (events) { // begin scope for event buffer
uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
@@ -291,9 +274,8 @@
memcpy(event->data + str_len - denial_metadata.length(),
denial_metadata.c_str(), denial_metadata.length());
- rc = logbuf->log(
- LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
- (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+ rc = logbuf->Log(LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+ (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
if (rc >= 0) {
notify |= 1 << LOG_ID_EVENTS;
}
@@ -313,9 +295,7 @@
pid = tid;
comm = "auditd";
} else {
- logbuf->wrlock();
- comm = commfree = logbuf->pidToName(pid);
- logbuf->unlock();
+ comm = commfree = stats_->PidToName(pid);
if (!comm) {
comm = "unknown";
}
@@ -347,9 +327,8 @@
strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
denial_metadata.c_str(), denial_metadata.length());
- rc = logbuf->log(
- LOG_ID_MAIN, now, uid, pid, tid, newstr,
- (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+ rc = logbuf->Log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
+ (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
if (rc >= 0) {
notify |= 1 << LOG_ID_MAIN;
@@ -361,7 +340,6 @@
free(str);
if (notify) {
- reader->notifyNewLog(notify);
if (rc < 0) {
rc = message_len;
}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index c3d7a3e..181920e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -14,36 +14,30 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_AUDIT_H__
-#define _LOGD_LOG_AUDIT_H__
+#pragma once
#include <map>
#include <sysutils/SocketListener.h>
#include "LogBuffer.h"
-
-class LogReader;
+#include "LogStatistics.h"
class LogAudit : public SocketListener {
LogBuffer* logbuf;
- LogReader* reader;
int fdDmesg; // fdDmesg >= 0 is functionally bool dmesg
bool main;
bool events;
bool initialized;
- public:
- LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
+ public:
+ LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats);
int log(char* buf, size_t len);
- bool isMonotonic() {
- return logbuf->isMonotonic();
- }
- protected:
+ protected:
virtual bool onDataAvailable(SocketClient* cli);
- private:
+ private:
static int getLogSocket();
std::map<std::string, std::string> populateDenialMap();
std::string denialParse(const std::string& denial, char terminator,
@@ -51,6 +45,6 @@
void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
int logPrint(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)));
-};
-#endif
+ LogStatistics* stats_;
+};
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
deleted file mode 100644
index a3e4e09..0000000
--- a/logd/LogBuffer.cpp
+++ /dev/null
@@ -1,1120 +0,0 @@
-/*
- * Copyright (C) 2012-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.
- */
-// for manual checking of stale entries during LogBuffer::erase()
-//#define DEBUG_CHECK_FOR_STALE_ENTRIES
-
-#include <ctype.h>
-#include <endian.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/user.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <unordered_map>
-#include <utility>
-
-#include <cutils/properties.h>
-#include <private/android_logger.h>
-
-#include "LogBuffer.h"
-#include "LogKlog.h"
-#include "LogReader.h"
-#include "LogUtils.h"
-
-#ifndef __predict_false
-#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
-#endif
-
-// Default
-#define log_buffer_size(id) mMaxSize[id]
-
-void LogBuffer::init() {
- log_id_for_each(i) {
- if (setSize(i, __android_logger_get_buffer_size(i))) {
- setSize(i, LOG_BUFFER_MIN_SIZE);
- }
- }
- bool lastMonotonic = monotonic;
- monotonic = android_log_clockid() == CLOCK_MONOTONIC;
- if (lastMonotonic != monotonic) {
- //
- // Fixup all timestamps, may not be 100% accurate, but better than
- // throwing what we have away when we get 'surprised' by a change.
- // In-place element fixup so no need to check reader-lock. Entries
- // should already be in timestamp order, but we could end up with a
- // few out-of-order entries if new monotonics come in before we
- // are notified of the reinit change in status. A Typical example would
- // be:
- // --------- beginning of system
- // 10.494082 184 201 D Cryptfs : Just triggered post_fs_data
- // --------- beginning of kernel
- // 0.000000 0 0 I : Initializing cgroup subsys
- // as the act of mounting /data would trigger persist.logd.timestamp to
- // be corrected. 1/30 corner case YMMV.
- //
- rdlock();
- LogBufferElementCollection::iterator it = mLogElements.begin();
- while ((it != mLogElements.end())) {
- LogBufferElement* e = *it;
- if (monotonic) {
- if (!android::isMonotonic(e->mRealTime)) {
- LogKlog::convertRealToMonotonic(e->mRealTime);
- if ((e->mRealTime.tv_nsec % 1000) == 0) {
- e->mRealTime.tv_nsec++;
- }
- }
- } else {
- if (android::isMonotonic(e->mRealTime)) {
- LogKlog::convertMonotonicToReal(e->mRealTime);
- if ((e->mRealTime.tv_nsec % 1000) == 0) {
- e->mRealTime.tv_nsec++;
- }
- }
- }
- ++it;
- }
- unlock();
- }
-
- // Release any sleeping reader threads to dump their current content.
- LogTimeEntry::wrlock();
-
- LastLogTimes::iterator times = mTimes.begin();
- while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
- entry->triggerReader_Locked();
- times++;
- }
-
- LogTimeEntry::unlock();
-}
-
-LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
- : monotonic(android_log_clockid() == CLOCK_MONOTONIC),
- mTimes(*times),
- tags_(tags),
- prune_(prune) {
- pthread_rwlock_init(&mLogElementsLock, nullptr);
-
- log_id_for_each(i) {
- lastLoggedElements[i] = nullptr;
- droppedElements[i] = nullptr;
- }
-
- init();
-}
-
-LogBuffer::~LogBuffer() {
- log_id_for_each(i) {
- delete lastLoggedElements[i];
- delete droppedElements[i];
- }
-}
-
-LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
- auto it = mLogElements.begin();
- if (oldest_[log_id]) {
- it = *oldest_[log_id];
- }
- while (it != mLogElements.end() && (*it)->getLogId() != log_id) {
- it++;
- }
- if (it != mLogElements.end()) {
- oldest_[log_id] = it;
- }
- return it;
-}
-
-enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
-
-static enum match_type identical(LogBufferElement* elem,
- LogBufferElement* last) {
- // is it mostly identical?
- // if (!elem) return DIFFERENT;
- ssize_t lenl = elem->getMsgLen();
- if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
- // if (!last) return DIFFERENT;
- ssize_t lenr = last->getMsgLen();
- if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
- // if (elem->getLogId() != last->getLogId()) return DIFFERENT;
- if (elem->getUid() != last->getUid()) return DIFFERENT;
- if (elem->getPid() != last->getPid()) return DIFFERENT;
- if (elem->getTid() != last->getTid()) return DIFFERENT;
-
- // last is more than a minute old, stop squashing identical messages
- if (elem->getRealTime().nsec() >
- (last->getRealTime().nsec() + 60 * NS_PER_SEC))
- return DIFFERENT;
-
- // Identical message
- const char* msgl = elem->getMsg();
- const char* msgr = last->getMsg();
- if (lenl == lenr) {
- if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
- // liblog tagged messages (content gets summed)
- if ((elem->getLogId() == LOG_ID_EVENTS) &&
- (lenl == sizeof(android_log_event_int_t)) &&
- !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
- sizeof(int32_t)) &&
- (elem->getTag() == LIBLOG_LOG_TAG)) {
- return SAME_LIBLOG;
- }
- }
-
- // audit message (except sequence number) identical?
- if (last->isBinary() &&
- (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
- (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
- if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
- sizeof(int32_t))) {
- return DIFFERENT;
- }
- msgl += sizeof(android_log_event_string_t);
- lenl -= sizeof(android_log_event_string_t);
- msgr += sizeof(android_log_event_string_t);
- lenr -= sizeof(android_log_event_string_t);
- }
- static const char avc[] = "): avc: ";
- const char* avcl = android::strnstr(msgl, lenl, avc);
- if (!avcl) return DIFFERENT;
- lenl -= avcl - msgl;
- const char* avcr = android::strnstr(msgr, lenr, avc);
- if (!avcr) return DIFFERENT;
- lenr -= avcr - msgr;
- if (lenl != lenr) return DIFFERENT;
- if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
- lenl - strlen(avc))) {
- return DIFFERENT;
- }
- return SAME;
-}
-
-int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
- pid_t tid, const char* msg, uint16_t len) {
- if (log_id >= LOG_ID_MAX) {
- return -EINVAL;
- }
-
- // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
- // This prevents any chance that an outside source can request an
- // exact entry with time specified in ms or us precision.
- if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
-
- LogBufferElement* elem = new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
-
- // b/137093665: don't coalesce security messages.
- if (log_id == LOG_ID_SECURITY) {
- wrlock();
- log(elem);
- unlock();
-
- return len;
- }
-
- int prio = ANDROID_LOG_INFO;
- const char* tag = nullptr;
- size_t tag_len = 0;
- if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
- tag = tags_->tagToName(elem->getTag());
- if (tag) {
- tag_len = strlen(tag);
- }
- } else {
- prio = *msg;
- tag = msg + 1;
- tag_len = strnlen(tag, len - 1);
- }
- if (!__android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE)) {
- // Log traffic received to total
- wrlock();
- stats.addTotal(elem);
- unlock();
- delete elem;
- return -EACCES;
- }
-
- wrlock();
- LogBufferElement* currentLast = lastLoggedElements[log_id];
- if (currentLast) {
- LogBufferElement* dropped = droppedElements[log_id];
- uint16_t count = dropped ? dropped->getDropped() : 0;
- //
- // State Init
- // incoming:
- // dropped = nullptr
- // currentLast = nullptr;
- // elem = incoming message
- // outgoing:
- // dropped = nullptr -> State 0
- // currentLast = copy of elem
- // log elem
- // State 0
- // incoming:
- // count = 0
- // dropped = nullptr
- // currentLast = copy of last message
- // elem = incoming message
- // outgoing: if match != DIFFERENT
- // dropped = copy of first identical message -> State 1
- // currentLast = reference to elem
- // break: if match == DIFFERENT
- // dropped = nullptr -> State 0
- // delete copy of last message (incoming currentLast)
- // currentLast = copy of elem
- // log elem
- // State 1
- // incoming:
- // count = 0
- // dropped = copy of first identical message
- // currentLast = reference to last held-back incoming
- // message
- // elem = incoming message
- // outgoing: if match == SAME
- // delete copy of first identical message (dropped)
- // dropped = reference to last held-back incoming
- // message set to chatty count of 1 -> State 2
- // currentLast = reference to elem
- // outgoing: if match == SAME_LIBLOG
- // dropped = copy of first identical message -> State 1
- // take sum of currentLast and elem
- // if sum overflows:
- // log currentLast
- // currentLast = reference to elem
- // else
- // delete currentLast
- // currentLast = reference to elem, sum liblog.
- // break: if match == DIFFERENT
- // delete dropped
- // dropped = nullptr -> State 0
- // log reference to last held-back (currentLast)
- // currentLast = copy of elem
- // log elem
- // State 2
- // incoming:
- // count = chatty count
- // dropped = chatty message holding count
- // currentLast = reference to last held-back incoming
- // message.
- // dropped = chatty message holding count
- // elem = incoming message
- // outgoing: if match != DIFFERENT
- // delete chatty message holding count
- // dropped = reference to last held-back incoming
- // message, set to chatty count + 1
- // currentLast = reference to elem
- // break: if match == DIFFERENT
- // log dropped (chatty message)
- // dropped = nullptr -> State 0
- // log reference to last held-back (currentLast)
- // currentLast = copy of elem
- // log elem
- //
- enum match_type match = identical(elem, currentLast);
- if (match != DIFFERENT) {
- if (dropped) {
- // Sum up liblog tag messages?
- if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
- android_log_event_int_t* event =
- reinterpret_cast<android_log_event_int_t*>(
- const_cast<char*>(currentLast->getMsg()));
- //
- // To unit test, differentiate with something like:
- // event->header.tag = htole32(CHATTY_LOG_TAG);
- // here, then instead of delete currentLast below,
- // log(currentLast) to see the incremental sums form.
- //
- uint32_t swab = event->payload.data;
- unsigned long long total = htole32(swab);
- event = reinterpret_cast<android_log_event_int_t*>(
- const_cast<char*>(elem->getMsg()));
- swab = event->payload.data;
-
- lastLoggedElements[LOG_ID_EVENTS] = elem;
- total += htole32(swab);
- // check for overflow
- if (total >= UINT32_MAX) {
- log(currentLast);
- unlock();
- return len;
- }
- stats.addTotal(currentLast);
- delete currentLast;
- swab = total;
- event->payload.data = htole32(swab);
- unlock();
- return len;
- }
- if (count == USHRT_MAX) {
- log(dropped);
- count = 1;
- } else {
- delete dropped;
- ++count;
- }
- }
- if (count) {
- stats.addTotal(currentLast);
- currentLast->setDropped(count);
- }
- droppedElements[log_id] = currentLast;
- lastLoggedElements[log_id] = elem;
- unlock();
- return len;
- }
- if (dropped) { // State 1 or 2
- if (count) { // State 2
- log(dropped); // report chatty
- } else { // State 1
- delete dropped;
- }
- droppedElements[log_id] = nullptr;
- log(currentLast); // report last message in the series
- } else { // State 0
- delete currentLast;
- }
- }
- lastLoggedElements[log_id] = new LogBufferElement(*elem);
-
- log(elem);
- unlock();
-
- return len;
-}
-
-// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
-void LogBuffer::log(LogBufferElement* elem) {
- mLogElements.push_back(elem);
- stats.add(elem);
- maybePrune(elem->getLogId());
-}
-
-// Prune at most 10% of the log entries or maxPrune, whichever is less.
-//
-// LogBuffer::wrlock() must be held when this function is called.
-void LogBuffer::maybePrune(log_id_t id) {
- size_t sizes = stats.sizes(id);
- unsigned long maxSize = log_buffer_size(id);
- if (sizes > maxSize) {
- size_t sizeOver = sizes - ((maxSize * 9) / 10);
- size_t elements = stats.realElements(id);
- size_t minElements = elements / 100;
- if (minElements < minPrune) {
- minElements = minPrune;
- }
- unsigned long pruneRows = elements * sizeOver / sizes;
- if (pruneRows < minElements) {
- pruneRows = minElements;
- }
- if (pruneRows > maxPrune) {
- pruneRows = maxPrune;
- }
- prune(id, pruneRows);
- }
-}
-
-LogBufferElementCollection::iterator LogBuffer::erase(
- LogBufferElementCollection::iterator it, bool coalesce) {
- LogBufferElement* element = *it;
- log_id_t id = element->getLogId();
-
- // Remove iterator references in the various lists that will become stale
- // after the element is erased from the main logging list.
-
- { // start of scope for found iterator
- int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
- ? element->getTag()
- : element->getUid();
- LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
- if ((found != mLastWorst[id].end()) && (it == found->second)) {
- mLastWorst[id].erase(found);
- }
- }
-
- { // start of scope for pid found iterator
- // element->getUid() may not be AID_SYSTEM for next-best-watermark.
- // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
- // long term code stability, find() check should be fast for those ids.
- LogBufferPidIteratorMap::iterator found =
- mLastWorstPidOfSystem[id].find(element->getPid());
- if ((found != mLastWorstPidOfSystem[id].end()) &&
- (it == found->second)) {
- mLastWorstPidOfSystem[id].erase(found);
- }
- }
-
- bool setLast[LOG_ID_MAX];
- bool doSetLast = false;
- log_id_for_each(i) { doSetLast |= setLast[i] = oldest_[i] && it == *oldest_[i]; }
-#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
- LogBufferElementCollection::iterator bad = it;
- int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
- ? element->getTag()
- : element->getUid();
-#endif
- it = mLogElements.erase(it);
- if (doSetLast) {
- log_id_for_each(i) {
- if (setLast[i]) {
- if (__predict_false(it == mLogElements.end())) {
- oldest_[i] = std::nullopt;
- } else {
- oldest_[i] = it; // Store the next iterator even if it does not correspond to
- // the same log_id, as a starting point for GetOldest().
- }
- }
- }
- }
-#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
- log_id_for_each(i) {
- for (auto b : mLastWorst[i]) {
- if (bad == b.second) {
- android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i,
- b.first, key);
- }
- }
- for (auto b : mLastWorstPidOfSystem[i]) {
- if (bad == b.second) {
- android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i,
- b.first);
- }
- }
- }
-#endif
- if (coalesce) {
- stats.erase(element);
- } else {
- stats.subtract(element);
- }
- delete element;
-
- return it;
-}
-
-// Define a temporary mechanism to report the last LogBufferElement pointer
-// for the specified uid, pid and tid. Used below to help merge-sort when
-// pruning for worst UID.
-class LogBufferElementKey {
- const union {
- struct {
- uint32_t uid;
- uint16_t pid;
- uint16_t tid;
- } __packed;
- uint64_t value;
- } __packed;
-
- public:
- LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid)
- : uid(uid), pid(pid), tid(tid) {
- }
- explicit LogBufferElementKey(uint64_t key) : value(key) {
- }
-
- uint64_t getKey() {
- return value;
- }
-};
-
-class LogBufferElementLast {
- typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
- LogBufferElementMap map;
-
- public:
- bool coalesce(LogBufferElement* element, uint16_t dropped) {
- LogBufferElementKey key(element->getUid(), element->getPid(),
- element->getTid());
- LogBufferElementMap::iterator it = map.find(key.getKey());
- if (it != map.end()) {
- LogBufferElement* found = it->second;
- uint16_t moreDropped = found->getDropped();
- if ((dropped + moreDropped) > USHRT_MAX) {
- map.erase(it);
- } else {
- found->setDropped(dropped + moreDropped);
- return true;
- }
- }
- return false;
- }
-
- void add(LogBufferElement* element) {
- LogBufferElementKey key(element->getUid(), element->getPid(),
- element->getTid());
- map[key.getKey()] = element;
- }
-
- inline void clear() {
- map.clear();
- }
-
- void clear(LogBufferElement* element) {
- uint64_t current = element->getRealTime().nsec() - (EXPIRE_RATELIMIT * NS_PER_SEC);
- for (LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
- LogBufferElement* mapElement = it->second;
- if (mapElement->getDropped() >= EXPIRE_THRESHOLD &&
- current > mapElement->getRealTime().nsec()) {
- it = map.erase(it);
- } else {
- ++it;
- }
- }
- }
-};
-
-// If the selected reader is blocking our pruning progress, decide on
-// what kind of mitigation is necessary to unblock the situation.
-void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
- if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
- // A misbehaving or slow reader has its connection
- // dropped if we hit too much memory pressure.
- android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
- me->mClient->getPid());
- me->release_Locked();
- } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
- // Allow a blocked WRAP timeout reader to
- // trigger and start reporting the log data.
- me->triggerReader_Locked();
- } else {
- // tell slow reader to skip entries to catch up
- android::prdebug(
- "Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
- pruneRows, me->mClient->getPid());
- me->triggerSkip_Locked(id, pruneRows);
- }
-}
-
-// prune "pruneRows" of type "id" from the buffer.
-//
-// This garbage collection task is used to expire log entries. It is called to
-// remove all logs (clear), all UID logs (unprivileged clear), or every
-// 256 or 10% of the total logs (whichever is less) to prune the logs.
-//
-// First there is a prep phase where we discover the reader region lock that
-// acts as a backstop to any pruning activity to stop there and go no further.
-//
-// There are three major pruning loops that follow. All expire from the oldest
-// entries. Since there are multiple log buffers, the Android logging facility
-// will appear to drop entries 'in the middle' when looking at multiple log
-// sources and buffers. This effect is slightly more prominent when we prune
-// the worst offender by logging source. Thus the logs slowly loose content
-// and value as you move back in time. This is preferred since chatty sources
-// invariably move the logs value down faster as less chatty sources would be
-// expired in the noise.
-//
-// The first loop performs blacklisting and worst offender pruning. Falling
-// through when there are no notable worst offenders and have not hit the
-// region lock preventing further worst offender pruning. This loop also looks
-// after managing the chatty log entries and merging to help provide
-// statistical basis for blame. The chatty entries are not a notification of
-// how much logs you may have, but instead represent how much logs you would
-// have had in a virtual log buffer that is extended to cover all the in-memory
-// logs without loss. They last much longer than the represented pruned logs
-// since they get multiplied by the gains in the non-chatty log sources.
-//
-// The second loop get complicated because an algorithm of watermarks and
-// history is maintained to reduce the order and keep processing time
-// down to a minimum at scale. These algorithms can be costly in the face
-// of larger log buffers, or severly limited processing time granted to a
-// background task at lowest priority.
-//
-// This second loop does straight-up expiration from the end of the logs
-// (again, remember for the specified log buffer id) but does some whitelist
-// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
-// spam filtration all take priority. This second loop also checks if a region
-// lock is causing us to buffer too much in the logs to help the reader(s),
-// and will tell the slowest reader thread to skip log entries, and if
-// persistent and hits a further threshold, kill the reader thread.
-//
-// The third thread is optional, and only gets hit if there was a whitelist
-// and more needs to be pruned against the backstop of the region lock.
-//
-// LogBuffer::wrlock() must be held when this function is called.
-//
-bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
- LogTimeEntry* oldest = nullptr;
- bool busy = false;
- bool clearAll = pruneRows == ULONG_MAX;
-
- LogTimeEntry::rdlock();
-
- // Region locked?
- LastLogTimes::iterator times = mTimes.begin();
- while (times != mTimes.end()) {
- 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)))) {
- oldest = entry;
- }
- times++;
- }
-
- LogBufferElementCollection::iterator it;
-
- if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
- // Only here if clear all request from non system source, so chatty
- // filter logistics is not required.
- it = GetOldest(id);
- while (it != mLogElements.end()) {
- LogBufferElement* element = *it;
-
- if ((element->getLogId() != id) ||
- (element->getUid() != caller_uid)) {
- ++it;
- continue;
- }
-
- if (oldest && oldest->mStart <= element->getSequence()) {
- busy = true;
- kickMe(oldest, id, pruneRows);
- break;
- }
-
- it = erase(it);
- if (--pruneRows == 0) {
- break;
- }
- }
- LogTimeEntry::unlock();
- return busy;
- }
-
- // prune by worst offenders; by blacklist, UID, and by PID of system UID
- bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
- while (!clearAll && (pruneRows > 0)) {
- // recalculate the worst offender on every batched pass
- int worst = -1; // not valid for getUid() or getKey()
- size_t worst_sizes = 0;
- size_t second_worst_sizes = 0;
- pid_t worstPid = 0; // POSIX guarantees PID != 0
-
- if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
- // Calculate threshold as 12.5% of available storage
- size_t threshold = log_buffer_size(id) / 8;
-
- if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
- stats.sortTags(AID_ROOT, (pid_t)0, 2, id)
- .findWorst(worst, worst_sizes, second_worst_sizes,
- threshold);
- // per-pid filter for AID_SYSTEM sources is too complex
- } else {
- stats.sort(AID_ROOT, (pid_t)0, 2, id)
- .findWorst(worst, worst_sizes, second_worst_sizes,
- threshold);
-
- if ((worst == AID_SYSTEM) && prune_->worstPidOfSystemEnabled()) {
- stats.sortPids(worst, (pid_t)0, 2, id)
- .findWorst(worstPid, worst_sizes, second_worst_sizes);
- }
- }
- }
-
- // skip if we have neither worst nor naughty filters
- if ((worst == -1) && !hasBlacklist) {
- break;
- }
-
- bool kick = false;
- bool leading = true; // true if starting from the oldest log entry, false if starting from
- // a specific chatty entry.
- // Perform at least one mandatory garbage collection cycle in following
- // - clear leading chatty tags
- // - coalesce chatty tags
- // - check age-out of preserved logs
- bool gc = pruneRows <= 1;
- if (!gc && (worst != -1)) {
- { // begin scope for worst found iterator
- LogBufferIteratorMap::iterator found =
- mLastWorst[id].find(worst);
- if ((found != mLastWorst[id].end()) &&
- (found->second != mLogElements.end())) {
- leading = false;
- it = found->second;
- }
- }
- if (worstPid) { // begin scope for pid worst found iterator
- // FYI: worstPid only set if !LOG_ID_EVENTS and
- // !LOG_ID_SECURITY, not going to make that assumption ...
- LogBufferPidIteratorMap::iterator found =
- mLastWorstPidOfSystem[id].find(worstPid);
- if ((found != mLastWorstPidOfSystem[id].end()) &&
- (found->second != mLogElements.end())) {
- leading = false;
- it = found->second;
- }
- }
- }
- if (leading) {
- it = GetOldest(id);
- }
- static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
- LogBufferElementCollection::iterator lastt;
- lastt = mLogElements.end();
- --lastt;
- LogBufferElementLast last;
- while (it != mLogElements.end()) {
- LogBufferElement* element = *it;
-
- if (oldest && oldest->mStart <= element->getSequence()) {
- busy = true;
- // Do not let chatty eliding trigger any reader mitigation
- break;
- }
-
- if (element->getLogId() != id) {
- ++it;
- continue;
- }
- // below this point element->getLogId() == id
-
- uint16_t dropped = element->getDropped();
-
- // remove any leading drops
- if (leading && dropped) {
- it = erase(it);
- continue;
- }
-
- if (dropped && last.coalesce(element, dropped)) {
- it = erase(it, true);
- continue;
- }
-
- int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
- ? element->getTag()
- : element->getUid();
-
- if (hasBlacklist && prune_->naughty(element)) {
- last.clear(element);
- it = erase(it);
- if (dropped) {
- continue;
- }
-
- pruneRows--;
- if (pruneRows == 0) {
- break;
- }
-
- if (key == worst) {
- kick = true;
- if (worst_sizes < second_worst_sizes) {
- break;
- }
- worst_sizes -= element->getMsgLen();
- }
- continue;
- }
-
- if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old)) ||
- (element->getRealTime() > (*lastt)->getRealTime())) {
- break;
- }
-
- if (dropped) {
- last.add(element);
- if (worstPid &&
- ((!gc && (element->getPid() == worstPid)) ||
- (mLastWorstPidOfSystem[id].find(element->getPid()) ==
- mLastWorstPidOfSystem[id].end()))) {
- // element->getUid() may not be AID_SYSTEM, next best
- // watermark if current one empty. id is not LOG_ID_EVENTS
- // or LOG_ID_SECURITY because of worstPid check.
- mLastWorstPidOfSystem[id][element->getPid()] = it;
- }
- if ((!gc && !worstPid && (key == worst)) ||
- (mLastWorst[id].find(key) == mLastWorst[id].end())) {
- mLastWorst[id][key] = it;
- }
- ++it;
- continue;
- }
-
- if ((key != worst) ||
- (worstPid && (element->getPid() != worstPid))) {
- leading = false;
- last.clear(element);
- ++it;
- continue;
- }
- // key == worst below here
- // If worstPid set, then element->getPid() == worstPid below here
-
- pruneRows--;
- if (pruneRows == 0) {
- break;
- }
-
- kick = true;
-
- uint16_t len = element->getMsgLen();
-
- // do not create any leading drops
- if (leading) {
- it = erase(it);
- } else {
- stats.drop(element);
- element->setDropped(1);
- if (last.coalesce(element, 1)) {
- it = erase(it, true);
- } else {
- last.add(element);
- if (worstPid &&
- (!gc || (mLastWorstPidOfSystem[id].find(worstPid) ==
- mLastWorstPidOfSystem[id].end()))) {
- // element->getUid() may not be AID_SYSTEM, next best
- // watermark if current one empty. id is not
- // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
- mLastWorstPidOfSystem[id][worstPid] = it;
- }
- if ((!gc && !worstPid) ||
- (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
- mLastWorst[id][worst] = it;
- }
- ++it;
- }
- }
- if (worst_sizes < second_worst_sizes) {
- break;
- }
- worst_sizes -= len;
- }
- last.clear();
-
- if (!kick || !prune_->worstUidEnabled()) {
- break; // the following loop will ask bad clients to skip/drop
- }
- }
-
- bool whitelist = false;
- bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
- it = GetOldest(id);
- while ((pruneRows > 0) && (it != mLogElements.end())) {
- LogBufferElement* element = *it;
-
- if (element->getLogId() != id) {
- it++;
- continue;
- }
-
- if (oldest && oldest->mStart <= element->getSequence()) {
- busy = true;
- if (!whitelist) kickMe(oldest, id, pruneRows);
- break;
- }
-
- if (hasWhitelist && !element->getDropped() && prune_->nice(element)) {
- // WhiteListed
- whitelist = true;
- it++;
- continue;
- }
-
- it = erase(it);
- pruneRows--;
- }
-
- // Do not save the whitelist if we are reader range limited
- if (whitelist && (pruneRows > 0)) {
- it = GetOldest(id);
- while ((it != mLogElements.end()) && (pruneRows > 0)) {
- LogBufferElement* element = *it;
-
- if (element->getLogId() != id) {
- ++it;
- continue;
- }
-
- if (oldest && oldest->mStart <= element->getSequence()) {
- busy = true;
- kickMe(oldest, id, pruneRows);
- break;
- }
-
- it = erase(it);
- pruneRows--;
- }
- }
-
- LogTimeEntry::unlock();
-
- return (pruneRows > 0) && busy;
-}
-
-// clear all rows of type "id" from the buffer.
-bool LogBuffer::clear(log_id_t id, uid_t uid) {
- bool busy = true;
- // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
- for (int retry = 4;;) {
- if (retry == 1) { // last pass
- // Check if it is still busy after the sleep, we say prune
- // one entry, not another clear run, so we are looking for
- // the quick side effect of the return value to tell us if
- // we have a _blocked_ reader.
- wrlock();
- busy = prune(id, 1, uid);
- unlock();
- // It is still busy, blocked reader(s), lets kill them all!
- // otherwise, lets be a good citizen and preserve the slow
- // readers and let the clear run (below) deal with determining
- // if we are still blocked and return an error code to caller.
- if (busy) {
- LogTimeEntry::wrlock();
- LastLogTimes::iterator times = mTimes.begin();
- while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
- // Killer punch
- if (entry->isWatching(id)) {
- android::prdebug(
- "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
- entry->mClient->getPid());
- entry->release_Locked();
- }
- times++;
- }
- LogTimeEntry::unlock();
- }
- }
- wrlock();
- busy = prune(id, ULONG_MAX, uid);
- unlock();
- if (!busy || !--retry) {
- break;
- }
- sleep(1); // Let reader(s) catch up after notification
- }
- return busy;
-}
-
-// get the used space associated with "id".
-unsigned long LogBuffer::getSizeUsed(log_id_t id) {
- rdlock();
- size_t retval = stats.sizes(id);
- unlock();
- return retval;
-}
-
-// set the total space allocated to "id"
-int LogBuffer::setSize(log_id_t id, unsigned long size) {
- // Reasonable limits ...
- if (!__android_logger_valid_buffer_size(size)) {
- return -1;
- }
- wrlock();
- log_buffer_size(id) = size;
- unlock();
- return 0;
-}
-
-// get the total space allocated to "id"
-unsigned long LogBuffer::getSize(log_id_t id) {
- rdlock();
- size_t retval = log_buffer_size(id);
- unlock();
- return retval;
-}
-
-uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged,
- bool security,
- int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
- LogBufferElementCollection::iterator it;
- uid_t uid = reader->getUid();
-
- rdlock();
-
- if (start <= 1) {
- // client wants to start from the beginning
- it = mLogElements.begin();
- } else {
- // Client wants to start from some specified time. Chances are
- // we are better off starting from the end of the time sorted list.
- for (it = mLogElements.end(); it != mLogElements.begin();
- /* do nothing */) {
- --it;
- LogBufferElement* element = *it;
- if (element->getSequence() <= start) {
- it++;
- break;
- }
- }
- }
-
- uint64_t curr = start;
-
- for (; it != mLogElements.end(); ++it) {
- LogBufferElement* element = *it;
-
- if (!privileged && (element->getUid() != uid)) {
- continue;
- }
-
- if (!security && (element->getLogId() == LOG_ID_SECURITY)) {
- continue;
- }
-
- // NB: calling out to another object with wrlock() held (safe)
- if (filter) {
- int ret = (*filter)(element, arg);
- if (ret == false) {
- continue;
- }
- if (ret != true) {
- break;
- }
- }
-
- bool sameTid = false;
- if (lastTid) {
- sameTid = lastTid[element->getLogId()] == element->getTid();
- // Dropped (chatty) immediately following a valid log from the
- // same source in the same log buffer indicates we have a
- // multiple identical squash. chatty that differs source
- // is due to spam filter. chatty to chatty of different
- // source is also due to spam filter.
- lastTid[element->getLogId()] =
- (element->getDropped() && !sameTid) ? 0 : element->getTid();
- }
-
- unlock();
-
- // range locking in LastLogTimes looks after us
- curr = element->flushTo(reader, this, sameTid);
-
- if (curr == element->FLUSH_ERROR) {
- return curr;
- }
-
- rdlock();
- }
- unlock();
-
- return curr;
-}
-
-std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
- unsigned int logMask) {
- wrlock();
-
- std::string ret = stats.format(uid, pid, logMask);
-
- unlock();
-
- return ret;
-}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 9a36712..859d740 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Android Open Source Project
+ * Copyright (C) 2020 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,154 +18,40 @@
#include <sys/types.h>
-#include <list>
-#include <optional>
-#include <string>
+#include <functional>
-#include <android/log.h>
-#include <private/android_filesystem_config.h>
-#include <sysutils/SocketClient.h>
+#include <log/log.h>
+#include <log/log_read.h>
-#include "LogBufferElement.h"
-#include "LogStatistics.h"
-#include "LogTags.h"
-#include "LogTimes.h"
-#include "LogWhiteBlackList.h"
+#include "LogWriter.h"
-//
-// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
-// differentiate without prejudice, we use 1972 to delineate, earlier
-// is likely monotonic, later is real. Otherwise we start using a
-// dividing line between monotonic and realtime if more than a minute
-// difference between them.
-//
-namespace android {
-
-static bool isMonotonic(const log_time& mono) {
- static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
- static const uint32_t EPOCH_PLUS_MINUTE = 60;
-
- if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
- return false;
- }
-
- log_time now(CLOCK_REALTIME);
-
- /* Timezone and ntp time setup? */
- if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
- return true;
- }
-
- /* no way to differentiate realtime from monotonic time */
- if (now.tv_sec < EPOCH_PLUS_MINUTE) {
- return false;
- }
-
- log_time cpu(CLOCK_MONOTONIC);
- /* too close to call to differentiate monotonic times from realtime */
- if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
- return false;
- }
-
- /* dividing line half way between monotonic and realtime */
- return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
-}
-}
-
-typedef std::list<LogBufferElement*> LogBufferElementCollection;
+enum class FilterResult {
+ kSkip,
+ kStop,
+ kWrite,
+};
class LogBuffer {
- LogBufferElementCollection mLogElements;
- pthread_rwlock_t mLogElementsLock;
+ public:
+ virtual ~LogBuffer() {}
- LogStatistics stats;
+ virtual void Init() = 0;
- // watermark of any worst/chatty uid processing
- typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
- LogBufferIteratorMap;
- LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
- // watermark of any worst/chatty pid of system processing
- typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator>
- LogBufferPidIteratorMap;
- LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
-
- unsigned long mMaxSize[LOG_ID_MAX];
-
- bool monotonic;
-
- LogBufferElement* lastLoggedElements[LOG_ID_MAX];
- LogBufferElement* droppedElements[LOG_ID_MAX];
- void log(LogBufferElement* elem);
-
- public:
- LastLogTimes& mTimes;
-
- LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune);
- ~LogBuffer();
- void init();
- bool isMonotonic() {
- return monotonic;
- }
-
- int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
- uint16_t len);
+ virtual int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len) = 0;
// lastTid is an optional context to help detect if the last previous
// valid message was from the same source so we can differentiate chatty
// filter types (identical or expired)
- uint64_t flushTo(SocketClient* writer, uint64_t start,
- pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
- bool privileged, bool security,
- int (*filter)(const LogBufferElement* element, void* arg) = nullptr,
- void* arg = nullptr);
+ static const uint64_t FLUSH_ERROR = 0;
+ virtual uint64_t FlushTo(LogWriter* writer, uint64_t start,
+ pid_t* last_tid, // nullable
+ const std::function<FilterResult(log_id_t log_id, pid_t pid,
+ uint64_t sequence, log_time realtime,
+ uint16_t dropped_count)>& filter) = 0;
- bool clear(log_id_t id, uid_t uid = AID_ROOT);
- unsigned long getSize(log_id_t id);
- int setSize(log_id_t id, unsigned long size);
- unsigned long getSizeUsed(log_id_t id);
+ virtual bool Clear(log_id_t id, uid_t uid) = 0;
+ virtual unsigned long GetSize(log_id_t id) = 0;
+ virtual int SetSize(log_id_t id, unsigned long size) = 0;
- std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
-
- void enableStatistics() {
- stats.enableStatistics();
- }
-
- // helper must be protected directly or implicitly by wrlock()/unlock()
- const char* pidToName(pid_t pid) {
- return stats.pidToName(pid);
- }
- uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
- const char* uidToName(uid_t uid) {
- return stats.uidToName(uid);
- }
- void wrlock() {
- pthread_rwlock_wrlock(&mLogElementsLock);
- }
- void rdlock() {
- pthread_rwlock_rdlock(&mLogElementsLock);
- }
- void unlock() {
- pthread_rwlock_unlock(&mLogElementsLock);
- }
-
- private:
- static constexpr size_t minPrune = 4;
- static constexpr size_t maxPrune = 256;
-
- void maybePrune(log_id_t id);
- void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
-
- bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
- LogBufferElementCollection::iterator erase(
- LogBufferElementCollection::iterator it, bool coalesce = false);
-
- // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
- // there are no logs for the given log type. Requires mLogElementsLock to be held.
- LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
-
- LogTags* tags_;
- PruneList* prune_;
-
- // Keeps track of the iterator to the oldest log message of a given log type, as an
- // optimization when pruning logs. Use GetOldest() to retrieve.
- std::optional<LogBufferElementCollection::iterator> oldest_[LOG_ID_MAX];
-};
+ virtual uint64_t sequence() const = 0;
+};
\ No newline at end of file
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 916ed42..c6dbda8 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogBufferElement.h"
+
#include <ctype.h>
#include <endian.h>
#include <fcntl.h>
@@ -25,21 +27,15 @@
#include <log/log_read.h>
#include <private/android_logger.h>
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
-#include "LogCommand.h"
-#include "LogReader.h"
+#include "LogStatistics.h"
#include "LogUtils.h"
-const uint64_t LogBufferElement::FLUSH_ERROR(0);
-atomic_int_fast64_t LogBufferElement::sequence(1);
-
LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
- pid_t tid, const char* msg, uint16_t len)
+ pid_t tid, uint64_t sequence, const char* msg, uint16_t len)
: mUid(uid),
mPid(pid),
mTid(tid),
- mSequence(sequence.fetch_add(1, memory_order_relaxed)),
+ mSequence(sequence),
mRealTime(realtime),
mMsgLen(len),
mLogId(log_id),
@@ -65,6 +61,23 @@
}
}
+LogBufferElement::LogBufferElement(LogBufferElement&& elem)
+ : mUid(elem.mUid),
+ mPid(elem.mPid),
+ mTid(elem.mTid),
+ mSequence(elem.mSequence),
+ mRealTime(elem.mRealTime),
+ mMsgLen(elem.mMsgLen),
+ mLogId(elem.mLogId),
+ mDropped(elem.mDropped) {
+ if (mDropped) {
+ mTag = elem.getTag();
+ } else {
+ mMsg = elem.mMsg;
+ elem.mMsg = nullptr;
+ }
+}
+
LogBufferElement::~LogBufferElement() {
if (!mDropped) {
delete[] mMsg;
@@ -153,7 +166,7 @@
}
// assumption: mMsg == NULL
-size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent,
+size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogStatistics* stats,
bool lastSame) {
static const char tag[] = "chatty";
@@ -163,17 +176,13 @@
}
static const char format_uid[] = "uid=%u%s%s %s %u line%s";
- parent->wrlock();
- const char* name = parent->uidToName(mUid);
- parent->unlock();
+ const char* name = stats->UidToName(mUid);
const char* commName = android::tidToName(mTid);
if (!commName && (mTid != mPid)) {
commName = android::tidToName(mPid);
}
if (!commName) {
- parent->wrlock();
- commName = parent->pidToName(mPid);
- parent->unlock();
+ commName = stats->PidToName(mPid);
}
if (name && name[0] && commName && (name[0] == commName[0])) {
size_t len = strlen(name + 1);
@@ -189,16 +198,16 @@
}
if (name) {
char* buf = nullptr;
- asprintf(&buf, "(%s)", name);
- if (buf) {
+ int result = asprintf(&buf, "(%s)", name);
+ if (result != -1) {
free(const_cast<char*>(name));
name = buf;
}
}
if (commName) {
char* buf = nullptr;
- asprintf(&buf, " %s", commName);
- if (buf) {
+ int result = asprintf(&buf, " %s", commName);
+ if (result != -1) {
free(const_cast<char*>(commName));
commName = buf;
}
@@ -246,7 +255,7 @@
return retval;
}
-uint64_t LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
+bool LogBufferElement::FlushTo(LogWriter* writer, LogStatistics* stats, bool lastSame) {
struct logger_entry entry = {};
entry.hdr_size = sizeof(struct logger_entry);
@@ -257,23 +266,18 @@
entry.sec = mRealTime.tv_sec;
entry.nsec = mRealTime.tv_nsec;
- struct iovec iovec[2];
- iovec[0].iov_base = &entry;
- iovec[0].iov_len = entry.hdr_size;
-
char* buffer = nullptr;
-
+ const char* msg;
if (mDropped) {
- entry.len = populateDroppedMessage(buffer, parent, lastSame);
- if (!entry.len) return mSequence;
- iovec[1].iov_base = buffer;
+ entry.len = populateDroppedMessage(buffer, stats, lastSame);
+ if (!entry.len) return true;
+ msg = buffer;
} else {
+ msg = mMsg;
entry.len = mMsgLen;
- iovec[1].iov_base = mMsg;
}
- iovec[1].iov_len = entry.len;
- uint64_t retval = reader->sendDatav(iovec, 1 + (entry.len != 0)) ? FLUSH_ERROR : mSequence;
+ bool retval = writer->Write(entry, msg);
if (buffer) free(buffer);
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 434b7db..35252f9 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -16,15 +16,15 @@
#pragma once
-#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <log/log.h>
-#include <sysutils/SocketClient.h>
-class LogBuffer;
+#include "LogWriter.h"
+
+class LogStatistics;
#define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve
// non-chatty UIDs less than this age in hours
@@ -33,8 +33,6 @@
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
class __attribute__((packed)) LogBufferElement {
- friend LogBuffer;
-
// sized to match reality of incoming log packets
const uint32_t mUid;
const uint32_t mPid;
@@ -52,16 +50,14 @@
const uint8_t mLogId;
bool mDropped;
- static atomic_int_fast64_t sequence;
-
// assumption: mDropped == true
- size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
- bool lastSame);
+ size_t populateDroppedMessage(char*& buffer, LogStatistics* parent, bool lastSame);
- public:
- LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
- pid_t tid, const char* msg, uint16_t len);
+ public:
+ LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ uint64_t sequence, const char* msg, uint16_t len);
LogBufferElement(const LogBufferElement& elem);
+ LogBufferElement(LogBufferElement&& elem);
~LogBufferElement();
bool isBinary(void) const {
@@ -92,11 +88,9 @@
return mDropped ? nullptr : mMsg;
}
uint64_t getSequence() const { return mSequence; }
- static uint64_t getCurrentSequence() { return sequence.load(memory_order_relaxed); }
log_time getRealTime(void) const {
return mRealTime;
}
- static const uint64_t FLUSH_ERROR;
- uint64_t flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
+ bool FlushTo(LogWriter* writer, LogStatistics* parent, bool lastSame);
};
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
new file mode 100644
index 0000000..334d57b
--- /dev/null
+++ b/logd/LogBufferTest.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2020 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 "LogBufferTest.h"
+
+#include <unistd.h>
+
+#include <limits>
+#include <memory>
+#include <regex>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "LogReaderThread.h"
+#include "LogWriter.h"
+
+using android::base::Join;
+using android::base::Split;
+using android::base::StringPrintf;
+
+#ifndef __ANDROID__
+unsigned long __android_logger_get_buffer_size(log_id_t) {
+ return 1024 * 1024;
+}
+
+bool __android_logger_valid_buffer_size(unsigned long) {
+ return true;
+}
+#endif
+
+void android::prdebug(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+char* android::uidToName(uid_t) {
+ return nullptr;
+}
+
+static std::vector<std::string> CompareLoggerEntries(const logger_entry& expected,
+ const logger_entry& result, bool ignore_len) {
+ std::vector<std::string> errors;
+ if (!ignore_len && expected.len != result.len) {
+ errors.emplace_back(
+ StringPrintf("len: expected %" PRIu16 " vs %" PRIu16, expected.len, result.len));
+ }
+ if (expected.hdr_size != result.hdr_size) {
+ errors.emplace_back(StringPrintf("hdr_size: %" PRIu16 " vs %" PRIu16, expected.hdr_size,
+ result.hdr_size));
+ }
+ if (expected.pid != result.pid) {
+ errors.emplace_back(
+ StringPrintf("pid: expected %" PRIi32 " vs %" PRIi32, expected.pid, result.pid));
+ }
+ if (expected.tid != result.tid) {
+ errors.emplace_back(
+ StringPrintf("tid: expected %" PRIu32 " vs %" PRIu32, expected.tid, result.tid));
+ }
+ if (expected.sec != result.sec) {
+ errors.emplace_back(
+ StringPrintf("sec: expected %" PRIu32 " vs %" PRIu32, expected.sec, result.sec));
+ }
+ if (expected.nsec != result.nsec) {
+ errors.emplace_back(
+ StringPrintf("nsec: expected %" PRIu32 " vs %" PRIu32, expected.nsec, result.nsec));
+ }
+ if (expected.lid != result.lid) {
+ errors.emplace_back(
+ StringPrintf("lid: expected %" PRIu32 " vs %" PRIu32, expected.lid, result.lid));
+ }
+ if (expected.uid != result.uid) {
+ errors.emplace_back(
+ StringPrintf("uid: expected %" PRIu32 " vs %" PRIu32, expected.uid, result.uid));
+ }
+ return errors;
+}
+
+static std::string MakePrintable(std::string in) {
+ if (in.size() > 80) {
+ in = in.substr(0, 80) + "...";
+ }
+ std::string result;
+ for (const char c : in) {
+ if (isprint(c)) {
+ result.push_back(c);
+ } else {
+ result.append(StringPrintf("\\%02x", static_cast<int>(c) & 0xFF));
+ }
+ }
+ return result;
+}
+
+static std::string CompareMessages(const std::string& expected, const std::string& result) {
+ if (expected == result) {
+ return {};
+ }
+ size_t diff_index = 0;
+ for (; diff_index < std::min(expected.size(), result.size()); ++diff_index) {
+ if (expected[diff_index] != result[diff_index]) {
+ break;
+ }
+ }
+
+ if (diff_index < 10) {
+ auto expected_short = MakePrintable(expected);
+ auto result_short = MakePrintable(result);
+ return StringPrintf("msg: expected '%s' vs '%s'", expected_short.c_str(),
+ result_short.c_str());
+ }
+
+ auto expected_short = MakePrintable(expected.substr(diff_index));
+ auto result_short = MakePrintable(result.substr(diff_index));
+ return StringPrintf("msg: index %zu: expected '%s' vs '%s'", diff_index, expected_short.c_str(),
+ result_short.c_str());
+}
+
+static std::string CompareRegexMessages(const std::string& expected, const std::string& result) {
+ auto expected_pieces = Split(expected, std::string("\0", 1));
+ auto result_pieces = Split(result, std::string("\0", 1));
+
+ if (expected_pieces.size() != 3 || result_pieces.size() != 3) {
+ return StringPrintf(
+ "msg: should have 3 null delimited strings found %d in expected, %d in result: "
+ "'%s' vs '%s'",
+ static_cast<int>(expected_pieces.size()), static_cast<int>(result_pieces.size()),
+ MakePrintable(expected).c_str(), MakePrintable(result).c_str());
+ }
+ if (expected_pieces[0] != result_pieces[0]) {
+ return StringPrintf("msg: tag/priority mismatch expected '%s' vs '%s'",
+ MakePrintable(expected_pieces[0]).c_str(),
+ MakePrintable(result_pieces[0]).c_str());
+ }
+ std::regex expected_tag_regex(expected_pieces[1]);
+ if (!std::regex_search(result_pieces[1], expected_tag_regex)) {
+ return StringPrintf("msg: message regex mismatch expected '%s' vs '%s'",
+ MakePrintable(expected_pieces[1]).c_str(),
+ MakePrintable(result_pieces[1]).c_str());
+ }
+ if (expected_pieces[2] != result_pieces[2]) {
+ return StringPrintf("msg: nothing expected after final null character '%s' vs '%s'",
+ MakePrintable(expected_pieces[2]).c_str(),
+ MakePrintable(result_pieces[2]).c_str());
+ }
+ return {};
+}
+
+void CompareLogMessages(const std::vector<LogMessage>& expected,
+ const std::vector<LogMessage>& result) {
+ EXPECT_EQ(expected.size(), result.size());
+ size_t end = std::min(expected.size(), result.size());
+ size_t num_errors = 0;
+ for (size_t i = 0; i < end; ++i) {
+ auto errors =
+ CompareLoggerEntries(expected[i].entry, result[i].entry, expected[i].regex_compare);
+ auto msg_error = expected[i].regex_compare
+ ? CompareRegexMessages(expected[i].message, result[i].message)
+ : CompareMessages(expected[i].message, result[i].message);
+ if (!msg_error.empty()) {
+ errors.emplace_back(msg_error);
+ }
+ if (!errors.empty()) {
+ GTEST_LOG_(ERROR) << "Mismatch log message " << i << "\n" << Join(errors, "\n");
+ ++num_errors;
+ }
+ }
+ EXPECT_EQ(0U, num_errors);
+}
+
+void FixupMessages(std::vector<LogMessage>* messages) {
+ for (auto& [entry, message, _] : *messages) {
+ entry.hdr_size = sizeof(logger_entry);
+ entry.len = message.size();
+ }
+}
+
+TEST_P(LogBufferTest, smoke) {
+ std::vector<LogMessage> log_messages = {
+ {{
+ .pid = 1,
+ .tid = 1,
+ .sec = 1234,
+ .nsec = 323001,
+ .lid = LOG_ID_MAIN,
+ .uid = 0,
+ },
+ "smoke test"},
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ uint64_t flush_result = log_buffer_->FlushTo(test_writer.get(), 1, nullptr, nullptr);
+ EXPECT_EQ(1ULL, flush_result);
+ CompareLogMessages(log_messages, read_log_messages);
+}
+
+TEST_P(LogBufferTest, smoke_with_reader_thread) {
+ std::vector<LogMessage> log_messages = {
+ {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
+ "first"},
+ {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
+ "second"},
+ {{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_KERNEL, .uid = 0},
+ "third"},
+ {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20004, .lid = LOG_ID_MAIN, .uid = 0},
+ "fourth"},
+ {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20005, .lid = LOG_ID_RADIO, .uid = 0},
+ "fifth"},
+ {{.pid = 2, .tid = 2, .sec = 10000, .nsec = 20006, .lid = LOG_ID_RADIO, .uid = 0},
+ "sixth"},
+ {{.pid = 3, .tid = 2, .sec = 10000, .nsec = 20007, .lid = LOG_ID_RADIO, .uid = 0},
+ "seventh"},
+ {{.pid = 4, .tid = 2, .sec = 10000, .nsec = 20008, .lid = LOG_ID_MAIN, .uid = 0},
+ "eighth"},
+ {{.pid = 5, .tid = 2, .sec = 10000, .nsec = 20009, .lid = LOG_ID_CRASH, .uid = 0},
+ "nineth"},
+ {{.pid = 6, .tid = 2, .sec = 10000, .nsec = 20011, .lid = LOG_ID_MAIN, .uid = 0},
+ "tenth"},
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+ 0, ~0, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ while (!released) {
+ usleep(5000);
+ }
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ EXPECT_EQ(0U, reader_list_.reader_threads().size());
+ }
+ CompareLogMessages(log_messages, read_log_messages);
+}
+
+// Generate random messages, set the 'sec' parameter explicit though, to be able to track the
+// expected order of messages.
+LogMessage GenerateRandomLogMessage(uint32_t sec) {
+ auto rand_uint32 = [](int max) -> uint32_t { return rand() % max; };
+ logger_entry entry = {
+ .hdr_size = sizeof(logger_entry),
+ .pid = rand() % 5000,
+ .tid = rand_uint32(5000),
+ .sec = sec,
+ .nsec = rand_uint32(NS_PER_SEC),
+ .lid = rand_uint32(LOG_ID_STATS),
+ .uid = rand_uint32(100000),
+ };
+
+ // See comment in ChattyLogBuffer::Log() for why this is disallowed.
+ if (entry.nsec % 1000 == 0) {
+ ++entry.nsec;
+ }
+
+ if (entry.lid == LOG_ID_EVENTS) {
+ entry.lid = LOG_ID_KERNEL;
+ }
+
+ std::string message;
+ char priority = ANDROID_LOG_INFO + rand() % 2;
+ message.push_back(priority);
+
+ int tag_length = 2 + rand() % 10;
+ for (int i = 0; i < tag_length; ++i) {
+ message.push_back('a' + rand() % 26);
+ }
+ message.push_back('\0');
+
+ int msg_length = 2 + rand() % 1000;
+ for (int i = 0; i < msg_length; ++i) {
+ message.push_back('a' + rand() % 26);
+ }
+ message.push_back('\0');
+
+ entry.len = message.size();
+
+ return {entry, message};
+}
+
+TEST_P(LogBufferTest, random_messages) {
+ srand(1);
+ std::vector<LogMessage> log_messages;
+ for (size_t i = 0; i < 1000; ++i) {
+ log_messages.emplace_back(GenerateRandomLogMessage(i));
+ }
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+ 0, ~0, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ while (!released) {
+ usleep(5000);
+ }
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ EXPECT_EQ(0U, reader_list_.reader_threads().size());
+ }
+ CompareLogMessages(log_messages, read_log_messages);
+}
+
+INSTANTIATE_TEST_CASE_P(LogBufferTests, LogBufferTest, testing::Values("chatty", "simple"));
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
new file mode 100644
index 0000000..1659f12
--- /dev/null
+++ b/logd/LogBufferTest.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ChattyLogBuffer.h"
+#include "LogReaderList.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
+#include "SimpleLogBuffer.h"
+
+struct LogMessage {
+ logger_entry entry;
+ std::string message;
+ bool regex_compare = false; // Only set for expected messages, when true 'message' should be
+ // interpretted as a regex.
+};
+
+// Compares the ordered list of expected and result, causing a test failure with appropriate
+// information on failure.
+void CompareLogMessages(const std::vector<LogMessage>& expected,
+ const std::vector<LogMessage>& result);
+// Sets hdr_size and len parameters appropriately.
+void FixupMessages(std::vector<LogMessage>* messages);
+
+class TestWriter : public LogWriter {
+ public:
+ TestWriter(std::vector<LogMessage>* msgs, bool* released)
+ : LogWriter(0, true, true), msgs_(msgs), released_(released) {}
+ bool Write(const logger_entry& entry, const char* message) override {
+ msgs_->emplace_back(LogMessage{entry, std::string(message, entry.len), false});
+ return true;
+ }
+
+ void Release() {
+ if (released_) *released_ = true;
+ }
+
+ std::string name() const override { return "test_writer"; }
+
+ private:
+ std::vector<LogMessage>* msgs_;
+ bool* released_;
+};
+
+class LogBufferTest : public testing::TestWithParam<std::string> {
+ protected:
+ void SetUp() override {
+ if (GetParam() == "chatty") {
+ log_buffer_.reset(new ChattyLogBuffer(&reader_list_, &tags_, &prune_, &stats_));
+ } else if (GetParam() == "simple") {
+ log_buffer_.reset(new SimpleLogBuffer(&reader_list_, &tags_, &stats_));
+ } else {
+ FAIL() << "Unknown buffer type selected for test";
+ }
+ }
+
+ void LogMessages(const std::vector<LogMessage>& messages) {
+ for (auto& [entry, message, _] : messages) {
+ log_buffer_->Log(static_cast<log_id_t>(entry.lid), log_time(entry.sec, entry.nsec),
+ entry.uid, entry.pid, entry.tid, message.c_str(), message.size());
+ }
+ }
+
+ LogReaderList reader_list_;
+ LogTags tags_;
+ PruneList prune_;
+ LogStatistics stats_{false};
+ std::unique_ptr<LogBuffer> log_buffer_;
+};
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index edd326a..1ea87a9 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogKlog.h"
+
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
@@ -29,8 +31,6 @@
#include <private/android_logger.h>
#include "LogBuffer.h"
-#include "LogKlog.h"
-#include "LogReader.h"
#define KMSG_PRIORITY(PRI) \
'<', '0' + (LOG_SYSLOG | (PRI)) / 10, '0' + (LOG_SYSLOG | (PRI)) % 10, '>'
@@ -201,15 +201,14 @@
? log_time(log_time::EPOCH)
: (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
-LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
- bool auditd)
+LogKlog::LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats)
: SocketListener(fdRead, false),
logbuf(buf),
- reader(reader),
signature(CLOCK_MONOTONIC),
initialized(false),
enableLogging(true),
- auditd(auditd) {
+ auditd(auditd),
+ stats_(stats) {
static const char klogd_message[] = "%s%s%" PRIu64 "\n";
char buffer[strlen(priority_message) + strlen(klogdStr) +
strlen(klogd_message) + 20];
@@ -309,8 +308,6 @@
}
buf = cp;
- if (isMonotonic()) return now;
-
const char* b;
if (((b = android::strnstr(cp, len, suspendStr))) &&
(((b += strlen(suspendStr)) - cp) < len)) {
@@ -356,11 +353,7 @@
convertMonotonicToReal(now);
} else {
- if (isMonotonic()) {
- now = log_time(CLOCK_MONOTONIC);
- } else {
- now = log_time(CLOCK_REALTIME);
- }
+ now = log_time(CLOCK_REALTIME);
}
return now;
}
@@ -431,45 +424,6 @@
return pri;
}
-// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
-// compensated start time.
-void LogKlog::synchronize(const char* buf, ssize_t len) {
- const char* cp = android::strnstr(buf, len, suspendStr);
- if (!cp) {
- cp = android::strnstr(buf, len, resumeStr);
- if (!cp) return;
- } else {
- const char* rp = android::strnstr(buf, len, resumeStr);
- if (rp && (rp < cp)) cp = rp;
- }
-
- do {
- --cp;
- } while ((cp > buf) && (*cp != '\n'));
- if (*cp == '\n') {
- ++cp;
- }
- parseKernelPrio(cp, len - (cp - buf));
-
- log_time now = sniffTime(cp, len - (cp - buf), true);
-
- const char* suspended = android::strnstr(buf, len, suspendedStr);
- if (!suspended || (suspended > cp)) {
- return;
- }
- cp = suspended;
-
- do {
- --cp;
- } while ((cp > buf) && (*cp != '\n'));
- if (*cp == '\n') {
- ++cp;
- }
- parseKernelPrio(cp, len - (cp - buf));
-
- sniffTime(cp, len - (cp - buf), true);
-}
-
// Convert kernel log priority number into an Android Logger priority number
static int convertKernelPrioToAndroidPrio(int pri) {
switch (pri & LOG_PRIMASK) {
@@ -573,9 +527,7 @@
const pid_t tid = pid;
uid_t uid = AID_ROOT;
if (pid) {
- logbuf->wrlock();
- uid = logbuf->pidToUid(pid);
- logbuf->unlock();
+ uid = stats_->PidToUid(pid);
}
// Parse (rules at top) to pull out a tag from the incoming kernel message.
@@ -789,7 +741,7 @@
memcpy(np, p, b);
np[b] = '\0';
- if (!isMonotonic()) {
+ {
// Watch out for singular race conditions with timezone causing near
// integer quarter-hour jumps in the time and compensate accordingly.
// Entries will be temporal within near_seconds * 2. b/21868540
@@ -815,12 +767,7 @@
}
// Log message
- int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
-
- // notify readers
- if (rc > 0) {
- reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
- }
+ int rc = logbuf->Log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
return rc;
}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 6bfd6a8..56e0452 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_KLOG_H__
-#define _LOGD_LOG_KLOG_H__
+#pragma once
#include <private/android_logger.h>
#include <sysutils/SocketListener.h>
-class LogBuffer;
-class LogReader;
+#include "LogBuffer.h"
+#include "LogStatistics.h"
class LogKlog : public SocketListener {
LogBuffer* logbuf;
- LogReader* reader;
const log_time signature;
// Set once thread is started, separates KLOG_ACTION_READ_ALL
// and KLOG_ACTION_READ phases.
@@ -38,27 +36,18 @@
static log_time correction;
- public:
- LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
- bool auditd);
+ public:
+ LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats);
int log(const char* buf, ssize_t len);
- void synchronize(const char* buf, ssize_t len);
- bool isMonotonic() {
- return logbuf->isMonotonic();
- }
- static void convertMonotonicToReal(log_time& real) {
- real += correction;
- }
- static void convertRealToMonotonic(log_time& real) {
- real -= correction;
- }
+ static void convertMonotonicToReal(log_time& real) { real += correction; }
- protected:
- log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
- pid_t sniffPid(const char*& buf, ssize_t len);
- void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
- virtual bool onDataAvailable(SocketClient* cli);
+ protected:
+ log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+ pid_t sniffPid(const char*& buf, ssize_t len);
+ void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+ virtual bool onDataAvailable(SocketClient* cli);
+
+ private:
+ LogStatistics* stats_;
};
-
-#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index ba61042..a6ab50b 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -22,42 +22,53 @@
#include <sys/un.h>
#include <unistd.h>
+#include <thread>
+
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "LogBuffer.h"
#include "LogListener.h"
-#include "LogUtils.h"
+#include "LogPermissions.h"
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
- : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
+LogListener::LogListener(LogBuffer* buf) : socket_(GetLogSocket()), logbuf_(buf) {}
-bool LogListener::onDataAvailable(SocketClient* cli) {
- static bool name_set;
- if (!name_set) {
- prctl(PR_SET_NAME, "logd.writer");
- name_set = true;
+bool LogListener::StartListener() {
+ if (socket_ <= 0) {
+ return false;
}
+ auto thread = std::thread(&LogListener::ThreadFunction, this);
+ thread.detach();
+ return true;
+}
+void LogListener::ThreadFunction() {
+ prctl(PR_SET_NAME, "logd.writer");
+
+ while (true) {
+ HandleData();
+ }
+}
+
+void LogListener::HandleData() {
// + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
- char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
- struct iovec iov = { buffer, sizeof(buffer) - 1 };
+ __attribute__((uninitialized)) char
+ buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
+ struct iovec iov = {buffer, sizeof(buffer) - 1};
alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
struct msghdr hdr = {
nullptr, 0, &iov, 1, control, sizeof(control), 0,
};
- int socket = cli->getSocket();
-
// To clear the entire buffer is secure/safe, but this contributes to 1.68%
// overhead under logging load. We are safe because we check counts, but
// still need to clear null terminator
// memset(buffer, 0, sizeof(buffer));
- ssize_t n = recvmsg(socket, &hdr, 0);
+ ssize_t n = recvmsg(socket_, &hdr, 0);
if (n <= (ssize_t)(sizeof(android_log_header_t))) {
- return false;
+ return;
}
buffer[n] = 0;
@@ -75,14 +86,14 @@
}
if (cred == nullptr) {
- return false;
+ return;
}
if (cred->uid == AID_LOGD) {
// ignore log messages we send to ourself.
// Such log messages are often generated by libraries we depend on
// which use standard Android logging.
- return false;
+ return;
}
android_log_header_t* header =
@@ -90,13 +101,13 @@
log_id_t logId = static_cast<log_id_t>(header->id);
if (/* logId < LOG_ID_MIN || */ logId >= LOG_ID_MAX ||
logId == LOG_ID_KERNEL) {
- return false;
+ return;
}
if ((logId == LOG_ID_SECURITY) &&
(!__android_log_security() ||
!clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
- return false;
+ return;
}
char* msg = ((char*)buffer) + sizeof(android_log_header_t);
@@ -105,16 +116,11 @@
// NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
// truncated message to the logs.
- int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
- ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
- if (res > 0) {
- reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
- }
-
- return true;
+ logbuf_->Log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+ ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
}
-int LogListener::getLogSocket() {
+int LogListener::GetLogSocket() {
static const char socketName[] = "logdw";
int sock = android_get_control_socket(socketName);
diff --git a/logd/LogListener.h b/logd/LogListener.h
index 8fe3da4..c114e38 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -14,24 +14,20 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_LISTENER_H__
-#define _LOGD_LOG_LISTENER_H__
+#pragma once
-#include <sysutils/SocketListener.h>
-#include "LogReader.h"
+#include "LogBuffer.h"
-class LogListener : public SocketListener {
- LogBuffer* logbuf;
- LogReader* reader;
+class LogListener {
+ public:
+ LogListener(LogBuffer* buf);
+ bool StartListener();
- public:
- LogListener(LogBuffer* buf, LogReader* reader);
+ private:
+ void ThreadFunction();
+ void HandleData();
+ static int GetLogSocket();
- protected:
- virtual bool onDataAvailable(SocketClient* cli);
-
- private:
- static int getLogSocket();
+ int socket_;
+ LogBuffer* logbuf_;
};
-
-#endif
diff --git a/logd/LogCommand.cpp b/logd/LogPermissions.cpp
similarity index 97%
rename from logd/LogCommand.cpp
rename to logd/LogPermissions.cpp
index 8bff9da..8f02d5a 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogPermissions.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogPermissions.h"
+
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -21,12 +23,6 @@
#include <private/android_filesystem_config.h>
-#include "LogCommand.h"
-#include "LogUtils.h"
-
-LogCommand::LogCommand(const char* cmd) : FrameworkCommand(cmd) {
-}
-
// gets a list of supplementary group IDs associated with
// the socket peer. This is implemented by opening
// /proc/PID/status and look for the "Group:" line.
diff --git a/logd/LogCommand.h b/logd/LogPermissions.h
similarity index 74%
rename from logd/LogCommand.h
rename to logd/LogPermissions.h
index e10ffa0..3130db5 100644
--- a/logd/LogCommand.h
+++ b/logd/LogPermissions.h
@@ -14,17 +14,11 @@
* limitations under the License.
*/
-#ifndef _LOGD_COMMAND_H
-#define _LOGD_COMMAND_H
+#pragma once
-#include <sysutils/FrameworkCommand.h>
+#include <sys/types.h>
+
#include <sysutils/SocketClient.h>
-class LogCommand : public FrameworkCommand {
- public:
- explicit LogCommand(const char* cmd);
- virtual ~LogCommand() {
- }
-};
-
-#endif
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
+bool clientHasLogCredentials(SocketClient* cli);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index c6dea69..35c46aa 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -21,39 +21,62 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include <chrono>
+
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "LogBuffer.h"
#include "LogBufferElement.h"
+#include "LogPermissions.h"
#include "LogReader.h"
#include "LogUtils.h"
+#include "LogWriter.h"
static bool CanReadSecurityLogs(SocketClient* client) {
return client->getUid() == AID_SYSTEM || client->getGid() == AID_SYSTEM;
}
-LogReader::LogReader(LogBuffer* logbuf)
- : SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
+static std::string SocketClientToName(SocketClient* client) {
+ return android::base::StringPrintf("pid %d, fd %d", client->getPid(), client->getSocket());
}
-// When we are notified a new log entry is available, inform
-// listening sockets who are watching this entry's log id.
-void LogReader::notifyNewLog(log_mask_t log_mask) {
- LastLogTimes& times = mLogbuf.mTimes;
+class SocketLogWriter : public LogWriter {
+ public:
+ SocketLogWriter(LogReader* reader, SocketClient* client, bool privileged,
+ bool can_read_security_logs)
+ : LogWriter(client->getUid(), privileged, can_read_security_logs),
+ reader_(reader),
+ client_(client) {}
- LogTimeEntry::wrlock();
- for (const auto& entry : times) {
- if (!entry->isWatchingMultiple(log_mask)) {
- continue;
- }
- if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
- continue;
- }
- entry->triggerReader_Locked();
+ bool Write(const logger_entry& entry, const char* msg) override {
+ struct iovec iovec[2];
+ iovec[0].iov_base = const_cast<logger_entry*>(&entry);
+ iovec[0].iov_len = entry.hdr_size;
+ iovec[1].iov_base = const_cast<char*>(msg);
+ iovec[1].iov_len = entry.len;
+
+ return client_->sendDatav(iovec, 1 + (entry.len != 0)) == 0;
}
- LogTimeEntry::unlock();
-}
+
+ void Release() override {
+ reader_->release(client_);
+ client_->decRef();
+ }
+
+ void Shutdown() override { shutdown(client_->getSocket(), SHUT_RDWR); }
+
+ std::string name() const override { return SocketClientToName(client_); }
+
+ private:
+ LogReader* reader_;
+ SocketClient* client_;
+};
+
+LogReader::LogReader(LogBuffer* logbuf, LogReaderList* reader_list)
+ : SocketListener(getLogSocket(), true), log_buffer_(logbuf), reader_list_(reader_list) {}
// Note returning false will release the SocketClient instance.
bool LogReader::onDataAvailable(SocketClient* cli) {
@@ -67,22 +90,15 @@
int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
if (len <= 0) {
- doSocketDelete(cli);
+ DoSocketDelete(cli);
return false;
}
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;
- }
+ // Clients are only allowed to send one command, disconnect them if they send another.
+ if (DoSocketDelete(cli)) {
+ return false;
}
- LogTimeEntry::unlock();
unsigned long tail = 0;
static const char _tail[] = " tail=";
@@ -99,11 +115,12 @@
start.strptime(cp + sizeof(_start) - 1, "%s.%q");
}
- uint64_t timeout = 0;
+ std::chrono::steady_clock::time_point deadline = {};
static const char _timeout[] = " timeout=";
cp = strstr(buffer, _timeout);
if (cp) {
- timeout = atol(cp + sizeof(_timeout) - 1) * NS_PER_SEC + log_time(CLOCK_MONOTONIC).nsec();
+ long timeout_seconds = atol(cp + sizeof(_timeout) - 1);
+ deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_seconds);
}
unsigned int logMask = -1;
@@ -137,8 +154,8 @@
if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
- LogTimeEntry::wrlock();
- LogTimeEntry::unlock();
+ reader_list_->reader_threads_lock().lock();
+ reader_list_->reader_threads_lock().unlock();
sched_yield();
nonBlock = true;
}
@@ -146,112 +163,85 @@
bool privileged = clientHasLogCredentials(cli);
bool can_read_security = CanReadSecurityLogs(cli);
+ std::unique_ptr<LogWriter> socket_log_writer(
+ new SocketLogWriter(this, cli, privileged, can_read_security));
+
uint64_t sequence = 1;
// Convert realtime to sequence number
if (start != log_time::EPOCH) {
- class LogFindStart {
- const pid_t mPid;
- const unsigned mLogMask;
- bool startTimeSet;
- const log_time start;
- uint64_t& sequence;
- uint64_t last;
- bool isMonotonic;
-
- public:
- LogFindStart(unsigned logMask, pid_t pid, log_time start, uint64_t& sequence,
- bool isMonotonic)
- : mPid(pid),
- mLogMask(logMask),
- startTimeSet(false),
- start(start),
- sequence(sequence),
- last(sequence),
- isMonotonic(isMonotonic) {}
-
- static int callback(const LogBufferElement* element, void* obj) {
- LogFindStart* me = reinterpret_cast<LogFindStart*>(obj);
- if ((!me->mPid || (me->mPid == element->getPid())) &&
- (me->mLogMask & (1 << element->getLogId()))) {
- if (me->start == element->getRealTime()) {
- me->sequence = element->getSequence();
- me->startTimeSet = true;
- return -1;
- } else if (!me->isMonotonic || android::isMonotonic(element->getRealTime())) {
- if (me->start < element->getRealTime()) {
- me->sequence = me->last;
- me->startTimeSet = true;
- return -1;
- }
- me->last = element->getSequence();
- } else {
- me->last = element->getSequence();
- }
+ bool start_time_set = false;
+ uint64_t last = sequence;
+ auto log_find_start = [pid, logMask, start, &sequence, &start_time_set, &last](
+ log_id_t element_log_id, pid_t element_pid,
+ uint64_t element_sequence, log_time element_realtime,
+ uint16_t) -> FilterResult {
+ if (pid && pid != element_pid) {
+ return FilterResult::kSkip;
+ }
+ if ((logMask & (1 << element_log_id)) == 0) {
+ return FilterResult::kSkip;
+ }
+ if (start == element_realtime) {
+ sequence = element_sequence;
+ start_time_set = true;
+ return FilterResult::kStop;
+ } else {
+ if (start < element_realtime) {
+ sequence = last;
+ start_time_set = true;
+ return FilterResult::kStop;
}
- return false;
+ last = element_sequence;
}
+ return FilterResult::kSkip;
+ };
- bool found() { return startTimeSet; }
- } logFindStart(logMask, pid, start, sequence,
- logbuf().isMonotonic() && android::isMonotonic(start));
+ log_buffer_->FlushTo(socket_log_writer.get(), sequence, nullptr, log_find_start);
- logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security,
- logFindStart.callback, &logFindStart);
-
- if (!logFindStart.found()) {
+ if (!start_time_set) {
if (nonBlock) {
- doSocketDelete(cli);
return false;
}
- sequence = LogBufferElement::getCurrentSequence();
+ sequence = log_buffer_->sequence();
}
}
android::prdebug(
"logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
- "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+ "start=%" PRIu64 "ns deadline=%" PRIi64 "ns\n",
cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail, logMask,
- (int)pid, start.nsec(), timeout);
+ (int)pid, start.nsec(), static_cast<int64_t>(deadline.time_since_epoch().count()));
if (start == log_time::EPOCH) {
- timeout = 0;
+ deadline = {};
}
- LogTimeEntry::wrlock();
- auto entry = std::make_unique<LogTimeEntry>(*this, cli, nonBlock, tail, logMask, pid, start,
- sequence, timeout, privileged, can_read_security);
- if (!entry->startReader_Locked()) {
- LogTimeEntry::unlock();
- return false;
- }
-
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ auto entry = std::make_unique<LogReaderThread>(log_buffer_, reader_list_,
+ std::move(socket_log_writer), nonBlock, tail,
+ logMask, pid, start, sequence, deadline);
// release client and entry reference counts once done
cli->incRef();
- mLogbuf.mTimes.emplace_front(std::move(entry));
+ reader_list_->reader_threads().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));
- LogTimeEntry::unlock();
-
return true;
}
-void LogReader::doSocketDelete(SocketClient* cli) {
- LastLogTimes& times = mLogbuf.mTimes;
- LogTimeEntry::wrlock();
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- LogTimeEntry* entry = it->get();
- if (entry->mClient == cli) {
- entry->release_Locked();
- break;
+bool LogReader::DoSocketDelete(SocketClient* cli) {
+ auto cli_name = SocketClientToName(cli);
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ for (const auto& reader : reader_list_->reader_threads()) {
+ if (reader->name() == cli_name) {
+ reader->release_Locked();
+ return true;
}
- it++;
}
- LogTimeEntry::unlock();
+ return false;
}
int LogReader::getLogSocket() {
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b5312b6..b85a584 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -14,35 +14,28 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_WRITER_H__
-#define _LOGD_LOG_WRITER_H__
+#pragma once
#include <sysutils/SocketListener.h>
-#include "LogTimes.h"
+#include "LogBuffer.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
#define LOGD_SNDTIMEO 32
-class LogBuffer;
-
class LogReader : public SocketListener {
- LogBuffer& mLogbuf;
+ public:
+ explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
- public:
- explicit LogReader(LogBuffer* logbuf);
- void notifyNewLog(log_mask_t logMask);
-
- LogBuffer& logbuf(void) const {
- return mLogbuf;
- }
-
- protected:
+ protected:
virtual bool onDataAvailable(SocketClient* cli);
- private:
+ private:
static int getLogSocket();
- void doSocketDelete(SocketClient* cli);
-};
+ bool DoSocketDelete(SocketClient* cli);
-#endif
+ LogBuffer* log_buffer_;
+ LogReaderList* reader_list_;
+};
diff --git a/logd/LogReaderList.cpp b/logd/LogReaderList.cpp
new file mode 100644
index 0000000..220027b
--- /dev/null
+++ b/logd/LogReaderList.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 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 "LogReaderList.h"
+
+// When we are notified a new log entry is available, inform
+// listening sockets who are watching this entry's log id.
+void LogReaderList::NotifyNewLog(unsigned int log_mask) const {
+ auto lock = std::lock_guard{reader_threads_lock_};
+
+ for (const auto& entry : reader_threads_) {
+ if (!entry->IsWatchingMultiple(log_mask)) {
+ continue;
+ }
+ if (entry->deadline().time_since_epoch().count() != 0) {
+ continue;
+ }
+ entry->triggerReader_Locked();
+ }
+}
diff --git a/logd/LogReaderList.h b/logd/LogReaderList.h
new file mode 100644
index 0000000..0d84aba
--- /dev/null
+++ b/logd/LogReaderList.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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 <list>
+#include <memory>
+#include <mutex>
+
+#include "LogReaderThread.h"
+
+class LogReaderList {
+ public:
+ void NotifyNewLog(unsigned int log_mask) const;
+
+ std::list<std::unique_ptr<LogReaderThread>>& reader_threads() { return reader_threads_; }
+ std::mutex& reader_threads_lock() { return reader_threads_lock_; }
+
+ private:
+ std::list<std::unique_ptr<LogReaderThread>> reader_threads_;
+ mutable std::mutex reader_threads_lock_;
+};
\ No newline at end of file
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
new file mode 100644
index 0000000..3a83f3f
--- /dev/null
+++ b/logd/LogReaderThread.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 "LogReaderThread.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include <thread>
+
+#include "LogBuffer.h"
+#include "LogReaderList.h"
+
+using namespace std::placeholders;
+
+LogReaderThread::LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
+ std::unique_ptr<LogWriter> writer, bool non_block,
+ unsigned long tail, unsigned int log_mask, pid_t pid,
+ log_time start_time, uint64_t start,
+ std::chrono::steady_clock::time_point deadline)
+ : log_buffer_(log_buffer),
+ reader_list_(reader_list),
+ writer_(std::move(writer)),
+ leading_dropped_(false),
+ log_mask_(log_mask),
+ pid_(pid),
+ tail_(tail),
+ count_(0),
+ index_(0),
+ start_time_(start_time),
+ start_(start),
+ deadline_(deadline),
+ non_block_(non_block) {
+ memset(last_tid_, 0, sizeof(last_tid_));
+ cleanSkip_Locked();
+ auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
+ thread.detach();
+}
+
+void LogReaderThread::ThreadFunction() {
+ prctl(PR_SET_NAME, "logd.reader.per");
+
+ leading_dropped_ = true;
+
+ auto lock = std::unique_lock{reader_list_->reader_threads_lock()};
+
+ uint64_t start = start_;
+
+ while (!release_) {
+ if (deadline_.time_since_epoch().count() != 0) {
+ if (thread_triggered_condition_.wait_until(lock, deadline_) ==
+ std::cv_status::timeout) {
+ deadline_ = {};
+ }
+ if (release_) {
+ break;
+ }
+ }
+
+ lock.unlock();
+
+ if (tail_) {
+ log_buffer_->FlushTo(writer_.get(), start, nullptr,
+ [this](log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime, uint16_t dropped_count) {
+ return FilterFirstPass(log_id, pid, sequence, realtime,
+ dropped_count);
+ });
+ leading_dropped_ =
+ true; // TODO: Likely a bug, if leading_dropped_ was not true before calling
+ // flushTo(), then it should not be reset to true after.
+ }
+ start = log_buffer_->FlushTo(writer_.get(), start, last_tid_,
+ [this](log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime, uint16_t dropped_count) {
+ return FilterSecondPass(log_id, pid, sequence, realtime,
+ dropped_count);
+ });
+
+ // We only ignore entries before the original start time for the first flushTo(), if we
+ // get entries after this first flush before the original start time, then the client
+ // wouldn't have seen them.
+ // Note: this is still racy and may skip out of order events that came in since the last
+ // time the client disconnected and then reconnected with the new start time. The long term
+ // solution here is that clients must request events since a specific sequence number.
+ start_time_.tv_sec = 0;
+ start_time_.tv_nsec = 0;
+
+ lock.lock();
+
+ if (start == LogBuffer::FLUSH_ERROR) {
+ break;
+ }
+
+ start_ = start + 1;
+
+ if (non_block_ || release_) {
+ break;
+ }
+
+ cleanSkip_Locked();
+
+ if (deadline_.time_since_epoch().count() == 0) {
+ thread_triggered_condition_.wait(lock);
+ }
+ }
+
+ writer_->Release();
+
+ auto& log_reader_threads = reader_list_->reader_threads();
+ auto it = std::find_if(log_reader_threads.begin(), log_reader_threads.end(),
+ [this](const auto& other) { return other.get() == this; });
+
+ if (it != log_reader_threads.end()) {
+ log_reader_threads.erase(it);
+ }
+}
+
+// A first pass to count the number of elements
+FilterResult LogReaderThread::FilterFirstPass(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime, uint16_t dropped_count) {
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+
+ if (leading_dropped_) {
+ if (dropped_count) {
+ return FilterResult::kSkip;
+ }
+ leading_dropped_ = false;
+ }
+
+ if (count_ == 0) {
+ start_ = sequence;
+ }
+
+ if ((!pid_ || pid_ == pid) && IsWatching(log_id) &&
+ (start_time_ == log_time::EPOCH || start_time_ <= realtime)) {
+ ++count_;
+ }
+
+ return FilterResult::kSkip;
+}
+
+// A second pass to send the selected elements
+FilterResult LogReaderThread::FilterSecondPass(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime, uint16_t dropped_count) {
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+
+ start_ = sequence;
+
+ if (skip_ahead_[log_id]) {
+ skip_ahead_[log_id]--;
+ return FilterResult::kSkip;
+ }
+
+ if (leading_dropped_) {
+ if (dropped_count) {
+ return FilterResult::kSkip;
+ }
+ leading_dropped_ = false;
+ }
+
+ // Truncate to close race between first and second pass
+ if (non_block_ && tail_ && index_ >= count_) {
+ return FilterResult::kStop;
+ }
+
+ if (!IsWatching(log_id)) {
+ return FilterResult::kSkip;
+ }
+
+ if (pid_ && pid_ != pid) {
+ return FilterResult::kSkip;
+ }
+
+ if (start_time_ != log_time::EPOCH && realtime <= start_time_) {
+ return FilterResult::kSkip;
+ }
+
+ if (release_) {
+ return FilterResult::kStop;
+ }
+
+ if (!tail_) {
+ goto ok;
+ }
+
+ ++index_;
+
+ if (count_ > tail_ && index_ <= (count_ - tail_)) {
+ return FilterResult::kSkip;
+ }
+
+ if (!non_block_) {
+ tail_ = 0;
+ }
+
+ok:
+ if (!skip_ahead_[log_id]) {
+ return FilterResult::kWrite;
+ }
+ return FilterResult::kSkip;
+}
+
+void LogReaderThread::cleanSkip_Locked(void) {
+ memset(skip_ahead_, 0, sizeof(skip_ahead_));
+}
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
new file mode 100644
index 0000000..ba81063
--- /dev/null
+++ b/logd/LogReaderThread.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012-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.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <list>
+#include <memory>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBuffer.h"
+#include "LogWriter.h"
+
+class LogReaderList;
+
+class LogReaderThread {
+ public:
+ LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
+ std::unique_ptr<LogWriter> writer, bool non_block, unsigned long tail,
+ unsigned int log_mask, pid_t pid, log_time start_time, uint64_t sequence,
+ std::chrono::steady_clock::time_point deadline);
+ void triggerReader_Locked() { thread_triggered_condition_.notify_all(); }
+
+ void triggerSkip_Locked(log_id_t id, unsigned int skip) { skip_ahead_[id] = skip; }
+ void cleanSkip_Locked();
+
+ void release_Locked() {
+ // gracefully shut down the socket.
+ writer_->Shutdown();
+ release_ = true;
+ thread_triggered_condition_.notify_all();
+ }
+
+ bool IsWatching(log_id_t id) const { return log_mask_ & (1 << id); }
+ bool IsWatchingMultiple(unsigned int log_mask) const { return log_mask_ & log_mask; }
+
+ std::string name() const { return writer_->name(); }
+ uint64_t start() const { return start_; }
+ std::chrono::steady_clock::time_point deadline() const { return deadline_; }
+
+ private:
+ void ThreadFunction();
+ // flushTo filter callbacks
+ FilterResult FilterFirstPass(log_id_t log_id, pid_t pid, uint64_t sequence, log_time realtime,
+ uint16_t dropped_count);
+ FilterResult FilterSecondPass(log_id_t log_id, pid_t pid, uint64_t sequence, log_time realtime,
+ uint16_t dropped_count);
+
+ std::condition_variable thread_triggered_condition_;
+ LogBuffer* log_buffer_;
+ LogReaderList* reader_list_;
+ std::unique_ptr<LogWriter> writer_;
+
+ // Set to true to cause the thread to end and the LogReaderThread to delete itself.
+ bool release_ = false;
+ // Indicates whether or not 'leading' (first logs seen starting from start_) 'dropped' (chatty)
+ // messages should be ignored.
+ bool leading_dropped_;
+
+ // A mask of the logs buffers that are read by this reader.
+ const unsigned int log_mask_;
+ // If set to non-zero, only pids equal to this are read by the reader.
+ const pid_t pid_;
+ // When a reader is referencing (via start_) old elements in the log buffer, and the log
+ // buffer's size grows past its memory limit, the log buffer may request the reader to skip
+ // ahead a specified number of logs.
+ unsigned int skip_ahead_[LOG_ID_MAX];
+ // Used for distinguishing 'dropped' messages for duplicate logs vs chatty drops
+ pid_t last_tid_[LOG_ID_MAX];
+
+ // These next three variables are used for reading only the most recent lines aka `adb logcat
+ // -t` / `adb logcat -T`.
+ // tail_ is the number of most recent lines to print.
+ unsigned long tail_;
+ // count_ is the result of a first pass through the log buffer to determine how many total
+ // messages there are.
+ unsigned long count_;
+ // index_ is used along with count_ to only start sending lines once index_ > (count_ - tail_)
+ // and to disconnect the reader (if it is dumpAndClose, `adb logcat -t`), when index_ >= count_.
+ unsigned long index_;
+
+ // When a reader requests logs starting from a given timestamp, its stored here for the first
+ // pass, such that logs before this time stamp that are accumulated in the buffer are ignored.
+ log_time start_time_;
+ // The point from which the reader will read logs once awoken.
+ uint64_t start_;
+ // CLOCK_MONOTONIC based deadline used for log wrapping. If this deadline expires before logs
+ // wrap, then wake up and send the logs to the reader anyway.
+ std::chrono::steady_clock::time_point deadline_;
+ // If this reader is 'dumpAndClose' and will disconnect once it has read its intended logs.
+ const bool non_block_;
+};
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 431b778..bb7621d 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogStatistics.h"
+
#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -27,14 +29,12 @@
#include <private/android_logger.h>
-#include "LogStatistics.h"
-
static const uint64_t hourSec = 60 * 60;
static const uint64_t monthSec = 31 * 24 * hourSec;
-size_t LogStatistics::SizesTotal;
+std::atomic<size_t> LogStatistics::SizesTotal;
-LogStatistics::LogStatistics() : enable(false) {
+LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
log_time now(CLOCK_REALTIME);
log_id_for_each(id) {
mSizes[id] = 0;
@@ -79,17 +79,16 @@
}
}
-void LogStatistics::addTotal(LogBufferElement* element) {
- if (element->getDropped()) return;
+void LogStatistics::AddTotal(log_id_t log_id, uint16_t size) {
+ auto lock = std::lock_guard{lock_};
- log_id_t log_id = element->getLogId();
- uint16_t size = element->getMsgLen();
mSizesTotal[log_id] += size;
SizesTotal += size;
++mElementsTotal[log_id];
}
-void LogStatistics::add(LogBufferElement* element) {
+void LogStatistics::Add(LogBufferElement* element) {
+ auto lock = std::lock_guard{lock_};
log_id_t log_id = element->getLogId();
uint16_t size = element->getMsgLen();
mSizes[log_id] += size;
@@ -159,7 +158,8 @@
}
}
-void LogStatistics::subtract(LogBufferElement* element) {
+void LogStatistics::Subtract(LogBufferElement* element) {
+ auto lock = std::lock_guard{lock_};
log_id_t log_id = element->getLogId();
uint16_t size = element->getMsgLen();
mSizes[log_id] -= size;
@@ -204,7 +204,8 @@
// Atomically set an entry to drop
// entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement* element) {
+void LogStatistics::Drop(LogBufferElement* element) {
+ auto lock = std::lock_guard{lock_};
log_id_t log_id = element->getLogId();
uint16_t size = element->getMsgLen();
mSizes[log_id] -= size;
@@ -238,9 +239,13 @@
tagNameTable.subtract(TagNameKey(element), element);
}
+const char* LogStatistics::UidToName(uid_t uid) const {
+ auto lock = std::lock_guard{lock_};
+ return UidToNameLocked(uid);
+}
+
// caller must own and free character string
-// Requires parent LogBuffer::wrlock() to be held
-const char* LogStatistics::uidToName(uid_t uid) const {
+const char* LogStatistics::UidToNameLocked(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
return strdup("auditd");
@@ -297,6 +302,80 @@
return name;
}
+template <typename TKey, typename TEntry>
+void LogStatistics::WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
+ int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const {
+ std::array<const TEntry*, 2> max_entries;
+ table.MaxEntries(AID_ROOT, 0, &max_entries);
+ if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
+ return;
+ }
+ *worst_sizes = max_entries[0]->getSizes();
+ // b/24782000: Allow time horizon to extend roughly tenfold, assume average entry length is
+ // 100 characters.
+ if (*worst_sizes > threshold && *worst_sizes > (10 * max_entries[0]->getDropped())) {
+ *worst = max_entries[0]->getKey();
+ *second_worst_sizes = max_entries[1]->getSizes();
+ if (*second_worst_sizes < threshold) {
+ *second_worst_sizes = threshold;
+ }
+ }
+}
+
+void LogStatistics::WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const {
+ auto lock = std::lock_guard{lock_};
+ WorstTwoWithThreshold(uidTable[id], threshold, worst, worst_sizes, second_worst_sizes);
+}
+
+void LogStatistics::WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const {
+ auto lock = std::lock_guard{lock_};
+ WorstTwoWithThreshold(tagTable, threshold, worst, worst_sizes, second_worst_sizes);
+}
+
+void LogStatistics::WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
+ size_t* second_worst_sizes) const {
+ auto lock = std::lock_guard{lock_};
+ std::array<const PidEntry*, 2> max_entries;
+ pidSystemTable[id].MaxEntries(AID_SYSTEM, 0, &max_entries);
+ if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
+ return;
+ }
+
+ *worst = max_entries[0]->getKey();
+ *second_worst_sizes = worst_uid_sizes - max_entries[0]->getSizes() + max_entries[1]->getSizes();
+}
+
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
+bool LogStatistics::ShouldPrune(log_id id, unsigned long max_size,
+ unsigned long* prune_rows) const {
+ static constexpr size_t kMinPrune = 4;
+ static constexpr size_t kMaxPrune = 256;
+
+ auto lock = std::lock_guard{lock_};
+ size_t sizes = mSizes[id];
+ if (sizes <= max_size) {
+ return false;
+ }
+ size_t size_over = sizes - ((max_size * 9) / 10);
+ size_t elements = mElements[id] - mDroppedElements[id];
+ size_t min_elements = elements / 100;
+ if (min_elements < kMinPrune) {
+ min_elements = kMinPrune;
+ }
+ *prune_rows = elements * size_over / sizes;
+ if (*prune_rows < min_elements) {
+ *prune_rows = min_elements;
+ }
+ if (*prune_rows > kMaxPrune) {
+ *prune_rows = kMaxPrune;
+ }
+
+ return true;
+}
+
std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
bool isprune = worstUidEnabledForLogid(id);
return formatLine(android::base::StringPrintf(name.c_str(),
@@ -308,10 +387,10 @@
}
// Helper to truncate name, if too long, and add name dressings
-static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
- std::string& name, std::string& size, size_t nameLen) {
+void LogStatistics::FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+ size_t nameLen) const {
const char* allocNameTmp = nullptr;
- if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+ if (!nameTmp) nameTmp = allocNameTmp = UidToNameLocked(uid);
if (nameTmp) {
size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
size_t len = EntryBaseConstants::total_len -
@@ -332,12 +411,12 @@
}
}
-std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
+std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const REQUIRES(stat.lock_) {
uid_t uid = getUid();
std::string name = android::base::StringPrintf("%u", uid);
std::string size = android::base::StringPrintf("%zu", getSizes());
- formatTmp(stat, nullptr, uid, name, size, 6);
+ stat.FormatTmp(nullptr, uid, name, size, 6);
std::string pruned = "";
if (worstUidEnabledForLogid(id)) {
@@ -347,9 +426,9 @@
it != stat.uidTable[id].end(); ++it) {
totalDropped += it->second.getDropped();
}
- size_t sizes = stat.sizes(id);
- size_t totalSize = stat.sizesTotal(id);
- size_t totalElements = stat.elementsTotal(id);
+ size_t sizes = stat.mSizes[id];
+ size_t totalSize = stat.mSizesTotal[id];
+ size_t totalElements = stat.mElementsTotal[id];
float totalVirtualSize =
(float)sizes + (float)totalDropped * totalSize / totalElements;
size_t entrySize = getSizes();
@@ -401,12 +480,9 @@
}
static const size_t maximum_sorted_entries = 32;
- std::unique_ptr<const PidEntry* []> sorted =
- stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
+ std::array<const PidEntry*, maximum_sorted_entries> sorted;
+ stat.pidSystemTable[id].MaxEntries(uid, 0, &sorted);
- if (!sorted.get()) {
- return output;
- }
std::string byPid;
size_t index;
bool hasDropped = false;
@@ -440,14 +516,14 @@
std::string("BYTES"), std::string("NUM"));
}
-std::string PidEntry::format(const LogStatistics& stat,
- log_id_t /* id */) const {
+std::string PidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
+ REQUIRES(stat.lock_) {
uid_t uid = getUid();
pid_t pid = getPid();
std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
std::string size = android::base::StringPrintf("%zu", getSizes());
- formatTmp(stat, getName(), uid, name, size, 12);
+ stat.FormatTmp(getName(), uid, name, size, 12);
std::string pruned = "";
size_t dropped = getDropped();
@@ -465,13 +541,13 @@
std::string("NUM"));
}
-std::string TidEntry::format(const LogStatistics& stat,
- log_id_t /* id */) const {
+std::string TidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
+ REQUIRES(stat.lock_) {
uid_t uid = getUid();
std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
std::string size = android::base::StringPrintf("%zu", getSizes());
- formatTmp(stat, getName(), uid, name, size, 12);
+ stat.FormatTmp(getName(), uid, name, size, 12);
std::string pruned = "";
size_t dropped = getDropped();
@@ -611,8 +687,36 @@
return output;
}
-std::string LogStatistics::format(uid_t uid, pid_t pid,
- unsigned int logMask) const {
+template <typename TKey, typename TEntry>
+std::string LogStatistics::FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid,
+ pid_t pid, const std::string& name, log_id_t id) const
+ REQUIRES(lock_) {
+ static const size_t maximum_sorted_entries = 32;
+ std::string output;
+ std::array<const TEntry*, maximum_sorted_entries> sorted;
+ table.MaxEntries(uid, pid, &sorted);
+ bool header_printed = false;
+ for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+ const TEntry* entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+ break;
+ }
+ if (!header_printed) {
+ output += "\n\n";
+ output += entry->formatHeader(name, id);
+ header_printed = true;
+ }
+ output += entry->format(*this, id);
+ }
+ return output;
+}
+
+std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
+ auto lock = std::lock_guard{lock_};
+
static const uint16_t spaces_total = 19;
// Report on total logging, current and for all time
@@ -642,9 +746,9 @@
if (!(logMask & (1 << id))) continue;
oldLength = output.length();
if (spaces < 0) spaces = 0;
- size_t szs = sizesTotal(id);
+ size_t szs = mSizesTotal[id];
totalSize += szs;
- size_t els = elementsTotal(id);
+ size_t els = mElementsTotal[id];
totalEls += els;
output +=
android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
@@ -663,11 +767,11 @@
log_id_for_each(id) {
if (!(logMask & (1 << id))) continue;
- size_t els = elements(id);
+ size_t els = mElements[id];
if (els) {
oldLength = output.length();
if (spaces < 0) spaces = 0;
- size_t szs = sizes(id);
+ size_t szs = mSizes[id];
totalSize += szs;
totalEls += els;
output +=
@@ -749,7 +853,7 @@
log_id_for_each(id) {
if (!(logMask & (1 << id))) continue;
- size_t els = elements(id);
+ size_t els = mElements[id];
if (els) {
oldLength = output.length();
if (spaces < 0) spaces = 0;
@@ -758,7 +862,7 @@
((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
-sizeof(uint64_t)) +
sizeof(std::list<LogBufferElement*>);
- size_t szs = sizes(id) + els * overhead;
+ size_t szs = mSizes[id] + els * overhead;
totalSize += szs;
output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
spaces -= output.length() - oldLength;
@@ -779,39 +883,38 @@
name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
: "Logging for your UID in %s log buffer:";
- output += uidTable[id].format(*this, uid, pid, name, id);
+ output += FormatTable(uidTable[id], uid, pid, name, id);
}
if (enable) {
name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
: "Logging for this PID:";
- output += pidTable.format(*this, uid, pid, name);
+ output += FormatTable(pidTable, uid, pid, name);
name = "Chattiest TIDs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output += tidTable.format(*this, uid, pid, name);
+ output += FormatTable(tidTable, uid, pid, name);
}
if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
name = "Chattiest events log buffer TAGs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
+ output += FormatTable(tagTable, uid, pid, name, LOG_ID_EVENTS);
}
if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
name = "Chattiest security log buffer TAGs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output +=
- securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
+ output += FormatTable(securityTagTable, uid, pid, name, LOG_ID_SECURITY);
}
if (enable) {
name = "Chattiest TAGs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output += tagNameTable.format(*this, uid, pid, name);
+ output += FormatTable(tagNameTable, uid, pid, name);
}
return output;
@@ -839,12 +942,14 @@
}
}
-uid_t LogStatistics::pidToUid(pid_t pid) {
+uid_t LogStatistics::PidToUid(pid_t pid) {
+ auto lock = std::lock_guard{lock_};
return pidTable.add(pid)->second.getUid();
}
// caller must free character string
-const char* LogStatistics::pidToName(pid_t pid) const {
+const char* LogStatistics::PidToName(pid_t pid) const {
+ auto lock = std::lock_guard{lock_};
// An inconvenient truth ... getName() can alter the object
pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
const char* name = writablePidTable.add(pid)->second.getName();
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 0782de3..7d13ff7 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_STATISTICS_H__
-#define _LOGD_LOG_STATISTICS_H__
+#pragma once
#include <ctype.h>
#include <inttypes.h>
@@ -25,12 +24,15 @@
#include <sys/types.h>
#include <algorithm> // std::max
+#include <array>
#include <memory>
+#include <mutex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include <android/log.h>
#include <log/log_time.h>
#include <private/android_filesystem_config.h>
@@ -79,16 +81,12 @@
typedef
typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
- std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
- size_t len) const {
- if (!len) {
- std::unique_ptr<const TEntry* []> sorted(nullptr);
- return sorted;
- }
-
- const TEntry** retval = new const TEntry*[len];
- memset(retval, 0, sizeof(*retval) * len);
-
+ // Returns a sorted array of up to len highest entries sorted by size. If fewer than len
+ // entries are found, their positions are set to nullptr.
+ template <size_t len>
+ void MaxEntries(uid_t uid, pid_t pid, std::array<const TEntry*, len>* out) const {
+ auto& retval = *out;
+ retval.fill(nullptr);
for (const_iterator it = map.begin(); it != map.end(); ++it) {
const TEntry& entry = it->second;
@@ -113,8 +111,6 @@
retval[index] = &entry;
}
}
- std::unique_ptr<const TEntry* []> sorted(retval);
- return sorted;
}
inline iterator add(const TKey& key, const LogBufferElement* element) {
@@ -170,35 +166,6 @@
inline const_iterator end() const {
return map.end();
}
-
- std::string format(const LogStatistics& stat, uid_t uid, pid_t pid,
- const std::string& name = std::string(""),
- log_id_t id = LOG_ID_MAX) const {
- static const size_t maximum_sorted_entries = 32;
- std::string output;
- std::unique_ptr<const TEntry* []> sorted =
- sort(uid, pid, maximum_sorted_entries);
- if (!sorted.get()) {
- return output;
- }
- bool headerPrinted = false;
- for (size_t index = 0; index < maximum_sorted_entries; ++index) {
- const TEntry* entry = sorted[index];
- if (!entry) {
- break;
- }
- if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
- break;
- }
- if (!headerPrinted) {
- output += "\n\n";
- output += entry->formatHeader(name, id);
- headerPrinted = true;
- }
- output += entry->format(stat, id);
- }
- return output;
- }
};
namespace EntryBaseConstants {
@@ -627,84 +594,51 @@
std::string format(const LogStatistics& stat, log_id_t id) const;
};
-template <typename TEntry>
-class LogFindWorst {
- std::unique_ptr<const TEntry* []> sorted;
-
- public:
- explicit LogFindWorst(std::unique_ptr<const TEntry* []>&& sorted)
- : sorted(std::move(sorted)) {
- }
-
- void findWorst(int& worst, size_t& worst_sizes, size_t& second_worst_sizes,
- size_t threshold) {
- if (sorted.get() && sorted[0] && sorted[1]) {
- worst_sizes = sorted[0]->getSizes();
- if ((worst_sizes > threshold)
- // Allow time horizon to extend roughly tenfold, assume
- // average entry length is 100 characters.
- && (worst_sizes > (10 * sorted[0]->getDropped()))) {
- worst = sorted[0]->getKey();
- second_worst_sizes = sorted[1]->getSizes();
- if (second_worst_sizes < threshold) {
- second_worst_sizes = threshold;
- }
- }
- }
- }
-
- void findWorst(int& worst, size_t worst_sizes, size_t& second_worst_sizes) {
- if (sorted.get() && sorted[0] && sorted[1]) {
- worst = sorted[0]->getKey();
- second_worst_sizes =
- worst_sizes - sorted[0]->getSizes() + sorted[1]->getSizes();
- }
- }
-};
-
// Log Statistics
class LogStatistics {
friend UidEntry;
+ friend PidEntry;
+ friend TidEntry;
- size_t mSizes[LOG_ID_MAX];
- size_t mElements[LOG_ID_MAX];
- size_t mDroppedElements[LOG_ID_MAX];
- size_t mSizesTotal[LOG_ID_MAX];
- size_t mElementsTotal[LOG_ID_MAX];
- log_time mOldest[LOG_ID_MAX];
- log_time mNewest[LOG_ID_MAX];
- log_time mNewestDropped[LOG_ID_MAX];
- static size_t SizesTotal;
+ size_t mSizes[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mElements[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mDroppedElements[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mSizesTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mElementsTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+ log_time mOldest[LOG_ID_MAX] GUARDED_BY(lock_);
+ log_time mNewest[LOG_ID_MAX] GUARDED_BY(lock_);
+ log_time mNewestDropped[LOG_ID_MAX] GUARDED_BY(lock_);
+ static std::atomic<size_t> SizesTotal;
bool enable;
// uid to size list
typedef LogHashtable<uid_t, UidEntry> uidTable_t;
- uidTable_t uidTable[LOG_ID_MAX];
+ uidTable_t uidTable[LOG_ID_MAX] GUARDED_BY(lock_);
// pid of system to size list
typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
- pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+ pidSystemTable_t pidSystemTable[LOG_ID_MAX] GUARDED_BY(lock_);
// pid to uid list
typedef LogHashtable<pid_t, PidEntry> pidTable_t;
- pidTable_t pidTable;
+ pidTable_t pidTable GUARDED_BY(lock_);
// tid to uid list
typedef LogHashtable<pid_t, TidEntry> tidTable_t;
- tidTable_t tidTable;
+ tidTable_t tidTable GUARDED_BY(lock_);
// tag list
typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
- tagTable_t tagTable;
+ tagTable_t tagTable GUARDED_BY(lock_);
// security tag list
- tagTable_t securityTagTable;
+ tagTable_t securityTagTable GUARDED_BY(lock_);
// global tag list
typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
tagNameTable_t tagNameTable;
- size_t sizeOf() const {
+ size_t sizeOf() const REQUIRES(lock_) {
size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
tagTable.sizeOf() + securityTagTable.sizeOf() +
tagNameTable.sizeOf() +
@@ -729,62 +663,59 @@
return size;
}
- public:
- LogStatistics();
+ public:
+ LogStatistics(bool enable_statistics);
- void enableStatistics() {
- enable = true;
- }
-
- void addTotal(LogBufferElement* entry);
- void add(LogBufferElement* entry);
- void subtract(LogBufferElement* entry);
+ void AddTotal(log_id_t log_id, uint16_t size) EXCLUDES(lock_);
+ void Add(LogBufferElement* entry) EXCLUDES(lock_);
+ void Subtract(LogBufferElement* entry) EXCLUDES(lock_);
// entry->setDropped(1) must follow this call
- void drop(LogBufferElement* entry);
+ void Drop(LogBufferElement* entry) EXCLUDES(lock_);
// Correct for coalescing two entries referencing dropped content
- void erase(LogBufferElement* element) {
+ void Erase(LogBufferElement* element) EXCLUDES(lock_) {
+ auto lock = std::lock_guard{lock_};
log_id_t log_id = element->getLogId();
--mElements[log_id];
--mDroppedElements[log_id];
}
- LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
- return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
- }
- LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len,
- log_id id) {
- return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
- }
- LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
- return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
- }
+ void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const EXCLUDES(lock_);
+ void WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const EXCLUDES(lock_);
+ void WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
+ size_t* second_worst_sizes) const EXCLUDES(lock_);
- // fast track current value by id only
- size_t sizes(log_id_t id) const {
+ bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const
+ EXCLUDES(lock_);
+
+ // Snapshot of the sizes for a given log buffer.
+ size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
+ auto lock = std::lock_guard{lock_};
return mSizes[id];
}
- size_t elements(log_id_t id) const {
- return mElements[id];
- }
- size_t realElements(log_id_t id) const {
- return mElements[id] - mDroppedElements[id];
- }
- size_t sizesTotal(log_id_t id) const {
- return mSizesTotal[id];
- }
- size_t elementsTotal(log_id_t id) const {
- return mElementsTotal[id];
- }
+ // TODO: Get rid of this entirely.
static size_t sizesTotal() {
return SizesTotal;
}
- std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
+ std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_);
- // helper (must be locked directly or implicitly by mLogElementsLock)
- const char* pidToName(pid_t pid) const;
- uid_t pidToUid(pid_t pid);
- const char* uidToName(uid_t uid) const;
+ const char* PidToName(pid_t pid) const EXCLUDES(lock_);
+ uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
+ const char* UidToName(uid_t uid) const EXCLUDES(lock_);
+
+ private:
+ template <typename TKey, typename TEntry>
+ void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
+ int* worst, size_t* worst_sizes, size_t* second_worst_sizes) const;
+ template <typename TKey, typename TEntry>
+ std::string FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid, pid_t pid,
+ const std::string& name = std::string(""),
+ log_id_t id = LOG_ID_MAX) const REQUIRES(lock_);
+ void FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+ size_t nameLen) const REQUIRES(lock_);
+ const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
+
+ mutable std::mutex lock_;
};
-
-#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 3e52b38..3afe3ee 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -32,11 +32,13 @@
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
#include <log/log_event_list.h>
#include <log/log_properties.h>
#include <log/log_read.h>
#include <private/android_filesystem_config.h>
+#include "LogStatistics.h"
#include "LogTags.h"
#include "LogUtils.h"
@@ -99,8 +101,7 @@
struct tm tm;
localtime_r(&now, &tm);
char timebuf[20];
- size_t len =
- strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
android::base::WriteStringToFd(
android::base::StringPrintf(
"# Rebuilt %.20s, content owned by logd\n", timebuf),
@@ -189,7 +190,6 @@
// Read the event log tags file, and build up our internal database
void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
bool etc = !strcmp(filename, system_event_log_tags);
- bool debug = !etc && !strcmp(filename, debug_event_log_tags);
if (!etc) {
RebuildFileEventLogTags(filename, warn);
@@ -495,7 +495,7 @@
// Every 16K (half the smallest configurable pmsg buffer size) record
static const size_t rate_to_pmsg = 16 * 1024;
- if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+ if (lastTotal && (LogStatistics::sizesTotal() - lastTotal) < rate_to_pmsg) {
return;
}
@@ -548,13 +548,13 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
android_log_header_t header = {
- .id = LOG_ID_EVENTS,
- .tid = (uint16_t)gettid(),
- .realtime.tv_sec = (uint32_t)ts.tv_sec,
- .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+ .id = LOG_ID_EVENTS,
+ .tid = static_cast<uint16_t>(android::base::GetThreadId()),
+ .realtime.tv_sec = static_cast<uint32_t>(ts.tv_sec),
+ .realtime.tv_nsec = static_cast<uint32_t>(ts.tv_nsec),
};
uint32_t outTag = TAG_DEF_LOG_TAG;
@@ -665,7 +665,7 @@
}
}
- lastTotal = android::sizesTotal();
+ lastTotal = LogStatistics::sizesTotal();
if (!lastTotal) ++lastTotal;
// record totals for next watermark.
diff --git a/logd/LogTags.h b/logd/LogTags.h
index e4d165a..cce700c 100644
--- a/logd/LogTags.h
+++ b/logd/LogTags.h
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_TAGS_H__
-#define _LOGD_LOG_TAGS_H__
+#pragma once
#include <string>
#include <unordered_map>
#include <unordered_set>
+#include <private/android_filesystem_config.h>
#include <utils/RWLock.h>
class LogTags {
@@ -120,5 +120,3 @@
std::string formatGetEventTag(uid_t uid, const char* name,
const char* format);
};
-
-#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
deleted file mode 100644
index ad150bd..0000000
--- a/logd/LogTimes.cpp
+++ /dev/null
@@ -1,253 +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 <errno.h>
-#include <string.h>
-#include <sys/prctl.h>
-
-#include "LogBuffer.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-
-pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
-
-LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
- unsigned long tail, log_mask_t logMask, pid_t pid, log_time start_time,
- uint64_t start, uint64_t timeout, bool privileged,
- bool can_read_security_logs)
- : leadingDropped(false),
- mReader(reader),
- mLogMask(logMask),
- mPid(pid),
- mCount(0),
- mTail(tail),
- mIndex(0),
- mClient(client),
- mStartTime(start_time),
- mStart(start),
- mNonBlock(nonBlock),
- privileged_(privileged),
- can_read_security_logs_(can_read_security_logs) {
- mTimeout.tv_sec = timeout / NS_PER_SEC;
- mTimeout.tv_nsec = timeout % NS_PER_SEC;
- memset(mLastTid, 0, sizeof(mLastTid));
- pthread_cond_init(&threadTriggeredCondition, nullptr);
- cleanSkip_Locked();
-}
-
-bool LogTimeEntry::startReader_Locked() {
- pthread_attr_t attr;
-
- 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 true;
- }
- }
- pthread_attr_destroy(&attr);
- }
-
- return false;
-}
-
-void* LogTimeEntry::threadStart(void* obj) {
- prctl(PR_SET_NAME, "logd.reader.per");
-
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- SocketClient* client = me->mClient;
-
- LogBuffer& logbuf = me->mReader.logbuf();
-
- me->leadingDropped = true;
-
- wrlock();
-
- uint64_t start = me->mStart;
-
- while (!me->mRelease) {
- if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
- if (pthread_cond_clockwait(&me->threadTriggeredCondition, ×Lock, CLOCK_MONOTONIC,
- &me->mTimeout) == ETIMEDOUT) {
- me->mTimeout.tv_sec = 0;
- me->mTimeout.tv_nsec = 0;
- }
- if (me->mRelease) {
- break;
- }
- }
-
- unlock();
-
- if (me->mTail) {
- logbuf.flushTo(client, start, nullptr, me->privileged_, me->can_read_security_logs_,
- FilterFirstPass, me);
- me->leadingDropped = true;
- }
- start = logbuf.flushTo(client, start, me->mLastTid, me->privileged_,
- me->can_read_security_logs_, FilterSecondPass, me);
-
- // We only ignore entries before the original start time for the first flushTo(), if we
- // get entries after this first flush before the original start time, then the client
- // wouldn't have seen them.
- // Note: this is still racy and may skip out of order events that came in since the last
- // time the client disconnected and then reconnected with the new start time. The long term
- // solution here is that clients must request events since a specific sequence number.
- me->mStartTime.tv_sec = 0;
- me->mStartTime.tv_nsec = 0;
-
- wrlock();
-
- if (start == LogBufferElement::FLUSH_ERROR) {
- break;
- }
-
- me->mStart = start + 1;
-
- if (me->mNonBlock || me->mRelease) {
- break;
- }
-
- me->cleanSkip_Locked();
-
- if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
- pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
- }
- }
-
- LogReader& reader = me->mReader;
- reader.release(client);
-
- 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;
-}
-
-// A first pass to count the number of elements
-int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- LogTimeEntry::wrlock();
-
- if (me->leadingDropped) {
- if (element->getDropped()) {
- LogTimeEntry::unlock();
- return false;
- }
- me->leadingDropped = false;
- }
-
- if (me->mCount == 0) {
- me->mStart = element->getSequence();
- }
-
- if ((!me->mPid || me->mPid == element->getPid()) && me->isWatching(element->getLogId()) &&
- (me->mStartTime == log_time::EPOCH || me->mStartTime <= element->getRealTime())) {
- ++me->mCount;
- }
-
- LogTimeEntry::unlock();
-
- return false;
-}
-
-// A second pass to send the selected elements
-int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- LogTimeEntry::wrlock();
-
- me->mStart = element->getSequence();
-
- if (me->skipAhead[element->getLogId()]) {
- me->skipAhead[element->getLogId()]--;
- goto skip;
- }
-
- if (me->leadingDropped) {
- if (element->getDropped()) {
- goto skip;
- }
- me->leadingDropped = false;
- }
-
- // Truncate to close race between first and second pass
- if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
- goto stop;
- }
-
- if (!me->isWatching(element->getLogId())) {
- goto skip;
- }
-
- if (me->mPid && (me->mPid != element->getPid())) {
- goto skip;
- }
-
- if (me->mStartTime != log_time::EPOCH && element->getRealTime() <= me->mStartTime) {
- goto skip;
- }
-
- if (me->mRelease) {
- goto stop;
- }
-
- if (!me->mTail) {
- goto ok;
- }
-
- ++me->mIndex;
-
- if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
- goto skip;
- }
-
- if (!me->mNonBlock) {
- me->mTail = 0;
- }
-
-ok:
- if (!me->skipAhead[element->getLogId()]) {
- LogTimeEntry::unlock();
- return true;
- }
-// FALLTHRU
-
-skip:
- LogTimeEntry::unlock();
- return false;
-
-stop:
- LogTimeEntry::unlock();
- return -1;
-}
-
-void LogTimeEntry::cleanSkip_Locked(void) {
- memset(skipAhead, 0, sizeof(skipAhead));
-}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
deleted file mode 100644
index 56c930a..0000000
--- a/logd/LogTimes.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2012-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 _LOGD_LOG_TIMES_H__
-#define _LOGD_LOG_TIMES_H__
-
-#include <pthread.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <list>
-#include <memory>
-
-#include <log/log.h>
-#include <sysutils/SocketClient.h>
-
-typedef unsigned int log_mask_t;
-
-class LogReader;
-class LogBufferElement;
-
-class LogTimeEntry {
- static pthread_mutex_t timesLock;
- bool mRelease = false;
- bool leadingDropped;
- pthread_cond_t threadTriggeredCondition;
- pthread_t mThread;
- LogReader& mReader;
- static void* threadStart(void* me);
- const log_mask_t mLogMask;
- const pid_t mPid;
- unsigned int skipAhead[LOG_ID_MAX];
- pid_t mLastTid[LOG_ID_MAX];
- unsigned long mCount;
- unsigned long mTail;
- unsigned long mIndex;
-
- public:
- LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock, unsigned long tail,
- log_mask_t logMask, pid_t pid, log_time start_time, uint64_t sequence,
- uint64_t timeout, bool privileged, bool can_read_security_logs);
-
- SocketClient* mClient;
- log_time mStartTime;
- uint64_t mStart;
- struct timespec mTimeout; // CLOCK_MONOTONIC based timeout used for log wrapping.
- const bool mNonBlock;
-
- // Protect List manipulations
- static void wrlock(void) {
- pthread_mutex_lock(×Lock);
- }
- static void rdlock(void) {
- pthread_mutex_lock(×Lock);
- }
- static void unlock(void) {
- pthread_mutex_unlock(×Lock);
- }
-
- bool startReader_Locked();
-
- void triggerReader_Locked(void) {
- pthread_cond_signal(&threadTriggeredCondition);
- }
-
- void triggerSkip_Locked(log_id_t id, unsigned int skip) {
- skipAhead[id] = skip;
- }
- void cleanSkip_Locked(void);
-
- void release_Locked(void) {
- // gracefully shut down the socket.
- shutdown(mClient->getSocket(), SHUT_RDWR);
- mRelease = true;
- pthread_cond_signal(&threadTriggeredCondition);
- }
-
- bool isWatching(log_id_t id) const {
- return mLogMask & (1 << id);
- }
- bool isWatchingMultiple(log_mask_t logMask) const {
- return mLogMask & logMask;
- }
- // flushTo filter callbacks
- static int FilterFirstPass(const LogBufferElement* element, void* me);
- static int FilterSecondPass(const LogBufferElement* element, void* me);
-
- private:
- bool privileged_;
- bool can_read_security_logs_;
-};
-
-typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
-
-#endif // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index f9cd42d..c472167 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -30,10 +30,8 @@
// Furnished in main.cpp. Caller must own and free returned value
char* uidToName(uid_t uid);
-void prdebug(const char* fmt, ...) __printflike(1, 2);
+void prdebug(const char* fmt, ...) __attribute__((__format__(printf, 1, 2)));
-// Furnished in LogStatistics.cpp.
-size_t sizesTotal();
// Caller must own and free returned value
char* pidToName(pid_t pid);
char* tidToName(pid_t tid);
@@ -62,10 +60,6 @@
}
}
-// Furnished in LogCommand.cpp
-bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
-bool clientHasLogCredentials(SocketClient* cli);
-
static inline bool worstUidEnabledForLogid(log_id_t id) {
return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
(id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 9d762dc..88a3bdc 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -16,13 +16,11 @@
#include <ctype.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
#include "LogWhiteBlackList.h"
-// White and Black list
-
Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
}
@@ -75,14 +73,12 @@
it = mNaughty.erase(it);
}
- static const char _default[] = "default";
// default here means take ro.logd.filter, persist.logd.filter then
// internal default in that order.
- if (str && !strcmp(str, _default)) {
+ if (str && !strcmp(str, "default")) {
str = nullptr;
}
- static const char _disable[] = "disable";
- if (str && !strcmp(str, _disable)) {
+ if (str && !strcmp(str, "disable")) {
str = "";
}
@@ -91,22 +87,20 @@
if (str) {
filter = str;
} else {
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.logd.filter", property, _default);
- filter = property;
- property_get("persist.logd.filter", property, filter.c_str());
+ filter = android::base::GetProperty("ro.logd.filter", "default");
+ auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
// default here means take ro.logd.filter
- if (strcmp(property, _default)) {
- filter = property;
+ if (persist_filter != "default") {
+ filter = persist_filter;
}
}
// default here means take internal default.
- if (filter == _default) {
+ if (filter == "default") {
// See README.property for description of filter format
filter = "~! ~1000/!";
}
- if (filter == _disable) {
+ if (filter == "disable") {
filter = "";
}
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 6e9893b..0e4e837 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__
-#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+#pragma once
#include <sys/types.h>
@@ -24,8 +23,6 @@
#include "LogBufferElement.h"
-// White and Blacklist
-
class Prune {
friend class PruneList;
@@ -84,5 +81,3 @@
std::string format();
};
-
-#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/LogWriter.h b/logd/LogWriter.h
new file mode 100644
index 0000000..b6c5b67
--- /dev/null
+++ b/logd/LogWriter.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <log/log_read.h>
+
+// An interface for writing logs to a reader.
+class LogWriter {
+ public:
+ LogWriter(uid_t uid, bool privileged, bool can_read_security_logs)
+ : uid_(uid), privileged_(privileged), can_read_security_logs_(can_read_security_logs) {}
+ virtual ~LogWriter() {}
+
+ virtual bool Write(const logger_entry& entry, const char* msg) = 0;
+ virtual void Shutdown() {}
+ virtual void Release() {}
+
+ virtual std::string name() const = 0;
+ uid_t uid() const { return uid_; }
+
+ bool privileged() const { return privileged_; }
+ bool can_read_security_logs() const { return can_read_security_logs_; }
+
+ private:
+ uid_t uid_;
+
+ // If this writer sees logs from all UIDs or only its own UID. See clientHasLogCredentials().
+ bool privileged_;
+ bool can_read_security_logs_; // If this writer sees security logs. See CanReadSecurityLogs().
+};
\ No newline at end of file
diff --git a/logd/README.property b/logd/README.property
index 1b7e165..6a9369a 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -44,10 +44,6 @@
oldest entries of chattiest UID, and
the chattiest PID of system
(1000, or AID_SYSTEM).
-persist.logd.timestamp string ro The recording timestamp source.
- "m[onotonic]" is the only supported
- key character, otherwise realtime.
-ro.logd.timestamp string realtime default for persist.logd.timestamp
log.tag string persist The global logging level, VERBOSE,
DEBUG, INFO, WARN, ERROR, ASSERT or
SILENT. Only the first character is
diff --git a/logd/SimpleLogBuffer.cpp b/logd/SimpleLogBuffer.cpp
new file mode 100644
index 0000000..8a11b92
--- /dev/null
+++ b/logd/SimpleLogBuffer.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2020 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 "SimpleLogBuffer.h"
+
+#include "LogBufferElement.h"
+
+SimpleLogBuffer::SimpleLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats)
+ : reader_list_(reader_list), tags_(tags), stats_(stats) {
+ Init();
+}
+
+SimpleLogBuffer::~SimpleLogBuffer() {}
+
+void SimpleLogBuffer::Init() {
+ log_id_for_each(i) {
+ if (SetSize(i, __android_logger_get_buffer_size(i))) {
+ SetSize(i, LOG_BUFFER_MIN_SIZE);
+ }
+ }
+
+ // Release any sleeping reader threads to dump their current content.
+ auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ reader_thread->triggerReader_Locked();
+ }
+}
+
+std::list<LogBufferElement>::iterator SimpleLogBuffer::GetOldest(log_id_t log_id) {
+ auto it = logs().begin();
+ if (oldest_[log_id]) {
+ it = *oldest_[log_id];
+ }
+ while (it != logs().end() && it->getLogId() != log_id) {
+ it++;
+ }
+ if (it != logs().end()) {
+ oldest_[log_id] = it;
+ }
+ return it;
+}
+
+bool SimpleLogBuffer::ShouldLog(log_id_t log_id, const char* msg, uint16_t len) {
+ if (log_id == LOG_ID_SECURITY) {
+ return true;
+ }
+
+ int prio = ANDROID_LOG_INFO;
+ const char* tag = nullptr;
+ size_t tag_len = 0;
+ if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
+ if (len < sizeof(android_event_header_t)) {
+ return false;
+ }
+ int32_t numeric_tag = reinterpret_cast<const android_event_header_t*>(msg)->tag;
+ tag = tags_->tagToName(numeric_tag);
+ if (tag) {
+ tag_len = strlen(tag);
+ }
+ } else {
+ prio = *msg;
+ tag = msg + 1;
+ tag_len = strnlen(tag, len - 1);
+ }
+ return __android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE);
+}
+
+int SimpleLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len) {
+ if (log_id >= LOG_ID_MAX) {
+ return -EINVAL;
+ }
+
+ if (!ShouldLog(log_id, msg, len)) {
+ // Log traffic received to total
+ stats_->AddTotal(log_id, len);
+ return -EACCES;
+ }
+
+ // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+ // This prevents any chance that an outside source can request an
+ // exact entry with time specified in ms or us precision.
+ if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
+ auto lock = std::lock_guard{lock_};
+ auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);
+ LogInternal(LogBufferElement(log_id, realtime, uid, pid, tid, sequence, msg, len));
+ return len;
+}
+
+void SimpleLogBuffer::LogInternal(LogBufferElement&& elem) {
+ log_id_t log_id = elem.getLogId();
+
+ logs_.emplace_back(std::move(elem));
+ stats_->Add(&logs_.back());
+ MaybePrune(log_id);
+ reader_list_->NotifyNewLog(1 << log_id);
+}
+
+uint64_t SimpleLogBuffer::FlushTo(
+ LogWriter* writer, uint64_t start, pid_t* last_tid,
+ const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime, uint16_t dropped_count)>& filter) {
+ auto shared_lock = SharedLock{lock_};
+
+ std::list<LogBufferElement>::iterator it;
+ if (start <= 1) {
+ // client wants to start from the beginning
+ it = logs_.begin();
+ } else {
+ // Client wants to start from some specified time. Chances are
+ // we are better off starting from the end of the time sorted list.
+ for (it = logs_.end(); it != logs_.begin();
+ /* do nothing */) {
+ --it;
+ if (it->getSequence() <= start) {
+ it++;
+ break;
+ }
+ }
+ }
+
+ uint64_t curr = start;
+
+ for (; it != logs_.end(); ++it) {
+ LogBufferElement& element = *it;
+
+ if (!writer->privileged() && element.getUid() != writer->uid()) {
+ continue;
+ }
+
+ if (!writer->can_read_security_logs() && element.getLogId() == LOG_ID_SECURITY) {
+ continue;
+ }
+
+ if (filter) {
+ FilterResult ret = filter(element.getLogId(), element.getPid(), element.getSequence(),
+ element.getRealTime(), element.getDropped());
+ if (ret == FilterResult::kSkip) {
+ continue;
+ }
+ if (ret == FilterResult::kStop) {
+ break;
+ }
+ }
+
+ bool same_tid = false;
+ if (last_tid) {
+ same_tid = last_tid[element.getLogId()] == element.getTid();
+ // Dropped (chatty) immediately following a valid log from the
+ // same source in the same log buffer indicates we have a
+ // multiple identical squash. chatty that differs source
+ // is due to spam filter. chatty to chatty of different
+ // source is also due to spam filter.
+ last_tid[element.getLogId()] =
+ (element.getDropped() && !same_tid) ? 0 : element.getTid();
+ }
+
+ shared_lock.unlock();
+
+ // We never prune logs equal to or newer than any LogReaderThreads' `start` value, so the
+ // `element` pointer is safe here without the lock
+ curr = element.getSequence();
+ if (!element.FlushTo(writer, stats_, same_tid)) {
+ return FLUSH_ERROR;
+ }
+
+ shared_lock.lock_shared();
+ }
+
+ return curr;
+}
+
+// clear all rows of type "id" from the buffer.
+bool SimpleLogBuffer::Clear(log_id_t id, uid_t uid) {
+ bool busy = true;
+ // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+ for (int retry = 4;;) {
+ if (retry == 1) { // last pass
+ // Check if it is still busy after the sleep, we say prune
+ // one entry, not another clear run, so we are looking for
+ // the quick side effect of the return value to tell us if
+ // we have a _blocked_ reader.
+ {
+ auto lock = std::lock_guard{lock_};
+ busy = Prune(id, 1, uid);
+ }
+ // It is still busy, blocked reader(s), lets kill them all!
+ // otherwise, lets be a good citizen and preserve the slow
+ // readers and let the clear run (below) deal with determining
+ // if we are still blocked and return an error code to caller.
+ if (busy) {
+ auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ if (reader_thread->IsWatching(id)) {
+ android::prdebug("Kicking blocked reader, %s, from LogBuffer::clear()\n",
+ reader_thread->name().c_str());
+ reader_thread->release_Locked();
+ }
+ }
+ }
+ }
+ {
+ auto lock = std::lock_guard{lock_};
+ busy = Prune(id, ULONG_MAX, uid);
+ }
+
+ if (!busy || !--retry) {
+ break;
+ }
+ sleep(1); // Let reader(s) catch up after notification
+ }
+ return busy;
+}
+
+// get the total space allocated to "id"
+unsigned long SimpleLogBuffer::GetSize(log_id_t id) {
+ auto lock = SharedLock{lock_};
+ size_t retval = max_size_[id];
+ return retval;
+}
+
+// set the total space allocated to "id"
+int SimpleLogBuffer::SetSize(log_id_t id, unsigned long size) {
+ // Reasonable limits ...
+ if (!__android_logger_valid_buffer_size(size)) {
+ return -1;
+ }
+
+ auto lock = std::lock_guard{lock_};
+ max_size_[id] = size;
+ return 0;
+}
+
+void SimpleLogBuffer::MaybePrune(log_id_t id) {
+ unsigned long prune_rows;
+ if (stats_->ShouldPrune(id, max_size_[id], &prune_rows)) {
+ Prune(id, prune_rows, 0);
+ }
+}
+
+bool SimpleLogBuffer::Prune(log_id_t id, unsigned long prune_rows, uid_t caller_uid) {
+ auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+
+ // Don't prune logs that are newer than the point at which any reader threads are reading from.
+ LogReaderThread* oldest = nullptr;
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ if (!reader_thread->IsWatching(id)) {
+ continue;
+ }
+ if (!oldest || oldest->start() > reader_thread->start() ||
+ (oldest->start() == reader_thread->start() &&
+ reader_thread->deadline().time_since_epoch().count() != 0)) {
+ oldest = reader_thread.get();
+ }
+ }
+
+ auto it = GetOldest(id);
+
+ while (it != logs_.end()) {
+ LogBufferElement& element = *it;
+
+ if (element.getLogId() != id) {
+ ++it;
+ continue;
+ }
+
+ if (caller_uid != 0 && element.getUid() != caller_uid) {
+ ++it;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.getSequence()) {
+ KickReader(oldest, id, prune_rows);
+ return true;
+ }
+
+ stats_->Subtract(&element);
+ it = Erase(it);
+ if (--prune_rows == 0) {
+ return false;
+ }
+ }
+ return false;
+}
+
+std::list<LogBufferElement>::iterator SimpleLogBuffer::Erase(
+ std::list<LogBufferElement>::iterator it) {
+ bool oldest_is_it[LOG_ID_MAX];
+ log_id_for_each(i) { oldest_is_it[i] = oldest_[i] && it == *oldest_[i]; }
+
+ it = logs_.erase(it);
+
+ log_id_for_each(i) {
+ if (oldest_is_it[i]) {
+ if (__predict_false(it == logs().end())) {
+ oldest_[i] = std::nullopt;
+ } else {
+ oldest_[i] = it; // Store the next iterator even if it does not correspond to
+ // the same log_id, as a starting point for GetOldest().
+ }
+ }
+ }
+
+ return it;
+}
+
+// If the selected reader is blocking our pruning progress, decide on
+// what kind of mitigation is necessary to unblock the situation.
+void SimpleLogBuffer::KickReader(LogReaderThread* reader, log_id_t id, unsigned long prune_rows) {
+ if (stats_->Sizes(id) > (2 * max_size_[id])) { // +100%
+ // A misbehaving or slow reader has its connection
+ // dropped if we hit too much memory pressure.
+ android::prdebug("Kicking blocked reader, %s, from LogBuffer::kickMe()\n",
+ reader->name().c_str());
+ reader->release_Locked();
+ } else if (reader->deadline().time_since_epoch().count() != 0) {
+ // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
+ reader->triggerReader_Locked();
+ } else {
+ // tell slow reader to skip entries to catch up
+ android::prdebug("Skipping %lu entries from slow reader, %s, from LogBuffer::kickMe()\n",
+ prune_rows, reader->name().c_str());
+ reader->triggerSkip_Locked(id, prune_rows);
+ }
+}
diff --git a/logd/SimpleLogBuffer.h b/logd/SimpleLogBuffer.h
new file mode 100644
index 0000000..72d26b0
--- /dev/null
+++ b/logd/SimpleLogBuffer.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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 <atomic>
+#include <list>
+#include <mutex>
+
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReaderList.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "rwlock.h"
+
+class SimpleLogBuffer : public LogBuffer {
+ public:
+ SimpleLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats);
+ ~SimpleLogBuffer();
+ void Init() override;
+
+ int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+ uint16_t len) override;
+ uint64_t FlushTo(LogWriter* writer, uint64_t start, pid_t* lastTid,
+ const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime, uint16_t dropped_count)>&
+ filter) override;
+
+ bool Clear(log_id_t id, uid_t uid) override;
+ unsigned long GetSize(log_id_t id) override;
+ int SetSize(log_id_t id, unsigned long size) override;
+
+ uint64_t sequence() const override { return sequence_.load(std::memory_order_relaxed); }
+
+ protected:
+ virtual bool Prune(log_id_t id, unsigned long prune_rows, uid_t uid) REQUIRES(lock_);
+ virtual void LogInternal(LogBufferElement&& elem) REQUIRES(lock_);
+
+ // Returns an iterator to the oldest element for a given log type, or logs_.end() if
+ // there are no logs for the given log type. Requires logs_lock_ to be held.
+ std::list<LogBufferElement>::iterator GetOldest(log_id_t log_id) REQUIRES(lock_);
+ std::list<LogBufferElement>::iterator Erase(std::list<LogBufferElement>::iterator it)
+ REQUIRES(lock_);
+ void KickReader(LogReaderThread* reader, log_id_t id, unsigned long prune_rows)
+ REQUIRES_SHARED(lock_);
+
+ LogStatistics* stats() { return stats_; }
+ LogReaderList* reader_list() { return reader_list_; }
+ unsigned long max_size(log_id_t id) REQUIRES_SHARED(lock_) { return max_size_[id]; }
+ std::list<LogBufferElement>& logs() { return logs_; }
+
+ RwLock lock_;
+
+ private:
+ bool ShouldLog(log_id_t log_id, const char* msg, uint16_t len);
+ void MaybePrune(log_id_t id) REQUIRES(lock_);
+
+ LogReaderList* reader_list_;
+ LogTags* tags_;
+ LogStatistics* stats_;
+
+ std::atomic<uint64_t> sequence_ = 1;
+
+ unsigned long max_size_[LOG_ID_MAX] GUARDED_BY(lock_);
+ std::list<LogBufferElement> logs_ GUARDED_BY(lock_);
+ // Keeps track of the iterator to the oldest log message of a given log type, as an
+ // optimization when pruning logs. Use GetOldest() to retrieve.
+ std::optional<std::list<LogBufferElement>::iterator> oldest_[LOG_ID_MAX] GUARDED_BY(lock_);
+};
diff --git a/logd/fuzz/Android.bp b/logd/fuzz/Android.bp
index 299242d..f65fbdf 100644
--- a/logd/fuzz/Android.bp
+++ b/logd/fuzz/Android.bp
@@ -25,6 +25,7 @@
"liblog",
"liblogd",
"libcutils",
+ "libsysutils",
],
cflags: ["-Werror"],
}
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 14c5163..8f90f50 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -15,8 +15,10 @@
*/
#include <string>
-#include "../LogBuffer.h"
-#include "../LogTimes.h"
+#include "../ChattyLogBuffer.h"
+#include "../LogReaderList.h"
+#include "../LogReaderThread.h"
+#include "../LogStatistics.h"
// We don't want to waste a lot of entropy on messages
#define MAX_MSG_LENGTH 5
@@ -36,7 +38,8 @@
unsigned int log_mask;
};
-int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
+int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer,
+ LogStatistics* stats) {
const uint8_t* data = *pdata;
const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
data += sizeof(LogInput);
@@ -69,9 +72,9 @@
// Other elements not in enum.
log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
- log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
+ log_buffer->Log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
sizeof(uint32_t) + msg_length + 1);
- log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
+ stats->Format(logInput->uid, logInput->pid, logInput->log_mask);
*pdata = data;
return 1;
}
@@ -93,25 +96,25 @@
return 0;
}
- LastLogTimes times;
+ LogReaderList reader_list;
LogTags tags;
PruneList prune_list;
- LogBuffer log_buffer(×, &tags, &prune_list);
+ LogStatistics stats(true);
+ LogBuffer* log_buffer = new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats);
size_t data_left = size;
const uint8_t** pdata = &data;
- log_buffer.enableStatistics();
prune_list.init(nullptr);
// We want to get pruning code to get called.
- log_id_for_each(i) { log_buffer.setSize(i, 10000); }
+ log_id_for_each(i) { log_buffer->SetSize(i, 10000); }
while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
- if (!write_log_messages(pdata, &data_left, &log_buffer)) {
+ if (!write_log_messages(pdata, &data_left, log_buffer, &stats)) {
return 0;
}
}
- log_id_for_each(i) { log_buffer.clear(i); }
+ log_id_for_each(i) { log_buffer->Clear(i, 0); }
return 0;
}
} // namespace android
diff --git a/logd/libaudit.c b/logd/libaudit.cpp
similarity index 67%
rename from logd/libaudit.c
rename to logd/libaudit.cpp
index f452c71..ccea0a2 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.cpp
@@ -18,11 +18,13 @@
*
*/
+#include "libaudit.h"
+
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include "libaudit.h"
+#include <limits>
/**
* Waits for an ack from the kernel
@@ -32,22 +34,15 @@
* This function returns 0 on success, else -errno.
*/
static int get_ack(int fd) {
- int rc;
- struct audit_message rep;
-
- /* Sanity check, this is an internal interface this shouldn't happen */
- if (fd < 0) {
- return -EINVAL;
- }
-
- rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+ struct audit_message rep = {};
+ int rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
if (rc < 0) {
return rc;
}
if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
- rc = ((struct nlmsgerr*)rep.data)->error;
+ rc = reinterpret_cast<struct nlmsgerr*>(rep.data)->error;
if (rc) {
return -rc;
}
@@ -70,19 +65,11 @@
* This function returns a positive sequence number on success, else -errno.
*/
static int audit_send(int fd, int type, const void* data, size_t size) {
- int rc;
- static int16_t sequence = 0;
- struct audit_message req;
- struct sockaddr_nl addr;
-
- memset(&req, 0, sizeof(req));
- memset(&addr, 0, sizeof(addr));
-
- /* We always send netlink messaged */
- addr.nl_family = AF_NETLINK;
+ struct sockaddr_nl addr = {.nl_family = AF_NETLINK};
/* Set up the netlink headers */
- req.nlh.nlmsg_type = type;
+ struct audit_message req = {};
+ req.nlh.nlmsg_type = static_cast<uint16_t>(type);
req.nlh.nlmsg_len = NLMSG_SPACE(size);
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
@@ -107,29 +94,23 @@
/*
* Only increment the sequence number on a guarantee
* you will send it to the kernel.
- *
- * Also, the sequence is defined as a u32 in the kernel
- * struct. Using an int here might not work on 32/64 bit splits. A
- * signed 64 bit value can overflow a u32..but a u32
- * might not fit in the response, so we need to use s32.
- * Which is still kind of hackish since int could be 16 bits
- * in size. The only safe type to use here is a signed 16
- * bit value.
*/
- req.nlh.nlmsg_seq = ++sequence;
+ static uint32_t sequence = 0;
+ if (sequence == std::numeric_limits<uint32_t>::max()) {
+ sequence = 1;
+ } else {
+ sequence++;
+ }
+ req.nlh.nlmsg_seq = sequence;
- /* While failing and its due to interrupts */
-
- rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
- (struct sockaddr*)&addr, sizeof(addr)));
+ ssize_t rc = TEMP_FAILURE_RETRY(
+ sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr)));
/* Not all the bytes were sent */
if (rc < 0) {
- rc = -errno;
- goto out;
+ return -errno;
} else if ((uint32_t)rc != req.nlh.nlmsg_len) {
- rc = -EPROTO;
- goto out;
+ return -EPROTO;
}
/* We sent all the bytes, get the ack */
@@ -138,32 +119,22 @@
/* If the ack failed, return the error, else return the sequence number */
rc = (rc == 0) ? (int)sequence : rc;
-out:
- /* Don't let sequence roll to negative */
- if (sequence < 0) {
- sequence = 0;
- }
-
return rc;
}
int audit_setup(int fd, pid_t pid) {
- int rc;
- struct audit_message rep;
- struct audit_status status;
-
- memset(&status, 0, sizeof(status));
-
/*
* In order to set the auditd PID we send an audit message over the netlink
* socket with the pid field of the status struct set to our current pid,
* and the the mask set to AUDIT_STATUS_PID
*/
- status.pid = pid;
- status.mask = AUDIT_STATUS_PID;
+ struct audit_status status = {
+ .mask = AUDIT_STATUS_PID,
+ .pid = static_cast<uint32_t>(pid),
+ };
/* Let the kernel know this pid will be registering for audit events */
- rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+ int rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
if (rc < 0) {
return rc;
}
@@ -178,6 +149,7 @@
* so I went to non-blocking and it seemed to fix the bug.
* Need to investigate further.
*/
+ struct audit_message rep = {};
audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
return 0;
@@ -188,27 +160,18 @@
}
int audit_rate_limit(int fd, uint32_t limit) {
- struct audit_status status;
- memset(&status, 0, sizeof(status));
- status.mask = AUDIT_STATUS_RATE_LIMIT;
- status.rate_limit = limit; /* audit entries per second */
+ struct audit_status status = {
+ .mask = AUDIT_STATUS_RATE_LIMIT, .rate_limit = limit, /* audit entries per second */
+ };
return audit_send(fd, AUDIT_SET, &status, sizeof(status));
}
int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
- ssize_t len;
- int flags;
- int rc = 0;
-
- struct sockaddr_nl nladdr;
- socklen_t nladdrlen = sizeof(nladdr);
-
if (fd < 0) {
return -EBADF;
}
- /* Set up the flags for recv from */
- flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+ int flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
flags |= peek;
/*
@@ -216,19 +179,20 @@
* the interface shows that EINTR can never be returned, other errors,
* however, can be returned.
*/
- len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
- (struct sockaddr*)&nladdr, &nladdrlen));
+ struct sockaddr_nl nladdr;
+ socklen_t nladdrlen = sizeof(nladdr);
+ ssize_t len = TEMP_FAILURE_RETRY(
+ recvfrom(fd, rep, sizeof(*rep), flags, (struct sockaddr*)&nladdr, &nladdrlen));
/*
* EAGAIN should be re-tried until success or another error manifests.
*/
if (len < 0) {
- rc = -errno;
- if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+ if (block == GET_REPLY_NONBLOCKING && errno == EAGAIN) {
/* If request is non blocking and errno is EAGAIN, just return 0 */
return 0;
}
- return rc;
+ return -errno;
}
if (nladdrlen != sizeof(nladdr)) {
@@ -242,10 +206,10 @@
/* Check if the reply from the kernel was ok */
if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
- rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+ return len == sizeof(*rep) ? -EFBIG : -EBADE;
}
- return rc;
+ return 0;
}
void audit_close(int fd) {
diff --git a/logd/libaudit.h b/logd/libaudit.h
index b4a92a8..27b0866 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -17,8 +17,7 @@
* Written by William Roberts <w.roberts@sta.samsung.com>
*/
-#ifndef _LIBAUDIT_H_
-#define _LIBAUDIT_H_
+#pragma once
#include <stdint.h>
#include <sys/cdefs.h>
@@ -102,5 +101,3 @@
extern int audit_rate_limit(int fd, uint32_t limit);
__END_DECLS
-
-#endif
diff --git a/logd/tests/logd_test.cpp b/logd/logd_test.cpp
similarity index 98%
rename from logd/tests/logd_test.cpp
rename to logd/logd_test.cpp
index 1dd5c86..ed34ea4 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -39,7 +39,7 @@
#include <selinux/selinux.h>
#endif
-#include "../LogReader.h" // pickup LOGD_SNDTIMEO
+#include "LogReader.h" // pickup LOGD_SNDTIMEO
#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
@@ -606,7 +606,7 @@
// A few tries to get it right just in case wrap kicks in due to
// content providers being active during the test.
int i = 5;
- log_time start(android_log_clockid());
+ log_time start(CLOCK_REALTIME);
start.tv_sec -= 30; // reach back a moderate period of time
while (--i) {
@@ -682,7 +682,7 @@
if (msg > start) {
start = msg;
start.tv_sec += 30;
- log_time now = log_time(android_log_clockid());
+ log_time now = log_time(CLOCK_REALTIME);
if (start > now) {
start = now;
--start.tv_sec;
@@ -904,10 +904,10 @@
(log_msg.entry.len == (4 + 1 + 8))) {
if (tag != 0) continue;
- log_time tx(eventData + 4 + 1);
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+ if (ts == *tx) {
++count;
- } else if (ts1 == tx) {
+ } else if (ts1 == *tx) {
++second_count;
}
} else if (eventData[4] == EVENT_TYPE_STRING) {
diff --git a/logd/main.cpp b/logd/main.cpp
index cc45eb3..c2b5a1d 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -38,7 +38,6 @@
#include <android-base/macros.h>
#include <cutils/android_get_control_file.h>
-#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
#include <packagelistparser/packagelistparser.h>
@@ -47,13 +46,17 @@
#include <processgroup/sched_policy.h>
#include <utils/threads.h>
+#include "ChattyLogBuffer.h"
#include "CommandListener.h"
#include "LogAudit.h"
#include "LogBuffer.h"
#include "LogKlog.h"
#include "LogListener.h"
+#include "LogReader.h"
+#include "LogStatistics.h"
#include "LogTags.h"
#include "LogUtils.h"
+#include "SimpleLogBuffer.h"
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
@@ -109,21 +112,6 @@
return 0;
}
-// Property helper
-static bool check_flag(const char* prop, const char* flag) {
- const char* cp = strcasestr(prop, flag);
- if (!cp) {
- return false;
- }
- // We only will document comma (,)
- static const char sep[] = ",:;|+ \t\f";
- if ((cp != prop) && !strchr(sep, cp[-1])) {
- return false;
- }
- cp += strlen(flag);
- return !*cp || !!strchr(sep, *cp);
-}
-
static int fdDmesg = -1;
void android::prdebug(const char* fmt, ...) {
if (fdDmesg < 0) {
@@ -201,10 +189,6 @@
}
buf[--len] = '\0';
- if (kl && kl->isMonotonic()) {
- kl->synchronize(buf.get(), len);
- }
-
ssize_t sublen;
for (char *ptr = nullptr, *tok = buf.get();
(rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
@@ -289,31 +273,32 @@
// A cache of event log tags
LogTags log_tags;
+
// Pruning configuration.
PruneList prune_list;
+ // Partial (required for chatty) or full logging statistics.
+ bool enable_full_log_statistics = __android_logger_property_get_bool(
+ "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
+ BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ LogStatistics log_statistics(enable_full_log_statistics);
+
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
// entries.
+ LogReaderList reader_list;
- LastLogTimes* times = new LastLogTimes();
-
- // LogBuffer is the object which is responsible for holding all
- // log entries.
-
- LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
-
- if (__android_logger_property_get_bool(
- "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
- BOOL_DEFAULT_FLAG_ENG |
- BOOL_DEFAULT_FLAG_SVELTE)) {
- logBuf->enableStatistics();
+ // LogBuffer is the object which is responsible for holding all log entries.
+ LogBuffer* logBuf;
+ if (true) {
+ logBuf = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+ } else {
+ logBuf = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
}
// LogReader listens on /dev/socket/logdr. When a client
// connects, log entries in the LogBuffer are written to the client.
-
- LogReader* reader = new LogReader(logBuf);
+ LogReader* reader = new LogReader(logBuf, &reader_list);
if (reader->startListener()) {
return EXIT_FAILURE;
}
@@ -321,17 +306,14 @@
// LogListener listens on /dev/socket/logdw for client
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
-
- LogListener* swl = new LogListener(logBuf, reader);
- // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
- if (swl->startListener(600)) {
+ LogListener* swl = new LogListener(logBuf);
+ if (!swl->StartListener()) {
return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
-
- CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
+ CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
if (cl->startListener()) {
return EXIT_FAILURE;
}
@@ -339,25 +321,22 @@
// LogAudit listens on NETLINK_AUDIT socket for selinux
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
-
LogAudit* al = nullptr;
if (auditd) {
- al = new LogAudit(logBuf, reader,
- __android_logger_property_get_bool(
- "ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
- ? fdDmesg
- : -1);
+ int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
+ ? fdDmesg
+ : -1;
+ al = new LogAudit(logBuf, dmesg_fd, &log_statistics);
}
LogKlog* kl = nullptr;
if (klogd) {
- kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
+ kl = new LogKlog(logBuf, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
}
readDmesg(al, kl);
// failure is an option ... messages are in dmesg (required by standard)
-
if (kl && kl->startListener()) {
delete kl;
}
diff --git a/logd/rwlock.h b/logd/rwlock.h
new file mode 100644
index 0000000..2b27ff1
--- /dev/null
+++ b/logd/rwlock.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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/LICENSE2.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 <pthread.h>
+
+#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
+
+// As of the end of May 2020, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
+// combination of std::mutex and std::condition variable, which is obviously less efficient. This
+// immitates what std::shared_mutex should be doing and is compatible with RAII thread wrappers.
+
+class SHARED_CAPABILITY("mutex") RwLock {
+ public:
+ RwLock() {}
+ ~RwLock() {}
+
+ void lock() ACQUIRE() { pthread_rwlock_wrlock(&rwlock_); }
+ void lock_shared() ACQUIRE_SHARED() { pthread_rwlock_rdlock(&rwlock_); }
+
+ void unlock() RELEASE() { pthread_rwlock_unlock(&rwlock_); }
+
+ private:
+ pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
+};
+
+// std::shared_lock does not have thread annotations, so we need our own.
+
+class SCOPED_CAPABILITY SharedLock {
+ public:
+ SharedLock(RwLock& lock) ACQUIRE_SHARED(lock) : lock_(lock) { lock_.lock_shared(); }
+ ~SharedLock() RELEASE() { lock_.unlock(); }
+
+ void lock_shared() ACQUIRE_SHARED() { lock_.lock_shared(); }
+ void unlock() RELEASE() { lock_.unlock(); }
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SharedLock);
+
+ private:
+ RwLock& lock_;
+};
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
deleted file mode 100644
index 9a5defa..0000000
--- a/logd/tests/Android.bp
+++ /dev/null
@@ -1,68 +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.
-//
-
-// -----------------------------------------------------------------------------
-// Unit tests.
-// -----------------------------------------------------------------------------
-
-cc_defaults {
- name: "logd-unit-test-defaults",
-
- cflags: [
- "-fstack-protector-all",
- "-g",
- "-Wall",
- "-Wextra",
- "-Werror",
- "-fno-builtin",
-
- "-DAUDITD_LOG_TAG=1003",
- "-DCHATTY_LOG_TAG=1004",
- ],
-
- srcs: ["logd_test.cpp"],
-
- static_libs: [
- "libbase",
- "libcutils",
- "libselinux",
- "liblog",
- ],
-}
-
-// Build tests for the logger. Run with:
-// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
-cc_test {
- name: "logd-unit-tests",
- defaults: ["logd-unit-test-defaults"],
-}
-
-cc_test {
- name: "CtsLogdTestCases",
- defaults: ["logd-unit-test-defaults"],
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
- test_suites: [
- "cts",
- "vts10",
- ],
-}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a380ebb..00a58bf 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -547,8 +547,8 @@
enter_default_mount_ns
# /data/apex is now available. Start apexd to scan and activate APEXes.
- mkdir /data/apex 0750 root system encryption=None
- mkdir /data/apex/active 0750 root system
+ mkdir /data/apex 0755 root system encryption=None
+ mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/hashtree 0700 root system
mkdir /data/apex/sessions 0700 root system