Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Unified Diff: build/android/rezip/rezip.cc

Issue 333433002: Rezip tool used to modify the APK. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « build/android/rezip/DEPS ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/rezip/rezip.cc
diff --git a/build/android/rezip/rezip.cc b/build/android/rezip/rezip.cc
new file mode 100644
index 0000000000000000000000000000000000000000..49d5895da9aec86a489a3f325d0df0e6d9b69300
--- /dev/null
+++ b/build/android/rezip/rezip.cc
@@ -0,0 +1,492 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// rezip is a tool which is used to modify zip files. It reads in a zip
+// file and outputs a new zip file applying various transforms. The tool
agl 2014/06/11 17:52:40 "after" before "applying".
Anton 2014/06/12 09:31:19 Done.
+// is used in the Android Chromium build process to modify and APK file
agl 2014/06/11 17:52:41 s/and/an/
Anton 2014/06/12 09:31:20 Done.
+// (which are zip files). The main application of this is to modify the
+// APK so that the shared library is no longer compressed. Ironically,
+// this saves both transmission and device drive space. It saves
+// transmission space because uncompressed libraries make much smaller
+// deltas with previous versions. It saves device drive space because
+// it is no longer necessary to have both a compressed and uncompressed
+// shared library on the device. To achieve this the uncompressed library
+// is opened directly from within the APK using the "crazy" linker.
+
+#include <assert.h>
+#include <string.h>
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "third_party/zlib/contrib/minizip/unzip.h"
+#include "third_party/zlib/contrib/minizip/zip.h"
+
+const int kMaxFilenameInZip = 256;
agl 2014/06/11 17:52:40 static for these?
Anton 2014/06/12 09:31:19 Why? compile time constants are better.
agl 2014/06/17 17:40:37 static doesn't affect whether they are constant or
Anton 2014/06/18 09:44:27 And so does "const". It is in section 7.1.5.1.2 of
+const int kMaxExtraFieldInZip = 8192;
+const int kBufferSize = 4096;
+const int kPageSize = 4096;
+
+// This is done to avoid having to make a dependency on all of base.
+class LogStream {
+ public:
+ ~LogStream() {
+ stream_.flush();
+ std::cerr << stream_.str() << std::endl;
+ }
+ std::ostream& stream() {
+ return stream_;
+ }
+ private:
+ std::ostringstream stream_;
+};
+
+#define LOG(tag) (LogStream().stream() << #tag << ":")
+
+// Copy the data from the currently opened file in the zipfile we are unzipping
+// into the currently opened file of the zipfile we are zipping.
+static bool CopySubfile(unzFile in_file,
+ zipFile out_file,
+ const char* in_zip_filename,
+ const char* out_zip_filename,
+ const char* in_filename,
+ const char* out_filename) {
+ char buf[kBufferSize];
+
+ int bytes = 0;
+ do {
+ bytes = unzReadCurrentFile(in_file, buf, sizeof(buf));
+ if (bytes == -1) {
agl 2014/06/11 17:52:40 unzReadCurrentFile returns negative numbers for er
Anton 2014/06/12 09:31:20 Done.
+ LOG(ERROR) << "failed to read from " << in_filename << " in zipfile "
+ << in_zip_filename;
+ return false;
+ }
+
+ if (bytes == 0) {
+ break;
+ }
+
+ if (ZIP_OK != zipWriteInFileInZip(out_file, buf, bytes)) {
+ LOG(ERROR) << "failed to write from " << out_filename << " in zipfile "
+ << out_zip_filename;
+ return false;
+ }
+ } while (bytes != 0);
agl 2014/06/11 17:52:41 it looks like this do { } while loop should be a f
Anton 2014/06/12 09:31:20 Changed to while (true)
+
+ return true;
+}
+
+static zip_fileinfo BuildOutInfo(const unz_file_info& in_info) {
+ zip_fileinfo out_info;
+ out_info.tmz_date.tm_sec = in_info.tmu_date.tm_sec;
agl 2014/06/11 17:52:41 does assigning the whole struct as one not work?
Anton 2014/06/12 09:31:20 No, they are different types. tm_zip v. tm_unz
+ out_info.tmz_date.tm_min = in_info.tmu_date.tm_min;
+ out_info.tmz_date.tm_hour = in_info.tmu_date.tm_hour;
+ out_info.tmz_date.tm_mday = in_info.tmu_date.tm_mday;
+ out_info.tmz_date.tm_mon = in_info.tmu_date.tm_mon;
+ out_info.tmz_date.tm_year = in_info.tmu_date.tm_year;
+
+ out_info.dosDate = in_info.dosDate;
+ out_info.internal_fa = in_info.internal_fa;
+ out_info.external_fa = in_info.external_fa;
+ return out_info;
+}
+
+// RAII pattern for closing the unzip file.
+class UnzipCloser {
+ public:
+ UnzipCloser(unzFile z_file, const char* z_filename)
+ : z_file_(z_file), z_filename_(z_filename) {}
+
+ ~UnzipCloser() {
+ if (unzClose(z_file_) != UNZ_OK) {
+ LOG(ERROR) << "failed to close input zipfile " << z_filename_;
+ exit(1);
+ }
+ }
+
+ private:
+ const char* z_filename_;
+ unzFile z_file_;
+};
+
+// RAII pattern for closing the out zip file.
+class ZipCloser {
+ public:
+ ZipCloser(zipFile z_file, const char* z_filename)
+ : z_file_(z_file), z_filename_(z_filename) {}
+
+ ~ZipCloser() {
+ if (zipClose(z_file_, NULL) != ZIP_OK) {
+ LOG(ERROR) << "failed to close output zipfile" << z_filename_;
+ exit(1);
+ }
+ }
+
+ private:
+ const char* z_filename_;
+ zipFile z_file_;
+};
+
+typedef std::string (*RenameFun)(const char* in_filename);
+typedef int (*AlignFun)(const char* in_filename,
+ unzFile in_file,
+ char* extra_buffer,
+ int size);
+typedef bool (*InflateFun)(const char* filename);
+
+static bool IsPrefixLibraryFilename(const char* filename,
+ const char* base_prefix) {
+ // We are basically matching "lib/[^/]*/<base_prefix>lib.*[.]so".
+ // However, we don't have C++11 regex, so we just handroll the test.
+ std::string filename_str = filename;
agl 2014/06/11 17:52:40 const for these?
Anton 2014/06/12 09:31:20 Done.
+ std::string prefix = "lib/";
+ std::string suffix = ".so";
+
+ if (filename_str.length() < suffix.length() + prefix.length()) {
+ // too short
+ return false;
+ }
+
+ if (filename_str.substr(0, prefix.size()) != prefix) {
agl 2014/06/11 17:52:41 this test uses strsub and !=, but the next uses ju
Anton 2014/06/12 09:31:20 Changed to use compare
+ // does not start with "lib/"
+ return false;
+ }
+
+ if (filename_str.compare(filename_str.length() - suffix.length(),
+ suffix.length(),
+ suffix) != 0) {
+ // does not end with ".so"
+ return false;
+ }
+
+ size_t last_slash = filename_str.find_last_of('/');
agl 2014/06/11 17:52:41 these, and below, could be const if you like - not
Anton 2014/06/12 09:31:20 Done.
+ if (last_slash < prefix.length()) {
+ // Only one slash
+ return false;
+ }
+
+ size_t second_slash = filename_str.find_first_of('/', prefix.length());
+ if (second_slash != last_slash) {
+ // filename_str contains more than two slashes.
+ return false;
+ }
+
+ std::string libprefix = std::string(base_prefix) + "lib";
+ if (filename_str.compare(last_slash + 1, libprefix.length(), libprefix) !=
agl 2014/06/11 17:52:41 I think you need to check that libprefix isn't out
Anton 2014/06/12 09:31:19 compare goes until libprefix.length() or the end o
+ 0) {
+ // basename piece does not start with <base_prefix>"lib"
+ return false;
+ }
+
+ std::string linker = "libchromium_android_linker.so";
agl 2014/06/11 17:52:41 this isn't mentioned in the comment at the top of
Anton 2014/06/12 09:31:20 Done
+ if (filename_str.compare(last_slash + 1, linker.length(), linker) == 0 &&
+ last_slash + 1 + linker.length() == filename_str.length()) {
+ // Do not match the linker.
+ return false;
+ }
+ return true;
+}
+
+static bool IsLibraryFilename(const char* filename) {
+ return IsPrefixLibraryFilename(filename, "");
+}
+
+static bool IsCrazyLibraryFilename(const char* filename) {
+ return IsPrefixLibraryFilename(filename, "crazy.");
+}
+
+static std::string RenameLibrary(const char* in_filename) {
+ if (!IsLibraryFilename(in_filename)) {
+ // Don't rename
+ return in_filename;
+ }
+
+ std::string filename_str = in_filename;
+ size_t last_slash = filename_str.find_last_of('/');
+ if (last_slash == std::string::npos &&
agl 2014/06/11 17:52:41 if last_slash equals npos, I'm not sure that the s
Anton 2014/06/12 09:31:20 This is really just a fail safe, as IsLibraryFilen
+ last_slash != filename_str.length() - 1) {
+ return in_filename;
+ }
+
+ // We rename the library, so that the Android Package Manager
+ // no longer extracts the library.
+ std::string basename_prefix = "crazy.";
+ return filename_str.substr(0, last_slash + 1) + basename_prefix +
+ filename_str.substr(last_slash + 1);
+}
+
+static int PageAlignCrazyLibrary(const char* in_filename,
+ unzFile in_file,
+ char* extra_buffer,
+ int size) {
+ if (!IsCrazyLibraryFilename(in_filename)) {
+ return size;
+ }
+ ZPOS64_T pos = unzGetCurrentFileZStreamPos64(in_file);
+ int padding = kPageSize - (pos % kPageSize);
+ if (padding == 0) {
agl 2014/06/11 17:52:41 Can padding ever be zero? You might want to be che
Anton 2014/06/12 09:31:20 Done.
+ return size;
+ }
agl 2014/06/11 17:52:41 maybe if (INT_MAX - size < padding) { abort();
Anton 2014/06/12 09:31:20 Changed to: assert(extra_size < kMaxExtraFieldInZi
+
+ assert(padding + size < kMaxExtraFieldInZip);
+ memset(extra_buffer + size, 0, padding);
+ return size + padding;
+}
+
+// As only the read side API provides offsets, we check that we added the
+// correct amount of padding by reading the zip file we just generated.
+static bool CheckPageAlign(const char* out_zip_filename) {
+ unzFile in_file = unzOpen(out_zip_filename);
+ if (in_file == NULL) {
+ LOG(ERROR) << "failed to open zipfile " << out_zip_filename;
+ return false;
+ }
+ UnzipCloser unzipCloser(in_file, out_zip_filename);
+
+ int err = 0;
+ bool checked = false;
+ do {
+ char in_filename[kMaxFilenameInZip + 1];
agl 2014/06/11 17:52:41 you add one to the size as if you were trying to e
Anton 2014/06/12 09:31:20 This is based on the code in unzip.c which appears
+ // Get info and extra field for current file.
+ unz_file_info in_info;
+ err = unzGetCurrentFileInfo(in_file,
+ &in_info,
+ in_filename,
+ sizeof(in_filename) - 1,
+ NULL,
+ 0,
+ NULL,
+ 0);
+ if (err != UNZ_OK) {
+ LOG(ERROR) << "failed to get filename" << out_zip_filename;
+ return false;
+ }
+
+ if (IsCrazyLibraryFilename(in_filename)) {
+ err = unzOpenCurrentFile(in_file);
+ if (err != UNZ_OK) {
+ LOG(ERROR) << "failed to open subfile" << out_zip_filename << " "
+ << in_filename;
+ return false;
+ }
+
+ ZPOS64_T pos = unzGetCurrentFileZStreamPos64(in_file);
+ int alignment = pos % kPageSize;
+ checked = (alignment == 0);
+ if (!checked) {
+ LOG(ERROR) << "Failed to page align library " << in_filename
+ << ", position " << pos << " alignment " << alignment;
+ }
+
+ err = unzCloseCurrentFile(in_file);
+ if (err != UNZ_OK) {
+ LOG(ERROR) << "failed to close subfile" << out_zip_filename << " "
+ << in_filename;
+ return false;
+ }
+ }
+
+ int next = unzGoToNextFile(in_file);
+ if (next == UNZ_END_OF_LIST_OF_FILE) {
+ break;
+ }
+ if (next != UNZ_OK) {
+ LOG(ERROR) << "failed to go to next file" << out_zip_filename;
+ return false;
+ }
+ } while (true);
agl 2014/06/11 17:52:41 use for(;;) rather than do { } while (true).
Anton 2014/06/12 09:31:19 Changed to while (true)
+ return checked;
+}
+
+// Copy files from one archive to another applying alignment, rename and
+// inflate transformations if given.
+static bool Rezip(const char* in_zip_filename,
+ const char* out_zip_filename,
+ AlignFun align_fun,
+ RenameFun rename_fun,
+ InflateFun inflate_fun) {
+ unzFile in_file = unzOpen(in_zip_filename);
+ if (in_file == NULL) {
+ LOG(ERROR) << "failed to open zipfile " << in_zip_filename;
+ return false;
+ }
+ UnzipCloser unzipCloser(in_file, in_zip_filename);
+
+ zipFile out_file = zipOpen(out_zip_filename, APPEND_STATUS_CREATE);
+ if (unzGoToFirstFile(in_file) != UNZ_OK) {
+ LOG(ERROR) << "failed to go to first file in " << in_zip_filename;
+ return false;
+ }
+ ZipCloser zipCloser(out_file, out_zip_filename);
+
+ int err = 0;
+ do {
+ char in_filename[kMaxFilenameInZip + 1];
agl 2014/06/11 17:52:40 ditto about the NUL termination.
Anton 2014/06/12 09:31:20 Done.
+ // Get info and extra field for current file.
+ char extra_buffer[kMaxExtraFieldInZip];
+ unz_file_info in_info;
+ err = unzGetCurrentFileInfo(in_file,
+ &in_info,
+ in_filename,
+ sizeof(in_filename) - 1,
+ &extra_buffer,
+ sizeof(extra_buffer),
+ NULL,
+ 0);
+ if (err != UNZ_OK) {
+ LOG(ERROR) << "failed to get filename " << in_zip_filename;
+ return false;
+ }
+
+ std::string out_filename = in_filename;
+ if (rename_fun != NULL) {
+ out_filename = rename_fun(in_filename);
+ } else {
+ out_filename = in_filename;
agl 2014/06/11 17:52:41 this else can be removed.
Anton 2014/06/12 09:31:20 Done.
+ }
+
+ bool inflate = false;
+ if (inflate_fun != NULL) {
+ inflate = inflate_fun(in_filename);
+ }
+
+ // Open the current file.
+ int method = 0;
+ int level = 0;
+ int raw = !inflate;
+ err = unzOpenCurrentFile2(in_file, &method, &level, raw);
+ if (inflate) {
+ method = Z_NO_COMPRESSION;
+ level = 0;
+ }
+
+ if (err != UNZ_OK) {
+ LOG(ERROR) << "failed to open subfile " << in_zip_filename << " "
+ << in_filename;
+ return false;
+ }
+
+ // Get the extra field from the local header.
+ char local_extra_buffer[kMaxExtraFieldInZip];
+ int local_extra_size = unzGetLocalExtrafield(
+ in_file, &local_extra_buffer, sizeof(local_extra_buffer));
+
+ if (align_fun != NULL) {
+ local_extra_size =
+ align_fun(in_filename, in_file, local_extra_buffer, local_extra_size);
+ }
+
+ const char* local_extra = local_extra_size > 0 ? local_extra_buffer : NULL;
+ const char* extra = in_info.size_file_extra > 0 ? extra_buffer : NULL;
+
+ // Build the output info structure from the input info structure.
+ zip_fileinfo out_info = BuildOutInfo(in_info);
+
+ int ret = zipOpenNewFileInZip4(out_file,
+ out_filename.c_str(),
+ &out_info,
+ local_extra,
+ local_extra_size,
+ extra,
+ in_info.size_file_extra,
+ /* comment */ NULL,
+ method,
+ level,
+ /* raw */ 1,
+ /* windowBits */ 0,
+ /* memLevel */ 0,
+ /* strategy */ 0,
+ /* password */ NULL,
+ /* crcForCrypting */ 0,
+ in_info.version,
+ /*flagBase */ 0);
+
+ if (ZIP_OK != ret) {
+ LOG(ERROR) << "failed to open subfile " << out_zip_filename << " "
+ << out_filename;
+ return false;
+ }
+
+ if (!CopySubfile(in_file,
+ out_file,
+ in_zip_filename,
+ out_zip_filename,
+ in_filename,
+ out_filename.c_str())) {
+ return false;
+ }
+
+ if (ZIP_OK != zipCloseFileInZipRaw(
+ out_file, in_info.uncompressed_size, in_info.crc)) {
+ LOG(ERROR) << "failed to close subfile " << out_zip_filename << " "
+ << out_filename;
+ return false;
+ }
+
+ err = unzCloseCurrentFile(in_file);
+ if (err != UNZ_OK) {
+ LOG(ERROR) << "failed to close subfile " << in_zip_filename << " "
+ << in_filename;
+ return false;
+ }
+ int next = unzGoToNextFile(in_file);
+ if (next == UNZ_END_OF_LIST_OF_FILE) {
+ break;
+ }
+ if (next != UNZ_OK) {
+ LOG(ERROR) << "failed to go to next file" << in_zip_filename;
+ return false;
+ }
+ } while (true);
agl 2014/06/11 17:52:41 use for(;;) rather than do { } while(true).
Anton 2014/06/12 09:31:19 Changed to while (true)
+
+ return true;
+}
+
+int main(int argc, const char* argv[]) {
+ if (argc != 4) {
+ LOG(ERROR) << "Usage: <action> <in_zipfile> <out_zipfile>";
+ LOG(ERROR) << " <action> is 'inflatealign', 'dropdescriptors' or 'rename'";
+ exit(1);
+ }
+
+ const char* action = argv[1];
+ const char* in_zip_filename = argv[2];
+ const char* out_zip_filename = argv[3];
+
+ InflateFun inflate_fun = NULL;
+ AlignFun align_fun = NULL;
+ RenameFun rename_fun = NULL;
+ bool checkPageAlign = false;
+ if (strcmp("inflatealign", action) == 0) {
+ inflate_fun = &IsCrazyLibraryFilename;
+ align_fun = &PageAlignCrazyLibrary;
+ checkPageAlign = true;
+ } else if (strcmp("rename", action) == 0) {
+ rename_fun = &RenameLibrary;
+ } else if (strcmp("dropdescriptors", action) == 0) {
+ // Minizip does not know about data descriptors, so the default
+ // copying action will drop the descriptors. This should be fine
+ // as data descriptors are complete redundant information.
agl 2014/06/11 17:52:40 s/complete//
Anton 2014/06/12 09:31:20 Done.
+ // Note we need to explicitly drop the descriptors before trying to
+ // do alignment otherwise we will miscalculate the position because
+ // we don't know about the data descriptors.
+ } else {
+ LOG(ERROR) << "Usage: <action> should be 'inflatealign', "
+ "'dropdescriptors' or 'rename'";
+ exit(1);
+ }
+
+ if (!Rezip(in_zip_filename,
+ out_zip_filename,
+ align_fun,
+ rename_fun,
+ inflate_fun)) {
+ exit(1);
+ }
+ if (checkPageAlign && !CheckPageAlign(out_zip_filename)) {
+ exit(1);
+ }
+ return 0;
+}
« no previous file with comments | « build/android/rezip/DEPS ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698