Merge "Fix race condition between restart and stop/reset."
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 7ed5b2b..121197c 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -51,6 +51,14 @@
ASSERT_EQ("bar", parts[2]);
}
+TEST(strings, split_with_trailing_empty_part) {
+ std::vector<std::string> parts = android::base::Split("foo,bar,", ",");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("", parts[2]);
+}
+
TEST(strings, split_null_char) {
std::vector<std::string> parts =
android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 209e81b..c1d5430 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -194,10 +194,12 @@
sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
then
+ sleep 1
break
fi
if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
then
+ sleep 1
break
fi
fi
@@ -239,7 +241,8 @@
[ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
-Returns true if current return (regex) value is true and the result matches" ]
+Returns true (0) if current return (regex) value is true and the result matches
+and the incoming return value is true as well (wired-or)" ]
EXPECT_PROPERTY() {
save_ret=${?}
property="${1}"
@@ -287,6 +290,7 @@
bootstat: Battery level at shutdown 100%
bootstat: Battery level at startup 100%
init : Parsing file /system/etc/init/bootstat.rc...
+init : Parsing file /system/etc/init/bootstat-debug.rc...
init : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc:
init : Command 'setprop ro.boot.bootreason \${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc:
init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
@@ -566,6 +570,8 @@
esac
adb reboot ${TEST}
wait_for_screen
+ bootloader_reason=`validate_property ro.boot.bootreason`
+ EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
EXPECT_PROPERTY sys.boot.reason ${reason}
EXPECT_PROPERTY persist.sys.boot.reason ${reason}
report_bootstat_logs ${reason}
@@ -623,8 +629,13 @@
adb reboot-bootloader
fi
fastboot format userdata >&2
+ save_ret=${?}
+ if [ 0 != ${save_ret} ]; then
+ echo "ERROR: fastboot can not format userdata" >&2
+ fi
fastboot reboot >&2
wait_for_screen
+ ( exit ${save_ret} ) # because one can not just do ?=${save_ret}
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,factory_reset bootloader \
@@ -868,17 +879,30 @@
- NB: should report reboot,its_just_so_hard
- NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
test_Its_Just_So_Hard_reboot() {
- duration_test
+ if isDebuggable; then # see below
+ duration_test
+ else
+ duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
+ fi
adb shell 'reboot "Its Just So Hard"'
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
- adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
- if checkDebugBuild; then
- flag=""
+ # Do not leave this test with an illegal value in persist.sys.boot.reason
+ save_ret=${?} # hold on to error code from above two lines
+ if isDebuggable; then # can do this easy, or we can do this hard.
+ adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
+ ( exit ${save_ret} ) # because one can not just do ?=${save_ret}
else
- flag="--allow_failure"
+ report_bootstat_logs reboot,its_just_so_hard # report what we have so far
+ # user build mitigation
+ adb shell reboot its_just_so_hard
+ wait_for_screen
+ ( exit ${save_ret} ) # because one can not just do ?=${save_ret}
+ EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
fi
+ # Ensure persist.sys.boot.reason now valid, failure here acts as a signal
+ # that we could choke up following tests. For example test_properties.
EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
report_bootstat_logs reboot,its_just_so_hard
}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 2270133..10753ce 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -223,6 +223,10 @@
{"2sec_reboot", 83},
{"reboot,by_key", 84},
{"reboot,longkey", 85},
+ {"reboot,2sec", 86},
+ {"shutdown,thermal,battery", 87},
+ {"reboot,its_just_so_hard", 88}, // produced by boot_reason_test
+ {"reboot,Its Just So Hard", 89}, // produced by boot_reason_test
};
// Converts a string value representing the reason the system booted to an
@@ -324,7 +328,105 @@
return android::base::ReadFileToString("/sys/fs/pstore/console-ramoops", &console);
}
-bool addKernelPanicSubReason(const std::string& console, std::string& ret) {
+// Implement a variant of std::string::rfind that is resilient to errors in
+// the data stream being inspected.
+class pstoreConsole {
+ private:
+ const size_t kBitErrorRate = 8; // number of bits per error
+ const std::string& console;
+
+ // Number of bits that differ between the two arguments l and r.
+ // Returns zero if the values for l and r are identical.
+ size_t numError(uint8_t l, uint8_t r) const { return std::bitset<8>(l ^ r).count(); }
+
+ // A string comparison function, reports the number of errors discovered
+ // in the match to a maximum of the bitLength / kBitErrorRate, at that
+ // point returning npos to indicate match is too poor.
+ //
+ // Since called in rfind which works backwards, expect cache locality will
+ // help if we check in reverse here as well for performance.
+ //
+ // Assumption: l (from console.c_str() + pos) is long enough to house
+ // _r.length(), checked in rfind caller below.
+ //
+ size_t numError(size_t pos, const std::string& _r) const {
+ const char* l = console.c_str() + pos;
+ const char* r = _r.c_str();
+ size_t n = _r.length();
+ const uint8_t* le = reinterpret_cast<const uint8_t*>(l) + n;
+ const uint8_t* re = reinterpret_cast<const uint8_t*>(r) + n;
+ size_t count = 0;
+ n = 0;
+ do {
+ // individual character bit error rate > threshold + slop
+ size_t num = numError(*--le, *--re);
+ if (num > ((8 + kBitErrorRate) / kBitErrorRate)) return std::string::npos;
+ // total bit error rate > threshold + slop
+ count += num;
+ ++n;
+ if (count > ((n * 8 + kBitErrorRate - (n > 2)) / kBitErrorRate)) {
+ return std::string::npos;
+ }
+ } while (le != reinterpret_cast<const uint8_t*>(l));
+ return count;
+ }
+
+ public:
+ explicit pstoreConsole(const std::string& console) : console(console) {}
+ // scope of argument must be equal to or greater than scope of pstoreConsole
+ explicit pstoreConsole(const std::string&& console) = delete;
+ explicit pstoreConsole(std::string&& console) = delete;
+
+ // Our implementation of rfind, use exact match first, then resort to fuzzy.
+ size_t rfind(const std::string& needle) const {
+ size_t pos = console.rfind(needle); // exact match?
+ if (pos != std::string::npos) return pos;
+
+ // Check to make sure needle fits in console string.
+ pos = console.length();
+ if (needle.length() > pos) return std::string::npos;
+ pos -= needle.length();
+ // fuzzy match to maximum kBitErrorRate
+ do {
+ if (numError(pos, needle) != std::string::npos) return pos;
+ } while (pos-- != 0);
+ return std::string::npos;
+ }
+
+ // Our implementation of find, use only fuzzy match.
+ size_t find(const std::string& needle, size_t start = 0) const {
+ // Check to make sure needle fits in console string.
+ if (needle.length() > console.length()) return std::string::npos;
+ const size_t last_pos = console.length() - needle.length();
+ // fuzzy match to maximum kBitErrorRate
+ for (size_t pos = start; pos <= last_pos; ++pos) {
+ if (numError(pos, needle) != std::string::npos) return pos;
+ }
+ return std::string::npos;
+ }
+};
+
+// If bit error match to needle, correct it.
+// Return true if any corrections were discovered and applied.
+bool correctForBer(std::string& reason, const std::string& needle) {
+ bool corrected = false;
+ if (reason.length() < needle.length()) return corrected;
+ const pstoreConsole console(reason);
+ const size_t last_pos = reason.length() - needle.length();
+ for (size_t pos = 0; pos <= last_pos; pos += needle.length()) {
+ pos = console.find(needle, pos);
+ if (pos == std::string::npos) break;
+
+ // exact match has no malice
+ if (needle == reason.substr(pos, needle.length())) continue;
+
+ corrected = true;
+ reason = reason.substr(0, pos) + needle + reason.substr(pos + needle.length());
+ }
+ return corrected;
+}
+
+bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {
// Check for kernel panic types to refine information
if (console.rfind("SysRq : Trigger a crash") != std::string::npos) {
// Can not happen, except on userdebug, during testing/debugging.
@@ -343,16 +445,28 @@
return false;
}
+bool addKernelPanicSubReason(const std::string& content, std::string& ret) {
+ return addKernelPanicSubReason(pstoreConsole(content), ret);
+}
+
// std::transform Helper callback functions:
// Converts a string value representing the reason the system booted to a
// string complying with Android system standard reason.
char tounderline(char c) {
return ::isblank(c) ? '_' : c;
}
+
char toprintable(char c) {
return ::isprint(c) ? c : '?';
}
+// Cleanup boot_reason regarding acceptable character set
+void transformReason(std::string& reason) {
+ std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+ std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
+ std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+}
+
const char system_reboot_reason_property[] = "sys.boot.reason";
const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
@@ -366,10 +480,7 @@
// If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
if (reason == ret) ret = "";
- // Cleanup boot_reason regarding acceptable character set
- std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
- std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
- std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+ transformReason(reason);
// Is the current system boot reason sys.boot.reason valid?
if (!isKnownRebootReason(ret)) ret = "";
@@ -406,6 +517,7 @@
{"shutdown,thermal", "thermal"},
{"warm,s3_wakeup", "s3_wakeup"},
{"hard,hw_reset", "hw_reset"},
+ {"reboot,2sec", "2sec_reboot"},
{"bootloader", ""},
};
@@ -441,9 +553,10 @@
// Check to see if last klog has some refinement hints.
std::string content;
if (readPstoreConsole(content)) {
+ const pstoreConsole console(content);
// The toybox reboot command used directly (unlikely)? But also
// catches init's response to Android's more controlled reboot command.
- if (content.rfind("reboot: Power down") != std::string::npos) {
+ if (console.rfind("reboot: Power down") != std::string::npos) {
ret = "shutdown"; // Still too blunt, but more accurate.
// ToDo: init should record the shutdown reason to kernel messages ala:
// init: shutdown system with command 'last_reboot_reason'
@@ -452,32 +565,46 @@
}
static const char cmd[] = "reboot: Restarting system with command '";
- size_t pos = content.rfind(cmd);
+ size_t pos = console.rfind(cmd);
if (pos != std::string::npos) {
pos += strlen(cmd);
std::string subReason(content.substr(pos, max_reason_length));
+ // Correct against any known strings that Bit Error Match
+ for (const auto& s : knownReasons) {
+ correctForBer(subReason, s);
+ }
+ for (const auto& m : kBootReasonMap) {
+ if (m.first.length() <= strlen("cold")) continue; // too short?
+ if (correctForBer(subReason, m.first + "'")) continue;
+ if (m.first.length() <= strlen("reboot,cold")) continue; // short?
+ if (!android::base::StartsWith(m.first, "reboot,")) continue;
+ correctForBer(subReason, m.first.substr(strlen("reboot,")) + "'");
+ }
for (pos = 0; pos < subReason.length(); ++pos) {
- char c = tounderline(subReason[pos]);
- if (!::isprint(c) || (c == '\'')) {
+ char c = subReason[pos];
+ // #, &, %, / are common single bit error for ' that we can block
+ if (!::isprint(c) || (c == '\'') || (c == '#') || (c == '&') || (c == '%') || (c == '/')) {
subReason.erase(pos);
break;
}
- subReason[pos] = ::tolower(c);
}
+ transformReason(subReason);
if (subReason != "") { // Will not land "reboot" as that is too blunt.
if (isKernelRebootReason(subReason)) {
ret = "reboot," + subReason; // User space can't talk kernel reasons.
- } else {
+ } else if (isKnownRebootReason(subReason)) {
ret = subReason;
+ } else {
+ ret = "reboot," + subReason; // legitimize unknown reasons
}
}
}
// Check for kernel panics, allowed to override reboot command.
- if (!addKernelPanicSubReason(content, ret) &&
+ if (!addKernelPanicSubReason(console, ret) &&
// check for long-press power down
- ((content.rfind("Power held for ") != std::string::npos) ||
- (content.rfind("charger: [") != std::string::npos))) {
+ ((console.rfind("Power held for ") != std::string::npos) ||
+ (console.rfind("charger: [") != std::string::npos))) {
ret = "cold";
}
}
@@ -493,14 +620,33 @@
// Really a hail-mary pass to find it in last klog content ...
static const int battery_dead_threshold = 2; // percent
static const char battery[] = "healthd: battery l=";
- size_t pos = content.rfind(battery); // last one
+ const pstoreConsole console(content);
+ size_t pos = console.rfind(battery); // last one
std::string digits;
if (pos != std::string::npos) {
- digits = content.substr(pos + strlen(battery));
+ digits = content.substr(pos + strlen(battery), strlen("100 "));
+ // correct common errors
+ correctForBer(digits, "100 ");
+ if (digits[0] == '!') digits[0] = '1';
+ if (digits[1] == '!') digits[1] = '1';
}
- char* endptr = NULL;
- unsigned long long level = strtoull(digits.c_str(), &endptr, 10);
- if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
+ const char* endptr = digits.c_str();
+ unsigned level = 0;
+ while (::isdigit(*endptr)) {
+ level *= 10;
+ level += *endptr++ - '0';
+ // make sure no leading zeros, except zero itself, and range check.
+ if ((level == 0) || (level > 100)) break;
+ }
+ // example bit error rate issues for 10%
+ // 'l=10 ' no bits in error
+ // 'l=00 ' single bit error (fails above)
+ // 'l=1 ' single bit error
+ // 'l=0 ' double bit error
+ // There are others, not typically critical because of 2%
+ // battery_dead_threshold. KISS check, make sure second
+ // character after digit sequence is not a space.
+ if ((level <= 100) && (endptr != digits.c_str()) && (endptr[0] == ' ') && (endptr[1] != ' ')) {
LOG(INFO) << "Battery level at shutdown " << level << "%";
if (level <= battery_dead_threshold) {
ret = "shutdown,battery";
@@ -540,10 +686,16 @@
pos = content.find(match); // The first one it finds.
if (pos != std::string::npos) {
- digits = content.substr(pos + strlen(match));
+ digits = content.substr(pos + strlen(match), strlen("100 "));
}
- endptr = NULL;
- level = strtoull(digits.c_str(), &endptr, 10);
+ endptr = digits.c_str();
+ level = 0;
+ while (::isdigit(*endptr)) {
+ level *= 10;
+ level += *endptr++ - '0';
+ // make sure no leading zeros, except zero itself, and range check.
+ if ((level == 0) || (level > 100)) break;
+ }
if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
LOG(INFO) << "Battery level at startup " << level << "%";
if (level <= battery_dead_threshold) {
@@ -560,10 +712,7 @@
// Content buffer no longer will have console data. Beware if more
// checks added below, that depend on parsing console content.
content = GetProperty(last_reboot_reason_property);
- // Cleanup last_boot_reason regarding acceptable character set
- std::transform(content.begin(), content.end(), content.begin(), ::tolower);
- std::transform(content.begin(), content.end(), content.begin(), tounderline);
- std::transform(content.begin(), content.end(), content.begin(), toprintable);
+ transformReason(content);
// Anything in last is better than 'super-blunt' reboot or shutdown.
if ((ret == "") || (ret == "reboot") || (ret == "shutdown") || !isBluntRebootReason(content)) {
diff --git a/init/service.cpp b/init/service.cpp
index 864aa2a..12acfc6 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -101,8 +101,22 @@
// It's OK to LOG(FATAL) in this function since it's running in the first
// child process.
- if (mount("", "/proc", "proc", kSafeFlags | MS_REMOUNT, "") == -1) {
- PLOG(FATAL) << "couldn't remount(/proc) for " << service_name;
+
+ // Recursively remount / as slave like zygote does so unmounting and mounting /proc
+ // doesn't interfere with the parent namespace's /proc mount. This will also
+ // prevent any other mounts/unmounts initiated by the service from interfering
+ // with the parent namespace but will still allow mount events from the parent
+ // namespace to propagate to the child.
+ if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
+ PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
+ }
+ // umount() then mount() /proc.
+ // Note that it is not sufficient to mount with MS_REMOUNT.
+ if (umount("/proc") == -1) {
+ PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
+ }
+ if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+ PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
}
if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
@@ -706,14 +720,20 @@
}
Result<Success> Service::Start() {
+ bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
- // on exit, unless they are ONESHOT.
+ // on exit, unless they are ONESHOT. For ONESHOT service, if it's in
+ // stopping status, we just set SVC_RESTART flag so it will get restarted
+ // in Reap().
if (flags_ & SVC_RUNNING) {
+ if ((flags_ & SVC_ONESHOT) && disabled) {
+ flags_ |= SVC_RESTART;
+ }
// It is not an error to try to start a service that is already running.
return Success();
}
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index c3f08c8..62e453f 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -64,7 +64,7 @@
auto frame = &unwinder_frames[i];
backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
- back_frame->num = frame->num;
+ back_frame->num = frame->num - num_ignore_frames;
back_frame->rel_pc = frame->rel_pc;
back_frame->pc = frame->pc;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index e5eb9e3..c3e5da0 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -1844,6 +1844,22 @@
UnwindThroughSignal(true, Backtrace::CreateNew, BacktraceMap::CreateNew);
}
+static void TestFrameSkipNumbering(create_func_t create_func, map_create_func_t map_create_func) {
+ std::unique_ptr<BacktraceMap> map(map_create_func(getpid(), false));
+ std::unique_ptr<Backtrace> backtrace(create_func(getpid(), gettid(), map.get()));
+ backtrace->Unwind(1);
+ ASSERT_NE(0U, backtrace->NumFrames());
+ ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
+}
+
+TEST(libbacktrace, unwind_frame_skip_numbering) {
+ TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
+}
+
+TEST(libbacktrace, unwind_frame_skip_numbering_new) {
+ TestFrameSkipNumbering(Backtrace::CreateNew, BacktraceMap::CreateNew);
+}
+
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index e3faee3..075fb86 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -86,19 +86,6 @@
},
}
-// Also provide libziparchive-host until everything is switched over to using libziparchive
-cc_library {
- name: "libziparchive-host",
- host_supported: true,
- device_supported: false,
- defaults: [
- "libziparchive_defaults",
- "libziparchive_flags",
- ],
- shared_libs: ["libz"],
- static_libs: ["libutils"],
-}
-
// Tests.
cc_test {
name: "ziparchive-tests",
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 73ae68d..dd463d1 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -230,4 +230,44 @@
ProcessZipEntryFunction func, void* cookie);
#endif
+namespace zip_archive {
+
+class Writer {
+ public:
+ virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+ virtual ~Writer();
+
+ protected:
+ Writer() = default;
+
+ private:
+ Writer(const Writer&) = delete;
+ void operator=(const Writer&) = delete;
+};
+
+class Reader {
+ public:
+ virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const = 0;
+ virtual ~Reader();
+
+ protected:
+ Reader() = default;
+
+ private:
+ Reader(const Reader&) = delete;
+ void operator=(const Reader&) = delete;
+};
+
+/*
+ * Inflates the first |compressed_length| bytes of |reader| to a given |writer|.
+ * |crc_out| is set to the CRC32 checksum of the uncompressed data.
+ *
+ * Returns 0 on success and negative values on failure, for example if |reader|
+ * cannot supply the right amount of data, or if the number of bytes written to
+ * data does not match |uncompressed_length|.
+ */
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+ const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
+} // namespace zip_archive
+
#endif // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index ad40d42..f27cd20 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -725,22 +725,10 @@
return kIterationEnd;
}
-class Writer {
- public:
- virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
- virtual ~Writer() {}
-
- protected:
- Writer() = default;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Writer);
-};
-
// A Writer that writes data to a fixed size memory region.
// The size of the memory region must be equal to the total size of
// the data appended to it.
-class MemoryWriter : public Writer {
+class MemoryWriter : public zip_archive::Writer {
public:
MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
@@ -764,7 +752,7 @@
// A Writer that appends data to a file |fd| at its current position.
// The file will be truncated to the end of the written data.
-class FileWriter : public Writer {
+class FileWriter : public zip_archive::Writer {
public:
// Creates a FileWriter for |fd| and prepare to write |entry| to it,
// guaranteeing that the file descriptor is valid and that there's enough
@@ -848,6 +836,22 @@
size_t total_bytes_written_;
};
+class EntryReader : public zip_archive::Reader {
+ public:
+ EntryReader(const MappedZipFile& zip_file, const ZipEntry* entry)
+ : Reader(), zip_file_(zip_file), entry_(entry) {}
+
+ virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
+ }
+
+ virtual ~EntryReader() {}
+
+ private:
+ const MappedZipFile& zip_file_;
+ const ZipEntry* entry_;
+};
+
// This method is using libz macros with old-style-casts
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -856,8 +860,14 @@
}
#pragma GCC diagnostic pop
-static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
- Writer* writer, uint64_t* crc_out) {
+namespace zip_archive {
+
+// Moved out of line to avoid -Wweak-vtables.
+Reader::~Reader() {}
+Writer::~Writer() {}
+
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+ const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
const size_t kBufSize = 32768;
std::vector<uint8_t> read_buf(kBufSize);
std::vector<uint8_t> write_buf(kBufSize);
@@ -898,25 +908,23 @@
std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
- const uint32_t uncompressed_length = entry->uncompressed_length;
-
uint64_t crc = 0;
- uint32_t compressed_length = entry->compressed_length;
+ uint32_t remaining_bytes = compressed_length;
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
- const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
- off64_t offset = entry->offset + (entry->compressed_length - compressed_length);
+ const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+ const uint32_t offset = (compressed_length - remaining_bytes);
// Make sure to read at offset to ensure concurrent access to the fd.
- if (!mapped_zip.ReadAtOffset(read_buf.data(), getSize, offset)) {
- ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
+ if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
+ ALOGW("Zip: inflate read failed, getSize = %zu: %s", read_size, strerror(errno));
return kIoError;
}
- compressed_length -= getSize;
+ remaining_bytes -= read_size;
zstream.next_in = &read_buf[0];
- zstream.avail_in = getSize;
+ zstream.avail_in = read_size;
}
/* uncompress the data */
@@ -952,7 +960,7 @@
// the same manner that we have above.
*crc_out = crc;
- if (zstream.total_out != uncompressed_length || compressed_length != 0) {
+ if (zstream.total_out != uncompressed_length || remaining_bytes != 0) {
ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
uncompressed_length);
return kInconsistentInformation;
@@ -960,9 +968,18 @@
return 0;
}
+} // namespace zip_archive
-static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
- uint64_t* crc_out) {
+static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+ zip_archive::Writer* writer, uint64_t* crc_out) {
+ const EntryReader reader(mapped_zip, entry);
+
+ return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
+ crc_out);
+}
+
+static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+ zip_archive::Writer* writer, uint64_t* crc_out) {
static const uint32_t kBufSize = 32768;
std::vector<uint8_t> buf(kBufSize);
@@ -995,7 +1012,7 @@
return 0;
}
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) {
+int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer) {
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
const uint16_t method = entry->method;
@@ -1025,12 +1042,12 @@
}
int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
- std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
+ std::unique_ptr<zip_archive::Writer> writer(new MemoryWriter(begin, size));
return ExtractToWriter(handle, entry, writer.get());
}
int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
- std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
+ std::unique_ptr<zip_archive::Writer> writer(FileWriter::Create(fd, entry));
if (writer.get() == nullptr) {
return kIoError;
}
@@ -1063,7 +1080,7 @@
}
#if !defined(_WIN32)
-class ProcessWriter : public Writer {
+class ProcessWriter : public zip_archive::Writer {
public:
ProcessWriter(ProcessZipEntryFunction func, void* cookie)
: Writer(), proc_function_(func), cookie_(cookie) {}
@@ -1118,7 +1135,7 @@
}
// Attempts to read |len| bytes into |buf| at offset |off|.
-bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
+bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
if (has_fd_) {
if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 174aa3f..18e0229 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -106,7 +106,7 @@
off64_t GetFileLength() const;
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
private:
// If has_fd_ is true, fd is valid and we'll read contents of a zip archive