Merge "Add EH frame and debug frame support."
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index f5d0f02..e533a00 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -125,7 +125,7 @@
static int _adb_connect(const std::string& service, std::string* error) {
D("_adb_connect: %s", service.c_str());
- if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
+ if (service.empty() || service.size() > MAX_PAYLOAD) {
*error = android::base::StringPrintf("bad service name length (%zd)",
service.size());
return -1;
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index ca8729e..38e3116 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -31,7 +31,7 @@
bool SendProtocolString(int fd, const std::string& s) {
unsigned int length = s.size();
- if (length > MAX_PAYLOAD_V1 - 4) {
+ if (length > MAX_PAYLOAD - 4) {
errno = EMSGSIZE;
return false;
}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 20c63b3..11c0ec9 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -74,13 +74,18 @@
template <typename Fn>
void PopAll(Fn fn) {
- std::unique_lock<std::mutex> lock(mutex);
- cv.wait(lock, [this]() { return !queue.empty(); });
+ std::vector<T> popped;
- for (const T& t : queue) {
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ cv.wait(lock, [this]() { return !queue.empty(); });
+ popped = std::move(queue);
+ queue.clear();
+ }
+
+ for (const T& t : popped) {
fn(t);
}
- queue.clear();
}
};
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7e77b5e..e7f44c6 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -180,10 +180,6 @@
if (port_count < 0) return "";
return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
}
-
-static bool is_device_accessible(libusb_device* device) {
- return access(get_device_dev_path(device).c_str(), R_OK | W_OK) == 0;
-}
#endif
static bool endpoint_is_output(uint8_t endpoint) {
@@ -377,9 +373,10 @@
{
std::unique_lock<std::mutex> lock(usb_handles_mutex);
usb_handles[device_address] = std::move(result);
- }
- register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
+ register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
+ writable);
+ }
LOG(INFO) << "registered new usb device '" << device_serial << "'";
}
@@ -388,18 +385,12 @@
static void device_connected(libusb_device* device) {
#if defined(__linux__)
// Android's host linux libusb uses netlink instead of udev for device hotplug notification,
- // which means we can get hotplug notifications before udev has updated ownership/perms on
- // the device. Since we're not going to be able to link against the system's libudev any
- // time soon, hack around this by checking for accessibility in a loop.
+ // which means we can get hotplug notifications before udev has updated ownership/perms on the
+ // device. Since we're not going to be able to link against the system's libudev any time soon,
+ // hack around this by inserting a sleep.
auto thread = std::thread([device]() {
std::string device_path = get_device_dev_path(device);
- auto start = std::chrono::steady_clock::now();
- while (std::chrono::steady_clock::now() - start < 500ms) {
- if (is_device_accessible(device)) {
- break;
- }
- std::this_thread::sleep_for(10ms);
- }
+ std::this_thread::sleep_for(1s);
process_device(device);
if (--connecting_devices == 0) {
@@ -422,8 +413,10 @@
if (!it->second->device_handle) {
// If the handle is null, we were never able to open the device.
unregister_usb_transport(it->second.get());
+ usb_handles.erase(it);
+ } else {
+ // Closure of the transport will erase the usb_handle.
}
- usb_handles.erase(it);
}
}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 68ae4af..c9f1ee9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -599,6 +599,13 @@
std::string service_string = ShellServiceString(use_shell_protocol,
type_arg, command);
+ // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
+ // Use |use_shell_protocol| to determine whether to allow a command longer than that.
+ if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
+ fprintf(stderr, "error: shell command too long\n");
+ return 1;
+ }
+
// Make local stdin raw if the device allocates a PTY, which happens if:
// 1. We are explicitly asking for a PTY shell, or
// 2. We don't specify shell type and are starting an interactive session.
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 14ad1ff..e0143c6 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -686,7 +686,7 @@
}
len = unhex(p->data, 4);
- if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
+ if ((len < 1) || (len > MAX_PAYLOAD)) {
D("SS(%d): bad size (%d)", s->id, len);
goto fail;
}
diff --git a/adb/test_device.py b/adb/test_device.py
index 737d0c2..9e1a2ec 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -342,6 +342,13 @@
out = self.device.shell(['echo', 'foo'])[0]
self.assertEqual(out, 'foo' + self.device.linesep)
+ def test_shell_command_length(self):
+ # Devices that have shell_v2 should be able to handle long commands.
+ if self.device.has_shell_protocol():
+ rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
+ self.assertEqual(rc, 0)
+ self.assertTrue(out == ('x' * 16384 + '\n'))
+
def test_shell_nocheck_failure(self):
rc, out, _ = self.device.shell_nocheck(['false'])
self.assertNotEqual(rc, 0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 308ee8d..2bbbefd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -603,15 +603,15 @@
static void transport_unref(atransport* t) {
CHECK(t != nullptr);
- std::lock_guard<std::mutex> lock(transport_lock);
- CHECK_GT(t->ref_count, 0u);
- t->ref_count--;
- if (t->ref_count == 0) {
+ size_t old_refcount = t->ref_count--;
+ CHECK_GT(old_refcount, 0u);
+
+ if (old_refcount == 1u) {
D("transport: %s unref (kicking and closing)", t->serial);
t->close(t);
remove_transport(t);
} else {
- D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+ D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
}
}
diff --git a/adb/transport.h b/adb/transport.h
index 57fc988..4a89ed9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -61,7 +61,7 @@
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
+ atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
@@ -88,7 +88,7 @@
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
- size_t ref_count = 0;
+ std::atomic<size_t> ref_count;
uint32_t sync_token = 0;
bool online = false;
TransportType type = kTransportAny;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index a9e583f..6cedd92 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -388,6 +388,25 @@
D("transport: qemu_socket_thread() exiting");
return;
}
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+static bool use_qemu_goldfish() {
+ // Legacy way to detect if adbd should use the goldfish pipe is to check for
+ // ro.kernel.qemu, keep that behaviour for backward compatibility.
+ if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+ return true;
+ }
+ // If service.adb.transport is present and is set to "goldfish", use the
+ // QEMUD pipe.
+ if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+ return true;
+ }
+ return false;
+}
+
#endif // !ADB_HOST
void local_init(int port)
@@ -401,13 +420,7 @@
#else
// For the adbd daemon in the system image we need to distinguish
// between the device, and the emulator.
- if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
- // Running inside the emulator: use QEMUD pipe as the transport.
- func = qemu_socket_thread;
- } else {
- // Running inside the device: use TCP socket as the transport.
- func = server_socket_thread;
- }
+ func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
debug_name = "server";
#endif // !ADB_HOST
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 558bc72..df7201d 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -44,6 +44,9 @@
#include <private/android_filesystem_config.h>
#include <procinfo/process.h>
+#define ATRACE_TAG ATRACE_TAG_BIONIC
+#include <utils/Trace.h>
+
#include "backtrace.h"
#include "tombstone.h"
#include "utility.h"
@@ -101,6 +104,7 @@
}
static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
+ ATRACE_CALL();
android::base::unique_fd amfd(socket_local_client(
"/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
if (amfd.get() == -1) {
@@ -176,6 +180,7 @@
}
static void drop_capabilities() {
+ ATRACE_CALL();
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -194,6 +199,8 @@
}
int main(int argc, char** argv) {
+ atrace_begin(ATRACE_TAG, "before reparent");
+
pid_t target = getppid();
bool tombstoned_connected = false;
unique_fd tombstoned_socket;
@@ -261,6 +268,8 @@
PLOG(FATAL) << "parent died";
}
+ atrace_end(ATRACE_TAG);
+
// Reparent ourselves to init, so that the signal handler can waitpid on the
// original process to avoid leaving a zombie for non-fatal dumps.
pid_t forkpid = fork();
@@ -270,47 +279,60 @@
exit(0);
}
+ ATRACE_NAME("after reparent");
+
// Die if we take too long.
alarm(2);
std::string attach_error;
- // Seize the main thread.
- if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
- LOG(FATAL) << attach_error;
- }
-
- // Seize the siblings.
std::map<pid_t, std::string> threads;
+
{
- std::set<pid_t> siblings;
- if (!android::procinfo::GetProcessTids(target, &siblings)) {
- PLOG(FATAL) << "failed to get process siblings";
+ ATRACE_NAME("ptrace");
+ // Seize the main thread.
+ if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
+ LOG(FATAL) << attach_error;
}
- // but not the already attached main thread.
- siblings.erase(main_tid);
- // or the handler pseudothread.
- siblings.erase(pseudothread_tid);
+ // Seize the siblings.
+ {
+ std::set<pid_t> siblings;
+ if (!android::procinfo::GetProcessTids(target, &siblings)) {
+ PLOG(FATAL) << "failed to get process siblings";
+ }
- for (pid_t sibling_tid : siblings) {
- if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
- LOG(WARNING) << attach_error;
- } else {
- threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+ // but not the already attached main thread.
+ siblings.erase(main_tid);
+ // or the handler pseudothread.
+ siblings.erase(pseudothread_tid);
+
+ for (pid_t sibling_tid : siblings) {
+ if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
+ LOG(WARNING) << attach_error;
+ } else {
+ threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+ }
}
}
}
// Collect the backtrace map, open files, and process/thread names, while we still have caps.
- std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
- if (!backtrace_map) {
- LOG(FATAL) << "failed to create backtrace map";
+ std::unique_ptr<BacktraceMap> backtrace_map;
+ {
+ ATRACE_NAME("backtrace map");
+ backtrace_map.reset(BacktraceMap::Create(main_tid));
+ if (!backtrace_map) {
+ LOG(FATAL) << "failed to create backtrace map";
+ }
}
// Collect the list of open files.
OpenFilesList open_files;
- populate_open_files_list(target, &open_files);
+ {
+ ATRACE_NAME("open files");
+ populate_open_files_list(target, &open_files);
+ }
std::string process_name = get_process_name(main_tid);
threads.emplace(main_tid, get_thread_name(main_tid));
@@ -318,9 +340,12 @@
// Drop our capabilities now that we've attached to the threads we care about.
drop_capabilities();
- const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
- LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
- tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+ {
+ ATRACE_NAME("tombstoned_connect");
+ const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
+ LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
+ tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+ }
// Write a '\1' to stdout to tell the crashing process to resume.
// It also restores the value of PR_SET_DUMPABLE at this point.
@@ -349,9 +374,12 @@
}
siginfo_t siginfo = {};
- if (!wait_for_signal(main_tid, &siginfo)) {
- printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
- exit(1);
+ {
+ ATRACE_NAME("wait_for_signal");
+ if (!wait_for_signal(main_tid, &siginfo)) {
+ printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+ exit(1);
+ }
}
int signo = siginfo.si_signo;
@@ -373,8 +401,10 @@
std::string amfd_data;
if (backtrace) {
+ ATRACE_NAME("dump_backtrace");
dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
} else {
+ ATRACE_NAME("engrave_tombstone");
engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9008b95..4660f3d 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,8 +88,12 @@
} \
} while (0)
+#define ASSERT_BACKTRACE_FRAME(result, frame_name) \
+ ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
+ R"(/libc.so \()" frame_name R"(\+)")
+
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
- DebuggerdDumpType intercept_type) {
+ InterceptStatus* status, DebuggerdDumpType intercept_type) {
intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (intercept_fd->get() == -1) {
@@ -136,7 +140,7 @@
<< ", received " << rc;
}
- ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+ *status = response.status;
}
class CrasherTest : public ::testing::Test {
@@ -180,7 +184,9 @@
FAIL() << "crasher hasn't been started";
}
- tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type);
+ InterceptStatus status;
+ tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
}
void CrasherTest::FinishIntercept(int* result) {
@@ -305,7 +311,7 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "tgkill");
}
TEST_F(CrasherTest, signal) {
@@ -441,7 +447,7 @@
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+ ASSERT_BACKTRACE_FRAME(result, "read");
int status;
ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
@@ -452,7 +458,7 @@
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "tgkill");
}
TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -472,7 +478,7 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "tgkill");
}
TEST_F(CrasherTest, capabilities) {
@@ -529,7 +535,7 @@
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "tgkill");
}
TEST_F(CrasherTest, fake_pid) {
@@ -560,7 +566,7 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "tgkill");
}
TEST(crash_dump, zombie) {
@@ -598,7 +604,9 @@
pid_t pid = 123'456'789 + i;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
{
unique_fd tombstoned_socket, input_fd;
@@ -630,7 +638,9 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
// Pretend to crash, and then immediately close the socket.
unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -661,7 +671,9 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
{
unique_fd tombstoned_socket, input_fd;
@@ -685,3 +697,65 @@
thread.join();
}
}
+
+TEST(tombstoned, java_trace_intercept_smoke) {
+ // Using a "real" PID is a little dangerous here - if the test fails
+ // or crashes, we might end up getting a bogus / unreliable stack
+ // trace.
+ const pid_t self = getpid();
+
+ unique_fd intercept_fd, output_fd;
+ InterceptStatus status;
+ tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+ // First connect to tombstoned requesting a native backtrace. This
+ // should result in a "regular" FD and not the installed intercept.
+ const char native[] = "native";
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+
+ // Then, connect to tombstoned asking for a java backtrace. This *should*
+ // trigger the intercept.
+ const char java[] = "java";
+ ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+
+ char outbuf[sizeof(java)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+ ASSERT_STREQ("java", outbuf);
+}
+
+TEST(tombstoned, multiple_intercepts) {
+ const pid_t fake_pid = 1'234'567;
+ unique_fd intercept_fd, output_fd;
+ InterceptStatus status;
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+ unique_fd intercept_fd_2, output_fd_2;
+ tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+}
+
+TEST(tombstoned, intercept_any) {
+ const pid_t fake_pid = 1'234'567;
+
+ unique_fd intercept_fd, output_fd;
+ InterceptStatus status;
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+ const char any[] = "any";
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+
+ char outbuf[sizeof(any)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+ ASSERT_STREQ("any", outbuf);
+}
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 0a9f000..b920ebc 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -23,7 +23,9 @@
#include <array>
#include <deque>
+#include <string>
#include <unordered_map>
+#include <utility>
#include <event2/event.h>
#include <event2/listener.h>
@@ -75,23 +77,24 @@
find_oldest_artifact();
}
- unique_fd get_output_fd() {
+ std::pair<unique_fd, std::string> get_output() {
unique_fd result;
- char buf[PATH_MAX];
- snprintf(buf, sizeof(buf), "%s%02d", file_name_prefix_.c_str(), next_artifact_);
+ std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
+
// Unlink and create the file, instead of using O_TRUNC, to avoid two processes
// interleaving their output in case we ever get into that situation.
- if (unlinkat(dir_fd_, buf, 0) != 0 && errno != ENOENT) {
- PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << buf;
+ if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
+ PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
}
- result.reset(openat(dir_fd_, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+ result.reset(openat(dir_fd_, file_name.c_str(),
+ O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
if (result == -1) {
- PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << buf;
+ PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
}
next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
- return result;
+ return {std::move(result), dir_path_ + "/" + file_name};
}
bool maybe_enqueue_crash(Crash* crash) {
@@ -124,8 +127,7 @@
time_t oldest_time = std::numeric_limits<time_t>::max();
for (size_t i = 0; i < max_artifacts_; ++i) {
- std::string path = android::base::StringPrintf("%s/%s%02zu", dir_path_.c_str(),
- file_name_prefix_.c_str(), i);
+ std::string path = StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
struct stat st;
if (stat(path.c_str(), &st) != 0) {
if (errno == ENOENT) {
@@ -163,7 +165,7 @@
};
// Whether java trace dumps are produced via tombstoned.
-static constexpr bool kJavaTraceDumpsEnabled = false;
+static constexpr bool kJavaTraceDumpsEnabled = true;
/* static */ CrashQueue* const CrashQueue::tombstone =
new CrashQueue("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
@@ -183,6 +185,7 @@
unique_fd crash_fd;
pid_t crash_pid;
event* crash_event = nullptr;
+ std::string crash_path;
DebuggerdDumpType crash_type;
};
@@ -203,7 +206,7 @@
static void perform_request(Crash* crash) {
unique_fd output_fd;
if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
- output_fd = get_crash_queue(crash)->get_output_fd();
+ std::tie(output_fd, crash->crash_path) = get_crash_queue(crash)->get_output();
}
TombstonedCrashPacket response = {
@@ -341,6 +344,10 @@
goto fail;
}
+ if (!crash->crash_path.empty()) {
+ LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+ }
+
fail:
CrashQueue* queue = get_crash_queue(crash);
delete crash;
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 96ab57d..e55c886 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -29,6 +29,7 @@
cc_library {
name: "libdemangle",
defaults: ["libdemangle_defaults"],
+ vendor_available: true,
srcs: [
"Demangler.cpp",
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 70cd608..7a41b14 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -337,16 +337,8 @@
dirent* dp;
while ((dp = readdir(fstabdir.get())) != NULL) {
- // skip over name and compatible
- if (dp->d_type != DT_DIR) {
- continue;
- }
-
- // skip if its not 'vendor', 'odm' or 'system'
- if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
- strcmp(dp->d_name, "vendor")) {
- continue;
- }
+ // skip over name, compatible and .
+ if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
// create <dev> <mnt_point> <type> <mnt_flags> <fsmgr_flags>\n
std::vector<std::string> fstab_entry;
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fa10bc..0177fdf 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -689,27 +689,55 @@
return read_verity_state(fstab->verity_loc, offset, mode);
}
-static void update_verity_table_blk_device(char *blk_device, char **table)
-{
- std::string result, word;
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+// verity table token: /dev/block/bootdevice/by-name/vendor
+// blk_device-1 (non-A/B): /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+// blk_device-2 (A/B): /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+// verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+// blk_device: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+ bool slot_select) {
+ bool updated = false;
+ std::string result, ab_suffix;
auto tokens = android::base::Split(*table, " ");
+ // If slot_select is set, it means blk_device is already updated with ab_suffix.
+ if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
for (const auto& token : tokens) {
- if (android::base::StartsWith(token, "/dev/block/") &&
- android::base::StartsWith(blk_device, token.c_str())) {
- word = blk_device;
+ std::string new_token;
+ if (android::base::StartsWith(token, "/dev/block/")) {
+ if (token == blk_device) return; // no need to update if they're already the same.
+ std::size_t found1 = blk_device.find("by-name");
+ std::size_t found2 = token.find("by-name");
+ if (found1 != std::string::npos && found2 != std::string::npos &&
+ blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+ new_token = blk_device;
+ }
+ }
+
+ if (!new_token.empty()) {
+ updated = true;
+ LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+ << "'";
} else {
- word = token;
+ new_token = token;
}
if (result.empty()) {
- result = word;
+ result = new_token;
} else {
- result += " " + word;
+ result += " " + new_token;
}
}
- if (result.empty()) {
+ if (!updated) {
return;
}
@@ -825,10 +853,9 @@
LINFO << "Enabling dm-verity for " << mount_point.c_str()
<< " (mode " << params.mode << ")";
- if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
- // Update the verity params using the actual block device path
- update_verity_table_blk_device(fstab->blk_device, ¶ms.table);
- }
+ // Update the verity params using the actual block device path
+ update_verity_table_blk_device(fstab->blk_device, ¶ms.table,
+ fstab->fs_mgr_flags & MF_SLOTSELECT);
// load the verity mapping table
if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 31fc2df..ece8693 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -71,8 +71,17 @@
// 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`.
+ // TODO: should be overridden by extra time field, if present.
uint32_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.
+ mode_t unix_mode;
+
// 1 if this entry contains a data descriptor segment, 0
// otherwise.
uint8_t has_data_descriptor;
diff --git a/init/Android.bp b/init/Android.bp
index af1e9d3..fce424e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -20,7 +20,6 @@
sanitize: {
misc_undefined: ["integer"],
},
- tidy_checks: ["-misc-forwarding-reference-overload"],
cppflags: [
"-DLOG_UEVENTS=0",
"-Wall",
diff --git a/init/Android.mk b/init/Android.mk
index 489d076..6cd47f4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -40,8 +40,6 @@
# --
include $(CLEAR_VARS)
-# b/38002385, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
bootchart.cpp \
diff --git a/init/devices.cpp b/init/devices.cpp
index 40f9740..c52d8f8 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -175,7 +175,7 @@
if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
}
- if (access(path.c_str(), F_OK) == 0) {
+ if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
LOG(VERBOSE) << "restorecon_recursive: " << path;
if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
@@ -467,12 +467,13 @@
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems)
+ std::vector<Subsystem> subsystems, bool skip_restorecon)
: dev_permissions_(std::move(dev_permissions)),
sysfs_permissions_(std::move(sysfs_permissions)),
subsystems_(std::move(subsystems)),
- sehandle_(selinux_android_file_context_handle()) {}
+ sehandle_(selinux_android_file_context_handle()),
+ skip_restorecon_(skip_restorecon) {}
DeviceHandler::DeviceHandler()
: DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
- std::vector<Subsystem>{}) {}
+ std::vector<Subsystem>{}, false) {}
diff --git a/init/devices.h b/init/devices.h
index 50f49fc..09a0ce3 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -114,14 +114,21 @@
DeviceHandler();
DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems);
+ std::vector<Subsystem> subsystems, bool skip_restorecon);
~DeviceHandler(){};
void HandleDeviceEvent(const Uevent& uevent);
+
+ void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
+
+ void HandlePlatformDeviceEvent(const Uevent& uevent);
+ void HandleBlockDeviceEvent(const Uevent& uevent) const;
+ void HandleGenericDeviceEvent(const Uevent& uevent) const;
+
std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+ void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
private:
- void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
const std::string& path, const std::vector<std::string>& links) const;
void MakeDevice(const std::string& path, int block, int major, int minor,
@@ -129,15 +136,13 @@
std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
int minor, const std::vector<std::string>& links) const;
- void HandlePlatformDeviceEvent(const Uevent& uevent);
- void HandleBlockDeviceEvent(const Uevent& uevent) const;
- void HandleGenericDeviceEvent(const Uevent& uevent) const;
std::vector<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
PlatformDeviceList platform_devices_;
selabel_handle* sehandle_;
+ bool skip_restorecon_;
};
// Exposed for testing
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 1471aeb..844c605 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <sys/sendfile.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <string>
@@ -103,14 +104,29 @@
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
// Loading the firmware in a child means we can do that in parallel...
- // (We ignore SIGCHLD rather than wait for our children.)
+ // We double fork instead of waiting for these processes.
pid_t pid = fork();
- if (pid == 0) {
- Timer t;
- ProcessFirmwareEvent(uevent);
- LOG(INFO) << "loading " << uevent.path << " took " << t;
- _exit(EXIT_SUCCESS);
- } else if (pid == -1) {
+ if (pid == -1) {
PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+ return;
}
+
+ if (pid == 0) {
+ pid = fork();
+ if (pid == -1) {
+ PLOG(ERROR) << "could not fork a sceond time to process firmware event for "
+ << uevent.firmware;
+ _exit(EXIT_FAILURE);
+ }
+ if (pid == 0) {
+ Timer t;
+ ProcessFirmwareEvent(uevent);
+ LOG(INFO) << "loading " << uevent.path << " took " << t;
+ _exit(EXIT_SUCCESS);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ waitpid(pid, nullptr, 0);
}
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 8a7d9a2..0f2b1f3 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -60,9 +60,8 @@
virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
bool need_dm_verity_;
- // Device tree fstab entries.
+
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
- // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
DeviceHandler device_handler_;
@@ -115,12 +114,10 @@
LOG(ERROR) << "Failed to read fstab from device tree";
return;
}
- for (auto mount_point : {"/system", "/vendor", "/odm"}) {
- fstab_rec* fstab_rec =
- fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
- if (fstab_rec != nullptr) {
- mount_fstab_recs_.push_back(fstab_rec);
- }
+ // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+ // for easier manipulation later, e.g., range-base for loop.
+ for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+ mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
}
}
@@ -420,7 +417,7 @@
// Public functions
// ----------------
-// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+// Mounts partitions specified by fstab in device tree.
bool DoFirstStageMount() {
// Skips first stage mount if we're in recovery mode.
if (IsRecoveryMode()) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3490544..b43da4e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,6 +39,7 @@
#include <sys/_system_properties.h>
#include <memory>
+#include <queue>
#include <vector>
#include <android-base/file.h>
@@ -162,7 +163,7 @@
return true;
}
-uint32_t property_set(const std::string& name, const std::string& value) {
+static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
if (!is_legal_property_name(name)) {
@@ -176,12 +177,6 @@
return PROP_ERROR_INVALID_VALUE;
}
- if (name == "selinux.restorecon_recursive" && valuelen > 0) {
- if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
- LOG(ERROR) << "Failed to restorecon_recursive " << value;
- }
- }
-
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
@@ -210,6 +205,85 @@
return PROP_SUCCESS;
}
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+ pid_t pid;
+ PropertyAsyncFunc func;
+ std::string name;
+ std::string value;
+};
+
+static std::queue<PropertyChildInfo> property_children;
+
+static void PropertyChildLaunch() {
+ auto& info = property_children.front();
+ pid_t pid = fork();
+ if (pid < 0) {
+ LOG(ERROR) << "Failed to fork for property_set_async";
+ while (!property_children.empty()) {
+ property_children.pop();
+ }
+ return;
+ }
+ if (pid != 0) {
+ info.pid = pid;
+ } else {
+ if (info.func(info.name, info.value) != 0) {
+ LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
+ << "\") failed";
+ }
+ exit(0);
+ }
+}
+
+bool PropertyChildReap(pid_t pid) {
+ if (property_children.empty()) {
+ return false;
+ }
+ auto& info = property_children.front();
+ if (info.pid != pid) {
+ return false;
+ }
+ if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set async property " << info.name;
+ }
+ property_children.pop();
+ if (!property_children.empty()) {
+ PropertyChildLaunch();
+ }
+ return true;
+}
+
+static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
+ PropertyAsyncFunc func) {
+ if (value.empty()) {
+ return PropertySetImpl(name, value);
+ }
+
+ PropertyChildInfo info;
+ info.func = func;
+ info.name = name;
+ info.value = value;
+ property_children.push(info);
+ if (property_children.size() == 1) {
+ PropertyChildLaunch();
+ }
+ return PROP_SUCCESS;
+}
+
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+ return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+uint32_t property_set(const std::string& name, const std::string& value) {
+ if (name == "selinux.restorecon_recursive") {
+ return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+ }
+
+ return PropertySetImpl(name, value);
+}
+
class SocketConnection {
public:
SocketConnection(int socket, const struct ucred& cred)
diff --git a/init/property_service.h b/init/property_service.h
index 9a5b6f6..9251722 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,6 +26,8 @@
const char* name;
};
+extern bool PropertyChildReap(pid_t pid);
+
void property_init(void);
void property_load_boot_defaults(void);
void load_persist_props(void);
diff --git a/init/service.cpp b/init/service.cpp
index 7c931da..b73ddfb 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -209,17 +209,6 @@
}
void Service::KillProcessGroup(int signal) {
- // We ignore reporting errors of ESRCH as this commonly happens in the below case,
- // 1) Terminate() is called, which sends SIGTERM to the process
- // 2) The process successfully exits
- // 3) ReapOneProcess() is called, which calls waitpid(-1, ...) which removes the pid entry.
- // 4) Reap() is called, which sends SIGKILL, but the pid no longer exists.
- // TODO: sigaction for SIGCHLD reports the pid of the exiting process,
- // we should do this kill with that pid first before calling waitpid().
- if (kill(-pid_, signal) == -1 && errno != ESRCH) {
- PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
- }
-
// If we've already seen a successful result from killProcessGroup*(), then we have removed
// the cgroup already and calling these functions a second time will simply result in an error.
// This is true regardless of which signal was sent.
@@ -1096,6 +1085,8 @@
} else if (pid == -1) {
PLOG(ERROR) << "waitpid failed";
return false;
+ } else if (PropertyChildReap(pid)) {
+ return true;
}
Service* svc = FindServiceByPid(pid);
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 27c5d23..01b8250 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -165,7 +165,7 @@
return RegenerateUeventsForDir(d.get(), callback);
}
-static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
void UeventListener::RegenerateUevents(RegenerateCallback callback) const {
for (const auto path : kRegenerationPaths) {
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index ba31aaa..8e6f3b4 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -35,6 +35,8 @@
using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>;
using PollCallback = std::function<void(const Uevent&)>;
+extern const char* kRegenerationPaths[3];
+
class UeventListener {
public:
UeventListener();
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index bd21a3e..31e4106 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -22,10 +22,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/wait.h>
+
+#include <set>
+#include <thread>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <selinux/android.h>
#include <selinux/selinux.h>
#include "devices.h"
@@ -35,6 +40,194 @@
#include "ueventd_parser.h"
#include "util.h"
+// At a high level, ueventd listens for uevent messages generated by the kernel through a netlink
+// socket. When ueventd receives such a message it handles it by taking appropriate actions,
+// which can typically be creating a device node in /dev, setting file permissions, setting selinux
+// labels, etc.
+// Ueventd also handles loading of firmware that the kernel requests, and creates symlinks for block
+// and character devices.
+
+// When ueventd starts, it regenerates uevents for all currently registered devices by traversing
+// /sys and writing 'add' to each 'uevent' file that it finds. This causes the kernel to generate
+// and resend uevent messages for all of the currently registered devices. This is done, because
+// ueventd would not have been running when these devices were registered and therefore was unable
+// to receive their uevent messages and handle them appropriately. This process is known as
+// 'cold boot'.
+
+// 'init' currently waits synchronously on the cold boot process of ueventd before it continues
+// its boot process. For this reason, cold boot should be as quick as possible. One way to achieve
+// a speed up here is to parallelize the handling of ueventd messages, which consume the bulk of the
+// time during cold boot.
+
+// Handling of uevent messages has two unique properties:
+// 1) It can be done in isolation; it doesn't need to read or write any status once it is started.
+// 2) It uses setegid() and setfscreatecon() so either care (aka locking) must be taken to ensure
+// that no file system operations are done while the uevent process has an abnormal egid or
+// fscreatecon or this handling must happen in a separate process.
+// Given the above two properties, it is best to fork() subprocesses to handle the uevents. This
+// reduces the overhead and complexity that would be required in a solution with threads and locks.
+// In testing, a racy multithreaded solution has the same performance as the fork() solution, so
+// there is no reason to deal with the complexity of the former.
+
+// One other important caveat during the boot process is the handling of SELinux restorecon.
+// Since many devices have child devices, calling selinux_android_restorecon() recursively for each
+// device when its uevent is handled, results in multiple restorecon operations being done on a
+// given file. It is more efficient to simply do restorecon recursively on /sys during cold boot,
+// than to do restorecon on each device as its uevent is handled. This only applies to cold boot;
+// once that has completed, restorecon is done for each device as its uevent is handled.
+
+// With all of the above considered, the cold boot process has the below steps:
+// 1) ueventd regenerates uevents by doing the /sys traversal and listens to the netlink socket for
+// the generated uevents. It writes these uevents into a queue represented by a vector.
+//
+// 2) ueventd forks 'n' separate uevent handler subprocesses and has each of them to handle the
+// uevents in the queue based on a starting offset (their process number) and a stride (the total
+// number of processes). Note that no IPC happens at this point and only const functions from
+// DeviceHandler should be called from this context.
+//
+// 3) In parallel to the subprocesses handling the uevents, the main thread of ueventd calls
+// selinux_android_restorecon() recursively on /sys/class, /sys/block, and /sys/devices.
+//
+// 4) Once the restorecon operation finishes, the main thread calls waitpid() to wait for all
+// subprocess handlers to complete and exit. Once this happens, it marks coldboot as having
+// completed.
+//
+// At this point, ueventd is single threaded, poll()'s and then handles any future uevents.
+
+// Lastly, it should be noted that uevents that occur during the coldboot process are handled
+// without issue after the coldboot process completes. This is because the uevent listener is
+// paused while the uevent handler and restorecon actions take place. Once coldboot completes,
+// the uevent listener resumes in polling mode and will handle the uevents that occurred during
+// coldboot.
+
+class ColdBoot {
+ public:
+ ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+ : uevent_listener_(uevent_listener),
+ device_handler_(device_handler),
+ num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+
+ void Run();
+
+ private:
+ void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
+ void RegenerateUevents();
+ void ForkSubProcesses();
+ void DoRestoreCon();
+ void WaitForSubProcesses();
+
+ UeventListener& uevent_listener_;
+ DeviceHandler& device_handler_;
+
+ unsigned int num_handler_subprocesses_;
+ std::vector<Uevent> uevent_queue_;
+
+ std::set<pid_t> subprocess_pids_;
+};
+
+void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
+ for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
+ auto& uevent = uevent_queue_[i];
+ if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+ device_handler_.FixupSysPermissions(uevent.path, uevent.subsystem);
+ }
+
+ if (uevent.subsystem == "block") {
+ device_handler_.HandleBlockDeviceEvent(uevent);
+ } else {
+ device_handler_.HandleGenericDeviceEvent(uevent);
+ }
+ }
+ _exit(EXIT_SUCCESS);
+}
+
+void ColdBoot::RegenerateUevents() {
+ uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
+ HandleFirmwareEvent(uevent);
+
+ // This is the one mutable part of DeviceHandler, in which platform devices are
+ // added to a vector for later reference. Since there is no communication after
+ // fork()'ing subprocess handlers, all platform devices must be in the vector before
+ // we fork, and therefore they must be handled in this loop.
+ if (uevent.subsystem == "platform") {
+ device_handler_.HandlePlatformDeviceEvent(uevent);
+ }
+
+ uevent_queue_.emplace_back(std::move(uevent));
+ return RegenerationAction::kContinue;
+ });
+}
+
+void ColdBoot::ForkSubProcesses() {
+ for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {
+ auto pid = fork();
+ if (pid < 0) {
+ PLOG(FATAL) << "fork() failed!";
+ }
+
+ if (pid == 0) {
+ UeventHandlerMain(i, num_handler_subprocesses_);
+ }
+
+ subprocess_pids_.emplace(pid);
+ }
+}
+
+void ColdBoot::DoRestoreCon() {
+ for (const char* path : kRegenerationPaths) {
+ selinux_android_restorecon(path, SELINUX_ANDROID_RESTORECON_RECURSE);
+ }
+ device_handler_.set_skip_restorecon(false);
+}
+
+void ColdBoot::WaitForSubProcesses() {
+ // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
+ // stuck.
+ //
+ // When a subprocess crashes, we fatally abort from ueventd. init will restart ueventd when
+ // init reaps it, and the cold boot process will start again. If this continues to fail, then
+ // since ueventd is marked as a critical service, init will reboot to recovery.
+ //
+ // When a subprocess gets stuck, keep ueventd spinning waiting for it. init has a timeout for
+ // cold boot and will reboot to the bootloader if ueventd does not complete in time.
+ while (!subprocess_pids_.empty()) {
+ int status;
+ pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0));
+ if (pid == -1) {
+ PLOG(ERROR) << "waitpid() failed";
+ continue;
+ }
+
+ auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);
+ if (it == subprocess_pids_.end()) continue;
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == EXIT_SUCCESS) {
+ subprocess_pids_.erase(it);
+ } else {
+ LOG(FATAL) << "subprocess exited with status " << WEXITSTATUS(status);
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(FATAL) << "subprocess killed by signal " << WTERMSIG(status);
+ }
+ }
+}
+
+void ColdBoot::Run() {
+ Timer cold_boot_timer;
+
+ RegenerateUevents();
+
+ ForkSubProcesses();
+
+ DoRestoreCon();
+
+ WaitForSubProcesses();
+
+ close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ LOG(INFO) << "Coldboot took " << cold_boot_timer;
+}
+
DeviceHandler CreateDeviceHandler() {
Parser parser;
@@ -64,11 +257,10 @@
parser.ParseConfig("/ueventd." + hardware + ".rc");
return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
- std::move(subsystems));
+ std::move(subsystems), true);
}
-int ueventd_main(int argc, char **argv)
-{
+int ueventd_main(int argc, char** argv) {
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
@@ -76,13 +268,6 @@
*/
umask(000);
- /* Prevent fire-and-forget children from becoming zombies.
- * If we should need to wait() for some children in the future
- * (as opposed to none right now), double-forking here instead
- * of ignoring SIGCHLD may be the better solution.
- */
- signal(SIGCHLD, SIG_IGN);
-
InitKernelLogging(argv);
LOG(INFO) << "ueventd started!";
@@ -95,16 +280,8 @@
UeventListener uevent_listener;
if (access(COLDBOOT_DONE, F_OK) != 0) {
- Timer t;
-
- uevent_listener.RegenerateUevents([&device_handler](const Uevent& uevent) {
- HandleFirmwareEvent(uevent);
- device_handler.HandleDeviceEvent(uevent);
- return RegenerationAction::kContinue;
- });
-
- close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
- LOG(INFO) << "Coldboot took " << t;
+ ColdBoot cold_boot(uevent_listener, device_handler);
+ cold_boot.Run();
}
uevent_listener.DoPolling([&device_handler](const Uevent& uevent) {
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7dd9227..a643a29 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -113,6 +113,7 @@
static_libs: ["libasync_safe", "libcutils"],
},
},
+ whole_static_libs: ["libdemangle"],
}
cc_library_shared {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3545661..e46d353 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -27,6 +27,8 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include <demangle.h>
+
#include "BacktraceLog.h"
#include "thread_utils.h"
#include "UnwindCurrent.h"
@@ -62,8 +64,7 @@
if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
return "";
}
- std::string func_name = GetFunctionNameRaw(pc, offset);
- return func_name;
+ return demangle(GetFunctionNameRaw(pc, offset).c_str());
}
bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index e29a844..7e38535 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -340,7 +340,7 @@
return 0;
}
-static void set_timerslack_ns(int tid, unsigned long long slack) {
+static void set_timerslack_ns(int tid, unsigned long slack) {
// v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
// TODO: once we've backported this, log if the open(2) fails.
if (__sys_supports_timerslack) {
@@ -348,7 +348,7 @@
snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
int fd = open(buf, O_WRONLY | O_CLOEXEC);
if (fd != -1) {
- int len = snprintf(buf, sizeof(buf), "%llu", slack);
+ int len = snprintf(buf, sizeof(buf), "%lu", slack);
if (write(fd, buf, len) != len) {
SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
}
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 057be5d..bb1ce34 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -177,6 +177,12 @@
return *this;
}
+ android_log_event_list& operator<<(bool value) {
+ int retval = android_log_write_int32(ctx, value ? 1 : 0);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
android_log_event_list& operator<<(int64_t value) {
int retval = android_log_write_int64(ctx, value);
if (retval < 0) ret = retval;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index e7c0beb..1c84744 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -502,7 +502,10 @@
std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
UnreachableMemoryInfo info;
if (!GetUnreachableMemory(info, limit)) {
- return "Failed to get unreachable memory\n";
+ return "Failed to get unreachable memory\n"
+ "If you are trying to get unreachable memory from a system app\n"
+ "(like com.android.systemui), disable selinux first using\n"
+ "setenforce 0\n";
}
return info.ToString(log_contents);
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index f28d726..1856e5c 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@
} else {
vprintf(fmt, argp);
}
+ va_end(argp);
}
/*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 3007b15..83a9740 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@
} else {
vprintf(fmt, argp);
}
+ va_end(argp);
}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 27b4065..f5d4e1c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -27,10 +27,12 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
#include <chrono>
#include <memory>
#include <mutex>
+#include <set>
#include <thread>
#include <android-base/logging.h>
@@ -258,6 +260,12 @@
return -errno;
}
+ // We separate all of the pids in the cgroup into those pids that are also the leaders of
+ // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+ std::set<pid_t> pgids;
+ pgids.emplace(initialPid);
+ std::set<pid_t> pids;
+
int ret;
pid_t pid;
int processes = 0;
@@ -269,8 +277,40 @@
LOG(WARNING) << "Yikes, we've been told to kill pid 0! How about we don't do that?";
continue;
}
+ pid_t pgid = getpgid(pid);
+ if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+ if (pgid == pid) {
+ pgids.emplace(pid);
+ } else {
+ pids.emplace(pid);
+ }
+ }
+
+ // Erase all pids that will be killed when we kill the process groups.
+ for (auto it = pids.begin(); it != pids.end();) {
+ pid_t pgid = getpgid(pid);
+ if (pgids.count(pgid) == 1) {
+ it = pids.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Kill all process groups.
+ for (const auto pgid : pgids) {
+ LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
+ << " as part of process cgroup " << initialPid;
+
+ if (kill(-pgid, signal) == -1) {
+ PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
+ }
+ }
+
+ // Kill remaining pids.
+ for (const auto pid : pids) {
LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
<< initialPid;
+
if (kill(pid, signal) == -1) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 98cd2c6..84af293 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -47,9 +47,11 @@
#ifndef _WIN32
if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
ALOGE("%s: Failed to format string", __FUNCTION__);
+ va_end(arglist);
return;
}
#else
+ va_end(arglist);
return;
#endif
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 287a99c..1084d59 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -52,17 +52,25 @@
],
}
-
cc_library {
name: "libziparchive",
host_supported: true,
- vendor_available:true,
+ vendor_available: true,
- defaults: ["libziparchive_defaults", "libziparchive_flags"],
- shared_libs: ["liblog", "libbase"],
+ defaults: [
+ "libziparchive_defaults",
+ "libziparchive_flags",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ ],
target: {
android: {
- shared_libs: ["libz", "libutils"],
+ shared_libs: [
+ "libz",
+ "libutils",
+ ],
},
host: {
static_libs: ["libutils"],
@@ -88,7 +96,10 @@
name: "libziparchive-host",
host_supported: true,
device_supported: false,
- defaults: ["libziparchive_defaults", "libziparchive_flags"],
+ defaults: [
+ "libziparchive_defaults",
+ "libziparchive_flags",
+ ],
shared_libs: ["libz-host"],
static_libs: ["libutils"],
}
@@ -150,3 +161,13 @@
},
},
}
+
+cc_binary {
+ name: "unzip",
+ defaults: ["libziparchive_flags"],
+ srcs: ["unzip.cpp"],
+ shared_libs: [
+ "libbase",
+ "libziparchive",
+ ],
+}
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
new file mode 100644
index 0000000..6756007
--- /dev/null
+++ b/libziparchive/unzip.cpp
@@ -0,0 +1,345 @@
+/*
+ * 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 <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.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>
+
+enum OverwriteMode {
+ kAlways,
+ kNever,
+ kPrompt,
+};
+
+static OverwriteMode overwrite_mode = kPrompt;
+static const char* flag_d = nullptr;
+static bool flag_l = false;
+static bool flag_p = false;
+static bool flag_q = false;
+static bool flag_v = 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 bool Filter(const std::string& name) {
+ if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
+ if (!includes.empty() && includes.find(name) == includes.end()) return true;
+ 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 int CompressionRatio(int64_t uncompressed, int64_t compressed) {
+ if (uncompressed == 0) return 0;
+ return (100LL * (uncompressed - compressed)) / uncompressed;
+}
+
+static void MaybeShowHeader() {
+ 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");
+ }
+}
+
+static void MaybeShowFooter() {
+ if (flag_v) {
+ printf(
+ "-------- ------- --- -------\n"
+ "%8" PRId64 " %8" PRId64 " %3d%% %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");
+ }
+}
+
+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) {
+ error(1, 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, ZipEntry& 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.
+ uint8_t* buffer = new uint8_t[entry.uncompressed_length];
+ int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+ if (err < 0) {
+ error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+ }
+ if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+ error(1, errno, "failed to write %s to stdout", name.c_str());
+ }
+ delete[] buffer;
+}
+
+static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ // Bad filename?
+ if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
+ name.find("/../") != std::string::npos) {
+ error(1, 0, "bad filename %s", name.c_str());
+ }
+
+ // Where are we actually extracting to (for human-readable output)?
+ std::string dst;
+ if (flag_d) {
+ dst = flag_d;
+ if (!android::base::EndsWith(dst, "/")) dst += '/';
+ }
+ dst += name;
+
+ // Ensure the directory hierarchy exists.
+ if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
+ error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+ }
+
+ // An entry in a zip file can just be a directory itself.
+ if (android::base::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;
+ }
+ error(1, 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) error(1, 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) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+ close(fd);
+}
+
+static void ListOne(const ZipEntry& 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("%8d %s %7d %3d%% %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("%9d %s %s\n", entry.uncompressed_length, time, name.c_str());
+ }
+}
+
+static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ 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);
+ }
+ }
+ total_uncompressed_length += entry.uncompressed_length;
+ total_compressed_length += entry.compressed_length;
+ ++file_count;
+}
+
+static void ProcessAll(ZipArchiveHandle zah) {
+ MaybeShowHeader();
+
+ // libziparchive iteration order doesn't match the central directory.
+ // We could sort, but that would cost extra and wouldn't match either.
+ void* cookie;
+ int err = StartIteration(zah, &cookie, nullptr, nullptr);
+ if (err != 0) {
+ error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+ }
+
+ ZipEntry entry;
+ ZipString string;
+ while ((err = Next(cookie, &entry, &string)) >= 0) {
+ std::string name(string.name, string.name + string.name_length);
+ if (!Filter(name)) ProcessOne(zah, entry, name);
+ }
+
+ if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+ EndIteration(cookie);
+
+ MaybeShowFooter();
+}
+
+static void ShowHelp(bool full) {
+ 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.\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");
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+ static struct option opts[] = {
+ {"help", no_argument, 0, 'h'},
+ };
+ bool saw_x = false;
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case 'd':
+ flag_d = optarg;
+ break;
+ case 'h':
+ ShowHelp(true);
+ 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;
+ case 'x':
+ saw_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 (saw_x) {
+ excludes.insert(optarg);
+ } else {
+ includes.insert(optarg);
+ }
+ break;
+ default:
+ ShowHelp(false);
+ }
+ }
+
+ if (!archive_name) error(1, 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) {
+ error(1, 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, but we
+ // require that the -d directory already exists.
+ if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+
+ ProcessAll(zah);
+
+ CloseArchive(zah);
+ return 0;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 246575f..a64b3b1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include <memory>
@@ -48,6 +49,10 @@
using android::base::get_unaligned;
+// 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 const bool kCrcChecksEnabled = false;
+
// This is for windows. If we don't open a file in binary mode, weird
// things will happen.
#ifndef O_BINARY
@@ -483,8 +488,7 @@
delete archive;
}
-static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
- ZipEntry *entry) {
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
return kIoError;
@@ -494,9 +498,17 @@
const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
- entry->crc32 = descriptor->crc32;
- entry->compressed_length = descriptor->compressed_size;
- entry->uncompressed_length = descriptor->uncompressed_size;
+ // 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 {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+ entry->compressed_length, entry->uncompressed_length, entry->crc32,
+ descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
+ return kInconsistentInformation;
+ }
return 0;
}
@@ -564,12 +576,22 @@
// Paranoia: Match the values specified in the local file header
// to those specified in the central directory.
- // Verify that the central directory and local file header agree on the use of a trailing
- // Data Descriptor.
+ // 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. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+ ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
cdr->gpb_flags, lfh->gpb_flags);
- return kInconsistentInformation;
}
// If there is no trailing data descriptor, verify that the central directory and local file
@@ -589,6 +611,13 @@
data->has_data_descriptor = 1;
}
+ // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+ if ((cdr->version_made_by >> 8) == 3) {
+ data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+ } else {
+ data->unix_mode = 0777;
+ }
+
// Check that the local file header name matches the declared
// name in the central directory.
if (lfh->file_name_length == nameLen) {
@@ -932,6 +961,7 @@
const uint32_t uncompressed_length = entry->uncompressed_length;
+ uint64_t crc = 0;
uint32_t compressed_length = entry->compressed_length;
do {
/* read as much as we can */
@@ -964,6 +994,8 @@
if (!writer->Append(&write_buf[0], write_size)) {
// The file might have declared a bogus length.
return kInconsistentInformation;
+ } else {
+ crc = crc32(crc, &write_buf[0], write_size);
}
zstream.next_out = &write_buf[0];
@@ -973,8 +1005,13 @@
assert(zerr == Z_STREAM_END); /* other errors should've been caught */
- // stream.adler holds the crc32 value for such streams.
- *crc_out = zstream.adler;
+ // 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.
+ *crc_out = crc;
if (zstream.total_out != uncompressed_length || compressed_length != 0) {
ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
@@ -1037,15 +1074,14 @@
}
if (!return_value && entry->has_data_descriptor) {
- return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
+ return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
if (return_value) {
return return_value;
}
}
- // TODO: Fix this check by passing the right flags to inflate2 so that
- // it calculates the CRC for us.
- if (entry->crc32 != crc && false) {
+ // 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;
}
@@ -1227,3 +1263,17 @@
}
return true;
}
+
+tm ZipEntry::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_common.h b/libziparchive/zip_archive_common.h
index ca42509..bc1ebb4 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -73,7 +73,7 @@
// The start of record signature. Must be |kSignature|.
uint32_t record_signature;
- // Tool version. Ignored by this implementation.
+ // Source tool version. Top byte gives source OS.
uint16_t version_made_by;
// Tool version. Ignored by this implementation.
uint16_t version_needed;
@@ -106,7 +106,7 @@
uint16_t file_start_disk;
// File attributes. Ignored by this implementation.
uint16_t internal_file_attributes;
- // File attributes. Ignored by this implementation.
+ // 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.
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9dd6cc0..42167dd 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -632,6 +632,96 @@
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);
+// ZipEntry ze = new ZipEntry("name");
+// ze.setMethod(ZipEntry.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));
+
+ // This function expects a variant of kDataDescriptorZipFile, for look for
+ // an entry whose name is "name" and whose size is 12 (contents =
+ // "abdcdefghijk").
+ ZipEntry entry;
+ ZipString empty_name;
+ SetZipString(&empty_name, "name");
+
+ ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(static_cast<uint32_t>(12), 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) {
+ 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_GT(0, error_code);
+ ASSERT_STREQ("Inconsistent information", ErrorCodeString(error_code));
+
+ std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+ invalid_csize[kSizeOffset] = 0xfe;
+
+ error_code = 0;
+ entry.clear();
+ ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+ ASSERT_GT(0, error_code);
+ ASSERT_STREQ("Inconsistent information", ErrorCodeString(error_code));
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5b526a4..9ad0252 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -135,17 +135,6 @@
CloseArchive(handle);
}
-static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
- memset(tm, 0, sizeof(struct tm));
- tm->tm_hour = (zip_time >> 11) & 0x1f;
- tm->tm_min = (zip_time >> 5) & 0x3f;
- tm->tm_sec = (zip_time & 0x1f) << 1;
-
- tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
- tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
- tm->tm_mday = (zip_time >> 16) & 0x1f;
-}
-
static struct tm MakeTm() {
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
@@ -177,8 +166,7 @@
ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
EXPECT_EQ(0, data.offset & 0x03);
- struct tm mod;
- ConvertZipTimeToTm(data.mod_time, &mod);
+ 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);
@@ -228,8 +216,7 @@
ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
EXPECT_EQ(0, data.offset & 0xfff);
- struct tm mod;
- ConvertZipTimeToTm(data.mod_time, &mod);
+ 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);
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index cd80212..4397b14 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -933,8 +933,12 @@
}
#ifdef __ANDROID__
-static inline int32_t get4LE(const char* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+static inline uint32_t get4LE(const uint8_t* src) {
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static inline uint32_t get4LE(const char* src) {
+ return get4LE(reinterpret_cast<const uint8_t*>(src));
}
#endif
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 4f4fc5d..6d35fed 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -3,6 +3,7 @@
required: [
"bzip2",
"grep",
+ "grep_vendor",
"gzip",
"mkshrc",
"mkshrc_vendor",
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 1c9fb20..8db8327 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -28,8 +28,8 @@
}
// We build BSD grep separately, so it can provide egrep and fgrep too.
-cc_binary {
- name: "grep",
+cc_defaults {
+ name: "grep_common",
srcs: [
"upstream-netbsd/usr.bin/grep/fastgrep.c",
"upstream-netbsd/usr.bin/grep/file.c",
@@ -40,5 +40,19 @@
cflags: common_cflags,
local_include_dirs: ["upstream-netbsd/include/"],
symlinks: ["egrep", "fgrep"],
+}
+cc_binary {
+ name: "grep",
+ defaults: ["grep_common"],
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+// when vendor_available is fully supported.
+cc_binary {
+ name: "grep_vendor",
+ stem: "grep",
+ vendor: true,
+ defaults: ["grep_common"],
}
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h
deleted file mode 100644
index 8fb5655..0000000
--- a/toolbox/upstream-netbsd/include/sys/mtio.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <linux/mtio.h>
diff --git a/tzdatacheck/Android.bp b/tzdatacheck/Android.bp
deleted file mode 100644
index 00ad141..0000000
--- a/tzdatacheck/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-// ========================================================
-// Executable
-// ========================================================
-cc_binary {
- name: "tzdatacheck",
- host_supported: true,
- srcs: ["tzdatacheck.cpp"],
- shared_libs: [
- "libbase",
- "libcutils",
- "liblog",
- ],
- cflags: ["-Werror"],
-}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
deleted file mode 100644
index 8fcd17f..0000000
--- a/tzdatacheck/tzdatacheck.cpp
+++ /dev/null
@@ -1,601 +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 <ctype.h>
-#include <errno.h>
-#include <ftw.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "android-base/logging.h"
-
-// The name of the directory that holds a staged time zone update distro. If this exists it should
-// replace the one in CURRENT_DIR_NAME.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* STAGED_DIR_NAME = "/staged";
-
-// The name of the directory that holds the (optional) installed time zone update distro.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* CURRENT_DIR_NAME = "/current";
-
-// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
-
-// The name of the file containing the distro version information.
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char* DISTRO_VERSION_FILENAME = "/distro_version";
-
-// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
-// AAA.BBB is the major/minor version of the distro format (e.g. 001.001),
-// CCCCC is the rules version (e.g. 2016g)
-// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001)
-// We only need the first 13 to determine if it is suitable for the device.
-static const int DISTRO_VERSION_LENGTH = 13;
-
-// The major version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001";
-
-// The length of the distro format major version excluding the \0
-static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
-
-// The minor version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
-
-// The length of the distro format minor version excluding the \0
-static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1;
-
-// The length of the distro format version. e.g. 001.001
-static const size_t SUPPORTED_DISTRO_VERSION_LEN =
- SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1;
-
-// The length of the IANA rules version bytes. e.g. 2016a
-static const size_t RULES_VERSION_LEN = 5;
-
-// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
-static const size_t DISTRO_VERSION_RULES_IDX = 8;
-
-// See also libcore.tzdata.shared2.TimeZoneDistro.
-static const char* TZDATA_FILENAME = "/tzdata";
-
-// tzdata file header (as much as we need for the version):
-// byte[11] tzdata_version -- e.g. "tzdata2012f"
-static const int TZ_HEADER_LENGTH = 11;
-
-static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
-static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
-
-static void usage() {
- std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
- "\n"
- "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n"
- "current Android release and better than or the same as base system timezone rules in\n"
- "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
- "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
- exit(1);
-}
-
-/*
- * Opens a file and fills buffer with the first byteCount bytes from the file.
- * If the file does not exist or cannot be opened or is too short then false is returned.
- * If the bytes were read successfully then true is returned.
- */
-static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
- FILE* file = fopen(fileName.c_str(), "r");
- if (file == nullptr) {
- if (errno != ENOENT) {
- PLOG(WARNING) << "Error opening file " << fileName;
- }
- return false;
- }
- size_t bytesRead = fread(buffer, 1, byteCount, file);
- fclose(file);
- if (bytesRead != byteCount) {
- LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
- return false;
- }
- return true;
-}
-
-/*
- * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
- * otherwise.
- */
-static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
- if (strncmp("tzdata", headerBytes, 6) != 0) {
- LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
- return false;
- }
- return true;
-}
-
-static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
- for (size_t j = 0; j < count; j++) {
- char toCheck = buffer[(*i)++];
- if (!isdigit(toCheck)) {
- return false;
- }
- }
- return true;
-}
-
-static bool checkValidDistroVersion(const char* buffer) {
- // See DISTRO_VERSION_LENGTH comments above for a description of the format.
- size_t i = 0;
- if (!checkDigits(buffer, 3, &i)) {
- return false;
- }
- if (buffer[i++] != '.') {
- return false;
- }
- if (!checkDigits(buffer, 3, &i)) {
- return false;
- }
- if (buffer[i++] != '|') {
- return false;
- }
- if (!checkDigits(buffer, 4, &i)) {
- return false;
- }
- // Ignore the last character. It is assumed to be a letter but we don't check because it's not
- // obvious what would happen at 'z'.
- return true;
-}
-
-/* Return the parent directory of dirName. */
-static std::string getParentDir(const std::string& dirName) {
- std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
- return dirname(mutable_dirname.get());
-}
-
-/* Deletes a single file, symlink or directory. Called from nftw(). */
-static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
- LOG(DEBUG) << "Inspecting " << fpath;
- switch (typeflag) {
- case FTW_F:
- case FTW_SL:
- LOG(DEBUG) << "Unlinking " << fpath;
- if (unlink(fpath)) {
- PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
- }
- break;
- case FTW_D:
- case FTW_DP:
- LOG(DEBUG) << "Removing dir " << fpath;
- if (rmdir(fpath)) {
- PLOG(WARNING) << "Failed to remove dir " << fpath;
- }
- break;
- default:
- LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
- break;
- }
- return 0;
-}
-
-enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
-
-static PathStatus checkPath(const std::string& path) {
- struct stat buf;
- if (stat(path.c_str(), &buf) != 0) {
- if (errno != ENOENT) {
- PLOG(WARNING) << "Unable to stat " << path;
- return ERR;
- }
- return NONE;
- }
- return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
-}
-
-/*
- * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
- * cannot be accessed this method returns false.
- */
-static bool deleteFile(const std::string& fileToDelete) {
- // Check whether the file exists.
- PathStatus pathStatus = checkPath(fileToDelete);
- if (pathStatus == NONE) {
- LOG(INFO) << "Path " << fileToDelete << " does not exist";
- return true;
- }
- if (pathStatus != IS_REG) {
- LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
- return false;
- }
-
- // Attempt the deletion.
- int rc = unlink(fileToDelete.c_str());
- if (rc != 0) {
- PLOG(WARNING) << "unlink() failed for " << fileToDelete;
- }
- return rc == 0;
-}
-
-/*
- * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
- * of the way. If dirToDelete does not exist this function does nothing and returns true. If
- * dirToDelete is not a directory or cannot be accessed this method returns false.
- *
- * During deletion, this function first renames the directory to a temporary name. If the temporary
- * directory cannot be created, or the directory cannot be renamed, false is returned. After the
- * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
- * basis. Symlinks beneath the directory are not followed.
- */
-static bool deleteDir(const std::string& dirToDelete) {
- // Check whether the dir exists.
- int pathStatus = checkPath(dirToDelete);
- if (pathStatus == NONE) {
- LOG(INFO) << "Path " << dirToDelete << " does not exist";
- return true;
- }
- if (pathStatus != IS_DIR) {
- LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
- return false;
- }
-
- // First, rename dirToDelete.
-
- std::string tempDirNameTemplate = getParentDir(dirToDelete);
- tempDirNameTemplate += "/tempXXXXXX";
-
- // Create an empty directory with the temporary name. For this we need a non-const char*.
- std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
- strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
- if (mkdtemp(&tempDirName[0]) == nullptr) {
- PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
- return false;
- }
-
- // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
- int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
- if (rc == -1) {
- PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
- << &tempDirName[0];
- return false;
- }
-
- // Recursively delete contents of tempDirName.
-
- rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
- FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
- if (rc == -1) {
- LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
- }
- return true;
-}
-
-/*
- * Deletes the ConfigInstaller metadata directory.
- * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
- */
-static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
- // Delete the update metadata
- std::string dataUpdatesDirName(dataZoneInfoDir);
- dataUpdatesDirName += "/updates";
- LOG(INFO) << "Removing: " << dataUpdatesDirName;
- if (!deleteDir(dataUpdatesDirName)) {
- LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
- << " was not successful";
- }
-}
-
-/*
- * Deletes the timezone update distro directory.
- */
-static void deleteUpdateDistroDir(const std::string& distroDirName) {
- LOG(INFO) << "Removing: " << distroDirName;
- if (!deleteDir(distroDirName)) {
- LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
- }
-}
-
-static void handleStagedUninstall(const std::string& dataStagedDirName,
- const std::string& dataCurrentDirName,
- const PathStatus dataCurrentDirStatus) {
- LOG(INFO) << "Staged operation is an uninstall.";
-
- // Delete the current install directory.
- switch (dataCurrentDirStatus) {
- case NONE:
- // This is unexpected: No uninstall should be staged if there is nothing to
- // uninstall. Carry on anyway.
- LOG(WARNING) << "No current install to delete.";
- break;
- case IS_DIR:
- // This is normal. Delete the current install dir.
- if (!deleteDir(dataCurrentDirName)) {
- LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
- << " was not successful";
- // If this happens we don't know whether we were able to delete or not. We don't
- // delete the staged operation so it will be retried next boot unless overridden.
- return;
- }
- break;
- case IS_REG:
- default:
- // This is unexpected: We can try to delete the unexpected file and carry on.
- LOG(WARNING) << "Current distro dir " << dataCurrentDirName
- << " is not actually a directory. Attempting deletion.";
- if (!deleteFile(dataCurrentDirName)) {
- LOG(WARNING) << "Could not delete " << dataCurrentDirName;
- return;
- }
- break;
- }
-
- // Delete the staged uninstall dir.
- if (!deleteDir(dataStagedDirName)) {
- LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
- << " was not successful";
- // If this happens we don't know whether we were able to delete the staged operation
- // or not.
- return;
- }
- LOG(INFO) << "Staged uninstall complete.";
-}
-
-static void handleStagedInstall(const std::string& dataStagedDirName,
- const std::string& dataCurrentDirName,
- const PathStatus dataCurrentDirStatus) {
- LOG(INFO) << "Staged operation is an install.";
-
- switch (dataCurrentDirStatus) {
- case NONE:
- // This is expected: This is the first install.
- LOG(INFO) << "No current install to replace.";
- break;
- case IS_DIR:
- // This is expected: We are replacing an existing install.
- // Delete the current dir so we can replace it.
- if (!deleteDir(dataCurrentDirName)) {
- LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
- << " was not successful";
- // If this happens, we cannot proceed.
- return;
- }
- break;
- case IS_REG:
- default:
- // This is unexpected: We can try to delete the unexpected file and carry on.
- LOG(WARNING) << "Current distro dir " << dataCurrentDirName
- << " is not actually a directory. Attempting deletion.";
- if (!deleteFile(dataCurrentDirName)) {
- LOG(WARNING) << "Could not delete " << dataCurrentDirName;
- return;
- }
- break;
- }
-
- // Move the staged dir so it is the new current dir, completing the install.
- LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
- int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
- if (rc == -1) {
- PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
- << &dataCurrentDirName[0];
- return;
- }
-
- LOG(INFO) << "Staged install complete.";
-}
-/*
- * Process a staged operation if there is one.
- */
-static void processStagedOperation(const std::string& dataStagedDirName,
- const std::string& dataCurrentDirName) {
- PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
-
- // Exit early for the common case.
- if (dataStagedDirStatus == NONE) {
- LOG(DEBUG) << "No staged time zone operation.";
- return;
- }
-
- // Check known directory names are in a good starting state.
- if (dataStagedDirStatus != IS_DIR) {
- LOG(WARNING) << "Staged distro dir " << dataStagedDirName
- << " could not be accessed or is not a directory."
- << " stagedDirStatus=" << dataStagedDirStatus;
- return;
- }
-
- // dataStagedDirStatus == IS_DIR.
-
- // Work out whether there is anything currently installed.
- PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
- if (dataCurrentDirStatus == ERR) {
- LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
- << " dataCurrentDirStatus=" << dataCurrentDirStatus;
- return;
- }
-
- // We must perform the staged operation.
-
- // Check to see if the staged directory contains an uninstall or an install operation.
- std::string uninstallTombStoneFile(dataStagedDirName);
- uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
- int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
- if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
- // Error case.
- LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
- return;
- }
- if (uninstallTombStoneFileStatus == IS_REG) {
- handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
- } else {
- // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
- handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
- }
-}
-
-/*
- * After a platform update it is likely that timezone data found on the system partition will be
- * newer than the version found in the data partition. This tool detects this case and removes the
- * version in /data.
- *
- * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
- * paths for the metadata and current timezone data must match.
- *
- * Typically on device the two args will be:
- * /system/usr/share/zoneinfo /data/misc/zoneinfo
- *
- * See usage() for usage notes.
- */
-int main(int argc, char* argv[]) {
- if (argc != 3) {
- usage();
- return 1;
- }
-
- const char* systemZoneInfoDir = argv[1];
- const char* dataZoneInfoDir = argv[2];
-
- std::string dataStagedDirName(dataZoneInfoDir);
- dataStagedDirName += STAGED_DIR_NAME;
-
- std::string dataCurrentDirName(dataZoneInfoDir);
- dataCurrentDirName += CURRENT_DIR_NAME;
-
- // Check for an process any staged operation.
- // If the staged operation could not be handled we still have to validate the current installed
- // directory so we do not check for errors and do not quit early.
- processStagedOperation(dataStagedDirName, dataCurrentDirName);
-
- // Check the distro directory exists. If it does not, exit quickly: nothing to do.
- PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
- if (dataCurrentDirStatus == NONE) {
- LOG(INFO) << "timezone distro dir " << dataCurrentDirName
- << " does not exist. No action required.";
- return 0;
- }
-
- // If the distro directory path is not a directory or we can't stat() the path, exit with a
- // warning: either there's a problem accessing storage or the world is not as it should be;
- // nothing to do.
- if (dataCurrentDirStatus != IS_DIR) {
- LOG(WARNING) << "Current distro dir " << dataCurrentDirName
- << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
- return 2;
- }
-
- // Check the installed distro version.
- std::string distroVersionFileName(dataCurrentDirName);
- distroVersionFileName += DISTRO_VERSION_FILENAME;
- std::vector<char> distroVersion;
- distroVersion.reserve(DISTRO_VERSION_LENGTH);
- bool distroVersionReadOk =
- readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH);
- if (!distroVersionReadOk) {
- LOG(WARNING) << "distro version file " << distroVersionFileName
- << " does not exist or is too short. Deleting distro dir.";
- // Implies the contents of the data partition is corrupt in some way. Try to clean up.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 3;
- }
-
- if (!checkValidDistroVersion(distroVersion.data())) {
- LOG(WARNING) << "distro version file " << distroVersionFileName
- << " is not valid. Deleting distro dir.";
- // Implies the contents of the data partition is corrupt in some way. Try to clean up.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 4;
- }
-
- std::string actualDistroVersion =
- std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN);
- // Check the first 3 bytes of the distro version: these are the major version (e.g. 001).
- // It must match the one we support exactly to be ok.
- if (strncmp(
- &distroVersion[0],
- SUPPORTED_DISTRO_MAJOR_VERSION,
- SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) {
-
- LOG(INFO) << "distro version file " << distroVersionFileName
- << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION
- << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
- // This implies there has been an OTA and the installed distro is not compatible with the
- // new version of Android. Remove the installed distro.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 5;
- }
-
- // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001).
- // If the version in the distro is < the minor version required by this device it cannot be
- // used.
- if (strncmp(
- &distroVersion[4],
- SUPPORTED_DISTRO_MINOR_VERSION,
- SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) {
-
- LOG(INFO) << "distro version file " << distroVersionFileName
- << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION
- << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
- // This implies there has been an OTA and the installed distro is not compatible with the
- // new version of Android. Remove the installed distro.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 5;
- }
-
- // Read the system rules version out of the /system tzdata file.
- std::string systemTzDataFileName(systemZoneInfoDir);
- systemTzDataFileName += TZDATA_FILENAME;
- std::vector<char> systemTzDataHeader;
- systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
- bool systemFileExists =
- readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
- if (!systemFileExists) {
- // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
- LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
- return 6;
- }
- if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
- // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
- LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
- return 7;
- }
-
- // Compare the distro rules version against the system rules version.
- if (strncmp(
- &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
- &distroVersion[DISTRO_VERSION_RULES_IDX],
- RULES_VERSION_LEN) <= 0) {
- LOG(INFO) << "Found an installed distro but it is valid. No action taken.";
- // Implies there is an installed update, but it is good.
- return 0;
- }
-
- // Implies there has been an OTA and the system version of the timezone rules is now newer
- // than the version installed in /data. Remove the installed distro.
- LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in "
- << systemTzDataFileName << "; fixing...";
-
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 0;
-}