Merge "emmc_optimized means stable_inodes"
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_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 d66d400..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();
}
@@ -349,7 +349,7 @@
}
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();
@@ -358,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. "
@@ -369,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;
@@ -408,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};
}
@@ -426,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");
}
@@ -490,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);
@@ -511,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;
@@ -550,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";
@@ -673,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.
@@ -697,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;
@@ -735,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);
@@ -742,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];
@@ -760,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());
@@ -785,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);
@@ -799,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) {
@@ -811,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;
@@ -830,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;
@@ -845,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;
}
@@ -855,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;
@@ -880,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 efb6c2f..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"
@@ -1735,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/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 c9993b7..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"
@@ -221,7 +222,7 @@
}
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());
@@ -248,26 +249,8 @@
return false;
}
- // adb secure service needs to do something different from just
- // connecting here.
- 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_);
- int index = adb_DNSServiceIndexByName(regType_.c_str());
- if (index == kADBSecureConnectServiceRefIndex) {
- ConnectSecureWifiDevice();
- } else {
- connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
- &response);
- D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
- ip_addr_, port_, response.c_str());
- }
- } else {
- D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
- serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
- }
-
+ // 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) {
@@ -294,6 +277,25 @@
}
services->push_back(std::unique_ptr<ResolvedService>(this));
+ if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
+ std::string response;
+ 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("%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());
+ }
+ } else {
+ D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
+ serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
+ }
+
return true;
}
@@ -319,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,
@@ -362,7 +364,7 @@
// 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();
@@ -372,7 +374,7 @@
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);
@@ -396,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) {
@@ -676,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/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/socket_spec.cpp b/adb/socket_spec.cpp
index b7b25fa..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;
@@ -195,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/test_adb.py b/adb/test_adb.py
index 9912f11..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(1)
- print(f"services={_mdns_services(server_port)}")
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(1)
- print(f"services={_mdns_services(server_port)}")
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/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/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_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/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/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index c191102..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,6 +285,11 @@
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"],
@@ -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/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/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 1e90ace..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.
@@ -74,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()) {
@@ -123,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()) {
@@ -153,10 +164,10 @@
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) {
+SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
(void)snapshot;
CHECK(current_module != nullptr);
CHECK(current_module->device_info != nullptr);
@@ -194,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;
}
@@ -202,15 +214,10 @@
SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
[[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
static SnapshotFuzzEnv env;
- [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
return &env;
}
-} // namespace android::snapshot
-
-DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
- using namespace android::snapshot;
-
+SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
current_data = &snapshot_fuzz_data;
auto env = GetSnapshotFuzzEnv();
@@ -219,9 +226,127 @@
auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
current_module = &test_module;
CHECK(test_module.snapshot);
+ return test_module;
+}
- SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
-
+void TearDownTest() {
current_module = nullptr;
current_data = nullptr;
}
+
+} // namespace android::snapshot
+
+DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
+ using namespace android::snapshot;
+
+ [[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 c9f1ab0..8926535 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -27,7 +27,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/properties.h>
#include <fs_mgr.h>
#include <libsnapshot/auto_device.h>
#include <libsnapshot/snapshot.h>
@@ -165,14 +164,6 @@
reinterpret_cast<PropertyList*>(cookie)->insert(key);
}
-void CheckUnsetGsidProps() {
- PropertyList list;
- property_list(&InsertProperty, reinterpret_cast<void*>(&list));
- for (const auto& key : list) {
- SetProperty(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) {
@@ -344,7 +335,6 @@
};
SnapshotFuzzEnv::SnapshotFuzzEnv() {
- CheckUnsetGsidProps();
CheckCleanupDeviceMapperDevices();
CheckDetachLoopDevices();
CheckUmountAll();
@@ -368,7 +358,6 @@
}
SnapshotFuzzEnv::~SnapshotFuzzEnv() {
- CheckUnsetGsidProps();
CheckCleanupDeviceMapperDevices();
mounted_data_ = nullptr;
auto_delete_data_mount_point_ = nullptr;
@@ -396,7 +385,7 @@
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.
@@ -507,4 +496,21 @@
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 2405088..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>
@@ -134,4 +136,68 @@
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/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/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index a4e4def..3bd5cf2 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -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;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index bbc985a..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;
}
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/libunwindstack/benchmarks/MapsBenchmark.cpp b/libunwindstack/benchmarks/MapsBenchmark.cpp
index be106a3..5df1491 100644
--- a/libunwindstack/benchmarks/MapsBenchmark.cpp
+++ b/libunwindstack/benchmarks/MapsBenchmark.cpp
@@ -39,55 +39,122 @@
std::string maps_file_;
};
-constexpr size_t kNumMaps = 10000;
+static constexpr size_t kNumSmallMaps = 100;
+static constexpr size_t kNumLargeMaps = 10000;
-static void CreateInitialMap(const char* filename) {
+static void CreateMap(const char* filename, size_t num_maps, size_t increment = 1) {
std::string maps;
- for (size_t i = 0; i < kNumMaps; i += 2) {
+ 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, i);
+ (i + 1) * 1000 * increment, i * increment);
}
if (!android::base::WriteStringToFile(maps, filename)) {
errx(1, "WriteStringToFile failed");
}
}
-static void CreateReparseMap(const char* filename) {
- std::string maps;
- for (size_t i = 0; i < kNumMaps; i++) {
- maps += android::base::StringPrintf("%zu-%zu r-xp 0000 00:00 0 name%zu\n", i * 2000,
- (i + 1) * 2000, 2 * i);
- }
- if (!android::base::WriteStringToFile(maps, filename)) {
- errx(1, "WriteStringToFile failed");
- }
-}
-
-void BM_local_updatable_maps_reparse(benchmark::State& state) {
- TemporaryFile initial_map;
- CreateInitialMap(initial_map.path);
-
- TemporaryFile reparse_map;
- CreateReparseMap(reparse_map.path);
-
+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(initial_map.path);
+ maps.BenchmarkSetMapsFile(maps1);
if (!maps.Reparse()) {
errx(1, "Internal Error: reparse of initial maps filed.");
}
- if (maps.Total() != (kNumMaps / 2)) {
+ if (maps.Total() != maps1_total) {
errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
- kNumMaps / 2);
+ maps1_total);
}
- maps.BenchmarkSetMapsFile(reparse_map.path);
+ maps.BenchmarkSetMapsFile(maps2);
if (!maps.Reparse()) {
errx(1, "Internal Error: reparse of second set of maps filed.");
}
- if (maps.Total() != kNumMaps) {
+ if (maps.Total() != maps2_total) {
errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
- kNumMaps);
+ maps2_total);
}
}
}
-BENCHMARK(BM_local_updatable_maps_reparse);
+
+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/libutils/Android.bp b/libutils/Android.bp
index 2dbfb70..0f7044a 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -175,8 +175,8 @@
],
shared_libs: [
- "libutils",
- "libbacktrace",
+ "libutils",
+ "libbacktrace",
],
target: {
@@ -194,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,
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_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/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/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/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 c5a968a..0000000
--- a/libziparchive/Android.bp
+++ /dev/null
@@ -1,237 +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,
- },
- },
-
- apex_available: [
- "//apex_available:platform",
- "com.android.art.debug",
- "com.android.art.release",
- ],
-}
-
-// 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/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 3a55c4e..f3fdfd7 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -475,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;
}
}
@@ -521,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 d203062..5e591d9 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -28,46 +28,53 @@
"-DLIBLOG_LOG_TAG=1006",
]
-cc_library_static {
- name: "liblogd",
-
- srcs: [
- "ChattyLogBuffer.cpp",
- "CommandListener.cpp",
- "LogListener.cpp",
- "LogPermissions.cpp",
- "LogReader.cpp",
- "LogReaderList.cpp",
- "LogReaderThread.cpp",
- "LogBufferElement.cpp",
- "LogStatistics.cpp",
- "LogWhiteBlackList.cpp",
- "libaudit.c",
- "LogAudit.cpp",
- "LogKlog.cpp",
- "LogTags.cpp",
- ],
- logtags: ["event.logtags"],
+cc_defaults {
+ name: "logd_defaults",
shared_libs: ["libbase"],
-
- export_include_dirs: ["."],
-
cflags: [
"-Wextra",
"-Wthread-safety",
] + event_flag,
lto: {
- thin: true
- }
+ thin: true,
+ },
+}
+
+cc_library_static {
+ name: "liblogd",
+ defaults: ["logd_defaults"],
+ host_supported: true,
+ srcs: [
+ "ChattyLogBuffer.cpp",
+ "LogReaderList.cpp",
+ "LogReaderThread.cpp",
+ "LogBufferElement.cpp",
+ "LogStatistics.cpp",
+ "LogWhiteBlackList.cpp",
+ "LogTags.cpp",
+ ],
+ logtags: ["event.logtags"],
+
+ export_include_dirs: ["."],
}
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",
@@ -77,34 +84,23 @@
shared_libs: [
"libsysutils",
"libcutils",
- "libbase",
"libpackagelistparser",
"libprocessgroup",
"libcap",
],
-
- cflags: [
- "-Wextra",
- ],
-
- lto: {
- thin: true
- }
}
cc_binary {
name: "auditctl",
- srcs: ["auditctl.cpp"],
-
- static_libs: [
- "liblogd",
+ srcs: [
+ "auditctl.cpp",
+ "libaudit.cpp",
],
shared_libs: ["libbase"],
cflags: [
- "-Wconversion",
"-Wextra",
],
}
@@ -114,3 +110,58 @@
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: [
+ "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
index c6c9a7c..f1305a5 100644
--- a/logd/ChattyLogBuffer.cpp
+++ b/logd/ChattyLogBuffer.cpp
@@ -28,6 +28,7 @@
#include <time.h>
#include <unistd.h>
+#include <limits>
#include <unordered_map>
#include <utility>
@@ -286,7 +287,7 @@
lastLoggedElements[LOG_ID_EVENTS] = elem;
total += htole32(swab);
// check for overflow
- if (total >= UINT32_MAX) {
+ if (total >= std::numeric_limits<int32_t>::max()) {
log(currentLast);
unlock();
return len;
@@ -426,31 +427,14 @@
// 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());
+ 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();
@@ -465,8 +449,8 @@
}
void add(LogBufferElement* element) {
- LogBufferElementKey key(element->getUid(), element->getPid(), element->getTid());
- map[key.getKey()] = element;
+ uint64_t key = LogBufferElementKey(element->getUid(), element->getPid(), element->getTid());
+ map[key] = element;
}
void clear() { map.clear(); }
@@ -483,6 +467,11 @@
}
}
}
+
+ 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);
+ }
};
// If the selected reader is blocking our pruning progress, decide on
@@ -660,7 +649,7 @@
if (leading) {
it = GetOldest(id);
}
- static const timespec too_old = {EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
+ static const log_time too_old{EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
LogBufferElementCollection::iterator lastt;
lastt = mLogElements.end();
--lastt;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 8499715..4f5cabd 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -183,16 +183,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;
}
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
new file mode 100644
index 0000000..b52ce8f
--- /dev/null
+++ b/logd/LogBufferTest.cpp
@@ -0,0 +1,570 @@
+/*
+ * 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 "LogBuffer.h"
+
+#include <unistd.h>
+
+#include <limits>
+#include <memory>
+#include <regex>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "ChattyLogBuffer.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.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;
+}
+
+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.
+};
+
+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;
+}
+
+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;
+}
+
+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());
+}
+
+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);
+}
+
+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::Test {
+ protected:
+ void SetUp() override {
+ log_buffer_.reset(new ChattyLogBuffer(&reader_list_, &tags_, &prune_, &stats_));
+ }
+
+ void FixupMessages(std::vector<LogMessage>* messages) {
+ for (auto& [entry, message, _] : *messages) {
+ entry.hdr_size = sizeof(logger_entry);
+ entry.len = message.size();
+ }
+ }
+
+ 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_;
+};
+
+TEST_F(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_F(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_F(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);
+}
+
+TEST_F(LogBufferTest, 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_F(LogBufferTest, 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_F(LogBufferTest, 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);
+};
\ No newline at end of file
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 8250808..3afe3ee 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -32,6 +32,7 @@
#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>
@@ -550,10 +551,10 @@
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;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index ce82b41..c472167 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -30,7 +30,7 @@
// 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)));
// Caller must own and free returned value
char* pidToName(pid_t pid);
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 55737e9..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) {
@@ -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/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",
- ],
-}