Merge "fastboot: add support for v3 boot header format"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e3a8675..44c47f3 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -28,6 +28,9 @@
"name": "fs_mgr_vendor_overlay_test"
},
{
+ "name": "init_kill_services_test"
+ },
+ {
"name": "libbase_test"
},
{
diff --git a/adb/Android.bp b/adb/Android.bp
index 1b7a266..d8fa713 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -387,17 +387,17 @@
generated_headers: ["platform_tools_version"],
static_libs: [
- "libadbconnection_server",
- "libapp_processes_protos_lite",
"libdiagnose_usb",
],
shared_libs: [
+ "libadbconnection_server",
"libadb_crypto",
"libadb_pairing_connection",
"libadb_protos",
"libadb_tls_connection",
"libadbd_auth",
+ "libapp_processes_protos_lite",
"libasyncio",
"libbase",
"libcrypto",
@@ -432,6 +432,7 @@
exclude_shared_libs: [
"libadb_pairing_auth",
"libadb_pairing_connection",
+ "libapp_processes_protos_lite",
],
}
},
@@ -458,9 +459,7 @@
static_libs: [
"libadbconnection_server",
"libadbd_core",
- "libapp_processes_protos_lite",
"libdiagnose_usb",
- "libprotobuf-cpp-lite",
],
shared_libs: [
@@ -470,12 +469,14 @@
"libadb_tls_connection",
"libadbd_auth",
"libadbd_fs",
+ "libapp_processes_protos_lite",
"libasyncio",
"libbase",
"libcrypto",
"libcrypto_utils",
"libcutils_sockets",
"liblog",
+ "libprotobuf-cpp-lite",
],
target: {
@@ -514,15 +515,14 @@
// libminadbd wants both, as it's used to build native tests.
compile_multilib: "both",
- // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
whole_static_libs: [
- "libadbconnection_server",
"libadbd_core",
- "libapp_processes_protos_lite",
- "libprotobuf-cpp-lite",
],
shared_libs: [
+ "libadbconnection_server",
+ "libapp_processes_protos_lite",
+ "libprotobuf-cpp-lite",
"libadb_crypto",
"libadb_pairing_connection",
"libadb_tls_connection",
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index c99aead..adae9f7 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -16,6 +16,7 @@
#if !ADB_HOST
+#if !defined(__ANDROID_RECOVERY__)
#define TRACE_TAG JDWP
#include "sysdeps.h"
@@ -459,7 +460,7 @@
return -1;
}
-asocket* create_process_tracker_service_socket(TrackerKind kind) {
+static asocket* create_process_tracker_service_socket(TrackerKind kind) {
auto t = std::make_unique<JdwpTracker>(kind, true);
if (!t) {
LOG(FATAL) << "failed to allocate JdwpTracker";
@@ -509,4 +510,28 @@
return 0;
}
+#else // !defined(__ANDROID_RECOVERY)
+#include "adb.h"
+
+asocket* create_jdwp_service_socket(void) {
+ return nullptr;
+}
+
+unique_fd create_jdwp_connection_fd(int pid) {
+ return {};
+}
+
+asocket* create_app_tracker_service_socket() {
+ return nullptr;
+}
+
+asocket* create_jdwp_tracker_service_socket() {
+ return nullptr;
+}
+
+int init_jdwp() {
+ return 0;
+}
+
+#endif /* defined(__ANDROID_RECOVERY__) */
#endif /* !ADB_HOST */
diff --git a/base/Android.bp b/base/Android.bp
index 12de3b2..894ad6c 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -74,10 +74,6 @@
"test_utils.cpp",
],
- static: {
- cflags: ["-DNO_LIBLOG_DLSYM"],
- },
-
cppflags: ["-Wexit-time-destructors"],
shared_libs: ["liblog"],
target: {
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
index 8d59179..ba4c161 100644
--- a/base/liblog_symbols.cpp
+++ b/base/liblog_symbols.cpp
@@ -16,11 +16,9 @@
#include "liblog_symbols.h"
-#if defined(__ANDROID__)
-#if !defined(NO_LIBLOG_DLSYM) || defined(__ANDROID_APEX__)
+#if defined(__ANDROID_SDK_VERSION__) && (__ANDROID_SDK_VERSION__ <= 29)
#define USE_DLSYM
#endif
-#endif
#ifdef USE_DLSYM
#include <dlfcn.h>
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index d9167e7..e7f785b 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -261,6 +261,10 @@
GTEST_LOG_(INFO) << "Flashing a logical partition..";
EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)
<< "flash logical -partition failed";
+
+ GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
+ EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)
+ << "delete logical-partition failed";
}
// Conformance tests
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 8e9e074..8b67e22 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -233,6 +233,15 @@
return value;
}
+bool BatteryMonitor::isScopedPowerSupply(const char* name) {
+ constexpr char kScopeDevice[] = "Device";
+
+ String8 path;
+ path.appendFormat("%s/%s/scope", POWER_SUPPLY_SYSFS_PATH, name);
+ std::string scope;
+ return (readFromFile(path, &scope) > 0 && scope == kScopeDevice);
+}
+
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
@@ -547,6 +556,11 @@
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
+ // Some devices expose the battery status of sub-component like
+ // stylus. Such a device-scoped battery info needs to be skipped
+ // in BatteryMonitor, which is intended to report the status of
+ // the battery supplying the power to the whole system.
+ if (isScopedPowerSupply(name)) continue;
mBatteryDevicePresent = true;
if (mHealthdConfig->batteryStatusPath.isEmpty()) {
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index d41a374..fadb5a5 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -78,6 +78,7 @@
PowerSupplyType readPowerSupplyType(const String8& path);
bool getBooleanField(const String8& path);
int getIntField(const String8& path);
+ bool isScopedPowerSupply(const char* name);
};
}; // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index 3bb08db..72a7bfe 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -81,6 +81,7 @@
"-Wextra",
"-Wno-unused-parameter",
"-Werror",
+ "-Wthread-safety",
"-DALLOW_FIRST_STAGE_CONSOLE=0",
"-DALLOW_LOCAL_PROP_OVERRIDE=0",
"-DALLOW_PERMISSIVE_SELINUX=0",
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index ebca762..b45f5cd 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -41,10 +41,12 @@
}
void ActionManager::QueueEventTrigger(const std::string& trigger) {
+ auto lock = std::lock_guard{event_queue_lock_};
event_queue_.emplace(trigger);
}
void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+ auto lock = std::lock_guard{event_queue_lock_};
event_queue_.emplace(std::make_pair(name, value));
}
@@ -53,6 +55,7 @@
}
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+ auto lock = std::lock_guard{event_queue_lock_};
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
action->AddCommand(std::move(func), {name}, 0);
@@ -62,15 +65,18 @@
}
void ActionManager::ExecuteOneCommand() {
- // Loop through the event queue until we have an action to execute
- while (current_executing_actions_.empty() && !event_queue_.empty()) {
- for (const auto& action : actions_) {
- if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
- event_queue_.front())) {
- current_executing_actions_.emplace(action.get());
+ {
+ auto lock = std::lock_guard{event_queue_lock_};
+ // Loop through the event queue until we have an action to execute
+ while (current_executing_actions_.empty() && !event_queue_.empty()) {
+ for (const auto& action : actions_) {
+ if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+ event_queue_.front())) {
+ current_executing_actions_.emplace(action.get());
+ }
}
+ event_queue_.pop();
}
- event_queue_.pop();
}
if (current_executing_actions_.empty()) {
@@ -103,6 +109,7 @@
}
bool ActionManager::HasMoreCommands() const {
+ auto lock = std::lock_guard{event_queue_lock_};
return !current_executing_actions_.empty() || !event_queue_.empty();
}
@@ -113,6 +120,7 @@
}
void ActionManager::ClearQueue() {
+ auto lock = std::lock_guard{event_queue_lock_};
// We are shutting down so don't claim the oneshot builtin actions back
current_executing_actions_ = {};
event_queue_ = {};
diff --git a/init/action_manager.h b/init/action_manager.h
index a2b95ac..b6f93d9 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -16,9 +16,12 @@
#pragma once
+#include <mutex>
#include <string>
#include <vector>
+#include <android-base/thread_annotations.h>
+
#include "action.h"
#include "builtins.h"
@@ -48,7 +51,9 @@
void operator=(ActionManager const&) = delete;
std::vector<std::unique_ptr<Action>> actions_;
- std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
+ std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_
+ GUARDED_BY(event_queue_lock_);
+ mutable std::mutex event_queue_lock_;
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_;
};
diff --git a/init/init.cpp b/init/init.cpp
index a7518fc..4289dcf 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -33,7 +33,9 @@
#include <functional>
#include <map>
#include <memory>
+#include <mutex>
#include <optional>
+#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -95,14 +97,155 @@
static int signal_fd = -1;
static int property_fd = -1;
-static std::unique_ptr<Timer> waiting_for_prop(nullptr);
-static std::string wait_prop_name;
-static std::string wait_prop_value;
-static std::string shutdown_command;
-static bool do_shutdown = false;
-
static std::unique_ptr<Subcontext> subcontext;
+struct PendingControlMessage {
+ std::string message;
+ std::string name;
+ pid_t pid;
+ int fd;
+};
+static std::mutex pending_control_messages_lock;
+static std::queue<PendingControlMessage> pending_control_messages;
+
+// Init epolls various FDs to wait for various inputs. It previously waited on property changes
+// with a blocking socket that contained the information related to the change, however, it was easy
+// to fill that socket and deadlock the system. Now we use locks to handle the property changes
+// directly in the property thread, however we still must wake the epoll to inform init that there
+// is a change to process, so we use this FD. It is non-blocking, since we do not care how many
+// times WakeEpoll() is called, only that the epoll will wake.
+static int wake_epoll_fd = -1;
+static void InstallInitNotifier(Epoll* epoll) {
+ int sockets[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, sockets) != 0) {
+ PLOG(FATAL) << "Failed to socketpair() between property_service and init";
+ }
+ int epoll_fd = sockets[0];
+ wake_epoll_fd = sockets[1];
+
+ auto drain_socket = [epoll_fd] {
+ char buf[512];
+ while (read(epoll_fd, buf, sizeof(buf)) > 0) {
+ }
+ };
+
+ if (auto result = epoll->RegisterHandler(epoll_fd, drain_socket); !result.ok()) {
+ LOG(FATAL) << result.error();
+ }
+}
+
+static void WakeEpoll() {
+ constexpr char value[] = "1";
+ write(wake_epoll_fd, value, sizeof(value));
+}
+
+static class PropWaiterState {
+ public:
+ bool StartWaiting(const char* name, const char* value) {
+ auto lock = std::lock_guard{lock_};
+ if (waiting_for_prop_) {
+ return false;
+ }
+ if (GetProperty(name, "") != value) {
+ // Current property value is not equal to expected value
+ wait_prop_name_ = name;
+ wait_prop_value_ = value;
+ waiting_for_prop_.reset(new Timer());
+ } else {
+ LOG(INFO) << "start_waiting_for_property(\"" << name << "\", \"" << value
+ << "\"): already set";
+ }
+ return true;
+ }
+
+ void ResetWaitForProp() {
+ auto lock = std::lock_guard{lock_};
+ ResetWaitForPropLocked();
+ }
+
+ void CheckAndResetWait(const std::string& name, const std::string& value) {
+ auto lock = std::lock_guard{lock_};
+ // We always record how long init waited for ueventd to tell us cold boot finished.
+ // If we aren't waiting on this property, it means that ueventd finished before we even
+ // started to wait.
+ if (name == kColdBootDoneProp) {
+ auto time_waited = waiting_for_prop_ ? waiting_for_prop_->duration().count() : 0;
+ std::thread([time_waited] {
+ SetProperty("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+ }).detach();
+ }
+
+ if (waiting_for_prop_) {
+ if (wait_prop_name_ == name && wait_prop_value_ == value) {
+ LOG(INFO) << "Wait for property '" << wait_prop_name_ << "=" << wait_prop_value_
+ << "' took " << *waiting_for_prop_;
+ ResetWaitForPropLocked();
+ WakeEpoll();
+ }
+ }
+ }
+
+ // This is not thread safe because it releases the lock when it returns, so the waiting state
+ // may change. However, we only use this function to prevent running commands in the main
+ // thread loop when we are waiting, so we do not care about false positives; only false
+ // negatives. StartWaiting() and this function are always called from the same thread, so false
+ // negatives are not possible and therefore we're okay.
+ bool MightBeWaiting() {
+ auto lock = std::lock_guard{lock_};
+ return static_cast<bool>(waiting_for_prop_);
+ }
+
+ private:
+ void ResetWaitForPropLocked() {
+ wait_prop_name_.clear();
+ wait_prop_value_.clear();
+ waiting_for_prop_.reset();
+ }
+
+ std::mutex lock_;
+ std::unique_ptr<Timer> waiting_for_prop_{nullptr};
+ std::string wait_prop_name_;
+ std::string wait_prop_value_;
+
+} prop_waiter_state;
+
+bool start_waiting_for_property(const char* name, const char* value) {
+ return prop_waiter_state.StartWaiting(name, value);
+}
+
+void ResetWaitForProp() {
+ prop_waiter_state.ResetWaitForProp();
+}
+
+static class ShutdownState {
+ public:
+ void TriggerShutdown(const std::string& command) {
+ // We can't call HandlePowerctlMessage() directly in this function,
+ // because it modifies the contents of the action queue, which can cause the action queue
+ // to get into a bad state if this function is called from a command being executed by the
+ // action queue. Instead we set this flag and ensure that shutdown happens before the next
+ // command is run in the main init loop.
+ auto lock = std::lock_guard{shutdown_command_lock_};
+ shutdown_command_ = command;
+ do_shutdown_ = true;
+ WakeEpoll();
+ }
+
+ std::optional<std::string> CheckShutdown() {
+ auto lock = std::lock_guard{shutdown_command_lock_};
+ if (do_shutdown_ && !IsShuttingDown()) {
+ do_shutdown_ = false;
+ return shutdown_command_;
+ }
+ return {};
+ }
+
+ private:
+ std::mutex shutdown_command_lock_;
+ std::string shutdown_command_;
+ bool do_shutdown_ = false;
+} shutdown_state;
+
void DumpState() {
ServiceList::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
@@ -156,39 +299,7 @@
}
}
-bool start_waiting_for_property(const char* name, const char* value) {
- if (waiting_for_prop) {
- return false;
- }
- if (GetProperty(name, "") != value) {
- // Current property value is not equal to expected value
- wait_prop_name = name;
- wait_prop_value = value;
- waiting_for_prop.reset(new Timer());
- } else {
- LOG(INFO) << "start_waiting_for_property(\"" << name << "\", \"" << value
- << "\"): already set";
- }
- return true;
-}
-
-void ResetWaitForProp() {
- wait_prop_name.clear();
- wait_prop_value.clear();
- waiting_for_prop.reset();
-}
-
-static void TriggerShutdown(const std::string& command) {
- // We can't call HandlePowerctlMessage() directly in this function,
- // because it modifies the contents of the action queue, which can cause the action queue
- // to get into a bad state if this function is called from a command being executed by the
- // action queue. Instead we set this flag and ensure that shutdown happens before the next
- // command is run in the main init loop.
- shutdown_command = command;
- do_shutdown = true;
-}
-
-void property_changed(const std::string& name, const std::string& value) {
+void PropertyChanged(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
@@ -196,26 +307,15 @@
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
if (name == "sys.powerctl") {
- TriggerShutdown(value);
+ trigger_shutdown(value);
}
- if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
-
- // We always record how long init waited for ueventd to tell us cold boot finished.
- // If we aren't waiting on this property, it means that ueventd finished before we even started
- // to wait.
- if (name == kColdBootDoneProp) {
- auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
- SetProperty("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+ if (property_triggers_enabled) {
+ ActionManager::GetInstance().QueuePropertyChange(name, value);
+ WakeEpoll();
}
- if (waiting_for_prop) {
- if (wait_prop_name == name && wait_prop_value == value) {
- LOG(INFO) << "Wait for property '" << wait_prop_name << "=" << wait_prop_value
- << "' took " << *waiting_for_prop;
- ResetWaitForProp();
- }
- }
+ prop_waiter_state.CheckAndResetWait(name, value);
}
static std::optional<boot_clock::time_point> HandleProcessActions() {
@@ -343,8 +443,46 @@
return true;
}
+bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd) {
+ auto lock = std::lock_guard{pending_control_messages_lock};
+ if (pending_control_messages.size() > 100) {
+ LOG(ERROR) << "Too many pending control messages, dropped '" << message << "' for '" << name
+ << "' from pid: " << pid;
+ return false;
+ }
+ pending_control_messages.push({message, name, pid, fd});
+ WakeEpoll();
+ return true;
+}
+
+static void HandleControlMessages() {
+ auto lock = std::unique_lock{pending_control_messages_lock};
+ // Init historically would only execute handle one property message, including control messages
+ // in each iteration of its main loop. We retain this behavior here to prevent starvation of
+ // other actions in the main loop.
+ if (!pending_control_messages.empty()) {
+ auto control_message = pending_control_messages.front();
+ pending_control_messages.pop();
+ lock.unlock();
+
+ bool success = HandleControlMessage(control_message.message, control_message.name,
+ control_message.pid);
+
+ uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ if (control_message.fd != -1) {
+ TEMP_FAILURE_RETRY(send(control_message.fd, &response, sizeof(response), 0));
+ close(control_message.fd);
+ }
+ lock.lock();
+ }
+ // If we still have items to process, make sure we wake back up to do so.
+ if (!pending_control_messages.empty()) {
+ WakeEpoll();
+ }
+}
+
static Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {
- if (!start_waiting_for_property(kColdBootDoneProp, "true")) {
+ if (!prop_waiter_state.StartWaiting(kColdBootDoneProp, "true")) {
LOG(FATAL) << "Could not wait for '" << kColdBootDoneProp << "'";
}
@@ -562,60 +700,6 @@
}
}
-void SendStopSendingMessagesMessage() {
- auto init_message = InitMessage{};
- init_message.set_stop_sending_messages(true);
- if (auto result = SendMessage(property_fd, init_message); !result.ok()) {
- LOG(ERROR) << "Failed to send 'stop sending messages' message: " << result.error();
- }
-}
-
-void SendStartSendingMessagesMessage() {
- auto init_message = InitMessage{};
- init_message.set_start_sending_messages(true);
- if (auto result = SendMessage(property_fd, init_message); !result.ok()) {
- LOG(ERROR) << "Failed to send 'start sending messages' message: " << result.error();
- }
-}
-
-static void HandlePropertyFd() {
- auto message = ReadMessage(property_fd);
- if (!message.ok()) {
- LOG(ERROR) << "Could not read message from property service: " << message.error();
- return;
- }
-
- auto property_message = PropertyMessage{};
- if (!property_message.ParseFromString(*message)) {
- LOG(ERROR) << "Could not parse message from property service";
- return;
- }
-
- switch (property_message.msg_case()) {
- case PropertyMessage::kControlMessage: {
- auto& control_message = property_message.control_message();
- bool success = HandleControlMessage(control_message.msg(), control_message.name(),
- control_message.pid());
-
- uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
- if (control_message.has_fd()) {
- int fd = control_message.fd();
- TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
- close(fd);
- }
- break;
- }
- case PropertyMessage::kChangedMessage: {
- auto& changed_message = property_message.changed_message();
- property_changed(changed_message.name(), changed_message.value());
- break;
- }
- default:
- LOG(ERROR) << "Unknown message type from property service: "
- << property_message.msg_case();
- }
-}
-
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -623,14 +707,21 @@
boot_clock::time_point start_time = boot_clock::now();
- trigger_shutdown = TriggerShutdown;
+ trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
- // Will handle EPIPE at the time of write by checking the errno
- signal(SIGPIPE, SIG_IGN);
+ // Init should not crash because of a dependence on any other process, therefore we ignore
+ // SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
+ // is inherited across exec, but custom signal handlers are not. Since we do not want to
+ // ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
+ {
+ struct sigaction action = {.sa_flags = SA_RESTART};
+ action.sa_handler = [](int) {};
+ sigaction(SIGPIPE, &action, nullptr);
+ }
// Set init and its forked children's oom_adj.
if (auto result =
@@ -683,11 +774,8 @@
}
InstallSignalFdHandler(&epoll);
-
+ InstallInitNotifier(&epoll);
StartPropertyService(&property_fd);
- if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result.ok()) {
- LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
- }
// Make the time that init stages started available for bootstat to log.
RecordStageBoottimes(start_time);
@@ -770,12 +858,12 @@
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
- if (do_shutdown && !IsShuttingDown()) {
- do_shutdown = false;
- HandlePowerctlMessage(shutdown_command);
+ auto shutdown_command = shutdown_state.CheckShutdown();
+ if (shutdown_command) {
+ HandlePowerctlMessage(*shutdown_command);
}
- if (!(waiting_for_prop || Service::is_exec_service_running())) {
+ if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!IsShuttingDown()) {
@@ -789,7 +877,7 @@
}
}
- if (!(waiting_for_prop || Service::is_exec_service_running())) {
+ if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
@@ -806,6 +894,7 @@
(*function)();
}
}
+ HandleControlMessages();
}
return 0;
diff --git a/init/init.h b/init/init.h
index 4bbca6f..27f64e2 100644
--- a/init/init.h
+++ b/init/init.h
@@ -38,8 +38,9 @@
void ResetWaitForProp();
void SendLoadPersistentPropertiesMessage();
-void SendStopSendingMessagesMessage();
-void SendStartSendingMessagesMessage();
+
+void PropertyChanged(const std::string& name, const std::string& value);
+bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);
int SecondStageMain(int argc, char** argv);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 730bf6d..8206522 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -92,8 +92,11 @@
static bool persistent_properties_loaded = false;
static int property_set_fd = -1;
+static int from_init_socket = -1;
static int init_socket = -1;
static bool accept_messages = false;
+static std::mutex accept_messages_lock;
+static std::thread property_service_thread;
static PropertyInfoAreaFile property_info_area;
@@ -115,6 +118,16 @@
return 0;
}
+void StartSendingMessages() {
+ auto lock = std::lock_guard{accept_messages_lock};
+ accept_messages = true;
+}
+
+void StopSendingMessages() {
+ auto lock = std::lock_guard{accept_messages_lock};
+ accept_messages = true;
+}
+
bool CanReadProperty(const std::string& source_context, const std::string& name) {
const char* target_context = nullptr;
property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
@@ -147,17 +160,6 @@
return has_access;
}
-static void SendPropertyChanged(const std::string& name, const std::string& value) {
- auto property_msg = PropertyMessage{};
- auto* changed_message = property_msg.mutable_changed_message();
- changed_message->set_name(name);
- changed_message->set_value(value);
-
- if (auto result = SendMessage(init_socket, property_msg); !result.ok()) {
- LOG(ERROR) << "Failed to send property changed message: " << result.error();
- }
-}
-
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
@@ -195,8 +197,9 @@
}
// If init hasn't started its main loop, then it won't be handling property changed messages
// anyway, so there's no need to try to send them.
+ auto lock = std::lock_guard{accept_messages_lock};
if (accept_messages) {
- SendPropertyChanged(name, value);
+ PropertyChanged(name, value);
}
return PROP_SUCCESS;
}
@@ -373,33 +376,24 @@
static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
SocketConnection* socket, std::string* error) {
+ auto lock = std::lock_guard{accept_messages_lock};
if (!accept_messages) {
*error = "Received control message after shutdown, ignoring";
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
- auto property_msg = PropertyMessage{};
- auto* control_message = property_msg.mutable_control_message();
- control_message->set_msg(msg);
- control_message->set_name(name);
- control_message->set_pid(pid);
-
// We must release the fd before sending it to init, otherwise there will be a race with init.
// If init calls close() before Release(), then fdsan will see the wrong tag and abort().
int fd = -1;
if (socket != nullptr && SelinuxGetVendorAndroidVersion() > __ANDROID_API_Q__) {
fd = socket->Release();
- control_message->set_fd(fd);
}
- if (auto result = SendMessage(init_socket, property_msg); !result.ok()) {
- // We've already released the fd above, so if we fail to send the message to init, we need
- // to manually free it here.
- if (fd != -1) {
- close(fd);
- }
- *error = "Failed to send control message: " + result.error().message();
- return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ bool queue_success = QueueControlMessage(msg, name, pid, fd);
+ if (!queue_success && fd != -1) {
+ uint32_t response = PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
+ close(fd);
}
return PROP_SUCCESS;
@@ -1110,14 +1104,6 @@
persistent_properties_loaded = true;
break;
}
- case InitMessage::kStopSendingMessages: {
- accept_messages = false;
- break;
- }
- case InitMessage::kStartSendingMessages: {
- accept_messages = true;
- break;
- }
default:
LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
}
@@ -1157,9 +1143,9 @@
if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
PLOG(FATAL) << "Failed to socketpair() between property_service and init";
}
- *epoll_socket = sockets[0];
+ *epoll_socket = from_init_socket = sockets[0];
init_socket = sockets[1];
- accept_messages = true;
+ StartSendingMessages();
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, {});
@@ -1171,7 +1157,8 @@
listen(property_set_fd, 8);
- std::thread{PropertyServiceThread}.detach();
+ auto new_thread = std::thread{PropertyServiceThread};
+ property_service_thread.swap(new_thread);
}
} // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 506d116..2d49a36 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,5 +32,8 @@
void PropertyInit();
void StartPropertyService(int* epoll_socket);
+void StartSendingMessages();
+void StopSendingMessages();
+
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 8d8bd8e..f006df3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -59,6 +59,7 @@
#include "builtin_arguments.h"
#include "init.h"
#include "mount_namespace.h"
+#include "property_service.h"
#include "reboot_utils.h"
#include "service.h"
#include "service_list.h"
@@ -711,17 +712,12 @@
for (const auto& s : ServiceList::GetInstance()) {
s->UnSetExec();
}
- // We no longer process messages about properties changing coming from property service, so we
- // need to tell property service to stop sending us these messages, otherwise it'll fill the
- // buffers and block indefinitely, causing future property sets, including those that init makes
- // during shutdown in Service::NotifyStateChange() to also block indefinitely.
- SendStopSendingMessagesMessage();
}
static void LeaveShutdown() {
LOG(INFO) << "Leaving shutdown mode";
shutting_down = false;
- SendStartSendingMessagesMessage();
+ StartSendingMessages();
}
static Result<void> UnmountAllApexes() {
@@ -981,6 +977,10 @@
return;
}
+ // We do not want to process any messages (queue'ing triggers, shutdown messages, control
+ // messages, etc) from properties during reboot.
+ StopSendingMessages();
+
if (userspace_reboot) {
HandleUserspaceReboot();
return;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index acbcbd6..808cb7f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -539,9 +539,9 @@
// adb remount, snapshot-based updates, and DSUs all create files during
// first-stage init.
- selinux_android_restorecon("/metadata", SELINUX_ANDROID_RESTORECON_RECURSE);
-
selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
+ selinux_android_restorecon("/metadata/gsi", SELINUX_ANDROID_RESTORECON_RECURSE |
+ SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/test_kill_services/Android.bp b/init/test_kill_services/Android.bp
new file mode 100644
index 0000000..f6e85e2
--- /dev/null
+++ b/init/test_kill_services/Android.bp
@@ -0,0 +1,7 @@
+cc_test {
+ name: "init_kill_services_test",
+ srcs: ["init_kill_services_test.cpp"],
+ shared_libs: ["libbase"],
+ test_suites: ["general-tests"],
+ require_root: true,
+}
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
new file mode 100644
index 0000000..7e543f2
--- /dev/null
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <android-base/properties.h>
+
+#include <iostream>
+
+using ::android::base::GetProperty;
+using ::android::base::SetProperty;
+
+void ExpectKillingServiceRecovers(const std::string& service_name) {
+ const std::string status_prop = "init.svc." + service_name;
+ const std::string pid_prop = "init.svc_debug_pid." + service_name;
+
+ const std::string initial_pid = GetProperty(pid_prop, "");
+
+ EXPECT_EQ("running", GetProperty(status_prop, "")) << status_prop;
+ EXPECT_NE("", initial_pid) << pid_prop;
+
+ EXPECT_EQ(0, system(("kill -9 " + initial_pid).c_str()));
+
+ constexpr size_t kMaxWaitMilliseconds = 10000;
+ constexpr size_t kRetryWaitMilliseconds = 100;
+
+ constexpr size_t kRetryTimes = kMaxWaitMilliseconds / kRetryWaitMilliseconds;
+
+ for (size_t retry = 0; retry < kRetryTimes; retry++) {
+ const std::string& pid = GetProperty(pid_prop, "");
+ if (pid != initial_pid && pid != "") break;
+ usleep(kRetryWaitMilliseconds * 1000);
+ }
+
+ // svc_debug_pid is set after svc property
+ EXPECT_EQ("running", GetProperty(status_prop, ""));
+}
+
+class InitKillServicesTest : public ::testing::TestWithParam<std::string> {};
+
+TEST_P(InitKillServicesTest, KillCriticalProcesses) {
+ ExpectKillingServiceRecovers(GetParam());
+
+ // sanity check init is still responding
+ EXPECT_TRUE(SetProperty("test.death.test", "asdf"));
+ EXPECT_EQ(GetProperty("test.death.test", ""), "asdf");
+ EXPECT_TRUE(SetProperty("test.death.test", ""));
+}
+
+static inline std::string PrintName(const testing::TestParamInfo<std::string>& info) {
+ return info.param;
+}
+
+INSTANTIATE_TEST_CASE_P(DeathTest, InitKillServicesTest,
+ ::testing::Values("lmkd", "ueventd", "hwservicemanager", "servicemanager"),
+ PrintName);
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 1bbffaf..4081b21 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -60,6 +60,8 @@
srcs: [
"zip_archive.cc",
"zip_archive_stream_entry.cc",
+ "zip_cd_entry_map.cc",
+ "zip_error.cpp",
"zip_writer.cc",
],
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 047af90..098a9cb 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -126,6 +126,9 @@
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);
/*
@@ -222,6 +225,12 @@
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);
#if !defined(_WIN32)
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 4451507..aa8bafc 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -85,100 +85,6 @@
* of the string length into the hash table entry.
*/
-/*
- * 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<int32_t, 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 {0, 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};
-}
-
-int32_t 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 0;
-}
-
-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) {
- 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);
-}
-
#if defined(__BIONIC__)
uint64_t GetOwnerTag(const ZipArchive* archive) {
return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
@@ -186,8 +92,8 @@
}
#endif
-ZipArchive::ZipArchive(const int fd, bool assume_ownership)
- : mapped_zip(fd),
+ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
+ : mapped_zip(map),
close_file(assume_ownership),
directory_offset(0),
central_directory(),
@@ -195,7 +101,8 @@
num_entries(0) {
#if defined(__BIONIC__)
if (assume_ownership) {
- android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
+ CHECK(mapped_zip.HasFd());
+ android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
}
#endif
}
@@ -357,12 +264,12 @@
const size_t cd_length = archive->central_directory.GetMapLength();
const uint16_t num_entries = archive->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.
- */
- archive->cd_entry_map = CdEntryMapZip32::Create(num_entries);
+ // TODO(xunchang) parse the zip64 Eocd
+ if (num_entries > UINT16_MAX) {
+ archive->cd_entry_map = CdEntryMapZip64::Create();
+ } else {
+ archive->cd_entry_map = CdEntryMapZip32::Create(num_entries);
+ }
if (archive->cd_entry_map == nullptr) {
return kAllocationFailed;
}
@@ -456,14 +363,32 @@
int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
bool assume_ownership) {
- ZipArchive* archive = new ZipArchive(fd, 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(fd, true);
+ ZipArchive* archive = new ZipArchive(MappedZipFile(fd), true);
*handle = archive;
if (fd < 0) {
@@ -1120,24 +1045,14 @@
return ExtractToWriter(archive, entry, &writer);
}
-const char* ErrorCodeString(int32_t error_code) {
- // Make sure that the number of entries in kErrorMessages and ErrorCodes
- // 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";
-}
-
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:
@@ -1177,31 +1092,65 @@
return base_ptr_;
}
+off64_t MappedZipFile::GetFileOffset() const {
+ return fd_offset_;
+}
+
off64_t MappedZipFile::GetFileLength() const {
if (has_fd_) {
- off64_t result = lseek64(fd_, 0, SEEK_END);
- if (result == -1) {
+ 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 result;
+ return data_length_;
} else {
if (base_ptr_ == nullptr) {
ALOGE("Zip: invalid file map");
return -1;
}
- return static_cast<off64_t>(data_length_);
+ 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 (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
+ 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 || off > static_cast<off64_t>(data_length_)) {
+ if (off < 0 || off > data_length_) {
ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
return false;
}
@@ -1219,7 +1168,8 @@
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(),
- cd_start_offset, cd_size, PROT_READ);
+ 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));
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 68977f6..3509b89 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -28,80 +28,20 @@
#include "android-base/macros.h"
#include "android-base/mapped_file.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",
-};
-
-enum ErrorCodes : int32_t {
- 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,
-
- kLastErrorCode = kAllocationFailed,
-};
+#include "zip_cd_entry_map.h"
+#include "zip_error.h"
class MappedZipFile {
public:
explicit MappedZipFile(const int fd)
- : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
+ : 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), base_ptr_(address), data_length_(static_cast<off64_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_; }
@@ -109,6 +49,8 @@
const void* GetBasePtr() const;
+ off64_t GetFileOffset() const;
+
off64_t GetFileLength() const;
bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
@@ -121,9 +63,10 @@
const bool has_fd_;
const int fd_;
+ const off64_t fd_offset_;
const void* const base_ptr_;
- const off64_t data_length_;
+ mutable off64_t data_length_;
};
class CentralDirectory {
@@ -141,75 +84,6 @@
size_t length_;
};
-// 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 int32_t 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<int32_t, 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);
-
- int32_t AddToMap(std::string_view name, const uint8_t* start) override;
- std::pair<int32_t, 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};
-};
-
struct ZipArchive {
// open Zip archive
mutable MappedZipFile mapped_zip;
@@ -224,7 +98,7 @@
uint16_t num_entries;
std::unique_ptr<CdEntryMapInterface> cd_entry_map;
- ZipArchive(const int fd, bool assume_ownership);
+ ZipArchive(MappedZipFile&& map, bool assume_ownership);
ZipArchive(const void* address, size_t length);
~ZipArchive();
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 0916304..5caca8a 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -24,11 +24,14 @@
#include <unistd.h>
#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/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
@@ -53,6 +56,76 @@
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));
@@ -108,6 +181,32 @@
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));
@@ -254,6 +353,48 @@
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.
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+ const uint32_t a_size = 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 uint32_t b_size = 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));
diff --git a/libziparchive/zip_cd_entry_map.cc b/libziparchive/zip_cd_entry_map.cc
new file mode 100644
index 0000000..f187c06
--- /dev/null
+++ b/libziparchive/zip_cd_entry_map.cc
@@ -0,0 +1,156 @@
+/*
+ * 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
new file mode 100644
index 0000000..4957f75
--- /dev/null
+++ b/libziparchive/zip_cd_entry_map.h
@@ -0,0 +1,114 @@
+/*
+ * 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
new file mode 100644
index 0000000..107ec47
--- /dev/null
+++ b/libziparchive/zip_error.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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",
+};
+
+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
new file mode 100644
index 0000000..37fd55f
--- /dev/null
+++ b/libziparchive/zip_error.h
@@ -0,0 +1,70 @@
+/*
+ * 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,
+
+ kLastErrorCode = kAllocationFailed,
+};
diff --git a/storaged/main.cpp b/storaged/main.cpp
index a7bda14..bbed210 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -71,6 +71,7 @@
bool flag_dump_perf = false;
int opt;
+ signal(SIGPIPE, SIG_IGN);
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
for (;;) {