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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « build/android/rezip/DEPS ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // rezip is a tool which is used to modify zip files. It reads in a zip
6 // 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.
7 // 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.
8 // (which are zip files). The main application of this is to modify the
9 // APK so that the shared library is no longer compressed. Ironically,
10 // this saves both transmission and device drive space. It saves
11 // transmission space because uncompressed libraries make much smaller
12 // deltas with previous versions. It saves device drive space because
13 // it is no longer necessary to have both a compressed and uncompressed
14 // shared library on the device. To achieve this the uncompressed library
15 // is opened directly from within the APK using the "crazy" linker.
16
17 #include <assert.h>
18 #include <string.h>
19
20 #include <iostream>
21 #include <sstream>
22 #include <string>
23
24 #include "third_party/zlib/contrib/minizip/unzip.h"
25 #include "third_party/zlib/contrib/minizip/zip.h"
26
27 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
28 const int kMaxExtraFieldInZip = 8192;
29 const int kBufferSize = 4096;
30 const int kPageSize = 4096;
31
32 // This is done to avoid having to make a dependency on all of base.
33 class LogStream {
34 public:
35 ~LogStream() {
36 stream_.flush();
37 std::cerr << stream_.str() << std::endl;
38 }
39 std::ostream& stream() {
40 return stream_;
41 }
42 private:
43 std::ostringstream stream_;
44 };
45
46 #define LOG(tag) (LogStream().stream() << #tag << ":")
47
48 // Copy the data from the currently opened file in the zipfile we are unzipping
49 // into the currently opened file of the zipfile we are zipping.
50 static bool CopySubfile(unzFile in_file,
51 zipFile out_file,
52 const char* in_zip_filename,
53 const char* out_zip_filename,
54 const char* in_filename,
55 const char* out_filename) {
56 char buf[kBufferSize];
57
58 int bytes = 0;
59 do {
60 bytes = unzReadCurrentFile(in_file, buf, sizeof(buf));
61 if (bytes == -1) {
agl 2014/06/11 17:52:40 unzReadCurrentFile returns negative numbers for er
Anton 2014/06/12 09:31:20 Done.
62 LOG(ERROR) << "failed to read from " << in_filename << " in zipfile "
63 << in_zip_filename;
64 return false;
65 }
66
67 if (bytes == 0) {
68 break;
69 }
70
71 if (ZIP_OK != zipWriteInFileInZip(out_file, buf, bytes)) {
72 LOG(ERROR) << "failed to write from " << out_filename << " in zipfile "
73 << out_zip_filename;
74 return false;
75 }
76 } 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)
77
78 return true;
79 }
80
81 static zip_fileinfo BuildOutInfo(const unz_file_info& in_info) {
82 zip_fileinfo out_info;
83 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
84 out_info.tmz_date.tm_min = in_info.tmu_date.tm_min;
85 out_info.tmz_date.tm_hour = in_info.tmu_date.tm_hour;
86 out_info.tmz_date.tm_mday = in_info.tmu_date.tm_mday;
87 out_info.tmz_date.tm_mon = in_info.tmu_date.tm_mon;
88 out_info.tmz_date.tm_year = in_info.tmu_date.tm_year;
89
90 out_info.dosDate = in_info.dosDate;
91 out_info.internal_fa = in_info.internal_fa;
92 out_info.external_fa = in_info.external_fa;
93 return out_info;
94 }
95
96 // RAII pattern for closing the unzip file.
97 class UnzipCloser {
98 public:
99 UnzipCloser(unzFile z_file, const char* z_filename)
100 : z_file_(z_file), z_filename_(z_filename) {}
101
102 ~UnzipCloser() {
103 if (unzClose(z_file_) != UNZ_OK) {
104 LOG(ERROR) << "failed to close input zipfile " << z_filename_;
105 exit(1);
106 }
107 }
108
109 private:
110 const char* z_filename_;
111 unzFile z_file_;
112 };
113
114 // RAII pattern for closing the out zip file.
115 class ZipCloser {
116 public:
117 ZipCloser(zipFile z_file, const char* z_filename)
118 : z_file_(z_file), z_filename_(z_filename) {}
119
120 ~ZipCloser() {
121 if (zipClose(z_file_, NULL) != ZIP_OK) {
122 LOG(ERROR) << "failed to close output zipfile" << z_filename_;
123 exit(1);
124 }
125 }
126
127 private:
128 const char* z_filename_;
129 zipFile z_file_;
130 };
131
132 typedef std::string (*RenameFun)(const char* in_filename);
133 typedef int (*AlignFun)(const char* in_filename,
134 unzFile in_file,
135 char* extra_buffer,
136 int size);
137 typedef bool (*InflateFun)(const char* filename);
138
139 static bool IsPrefixLibraryFilename(const char* filename,
140 const char* base_prefix) {
141 // We are basically matching "lib/[^/]*/<base_prefix>lib.*[.]so".
142 // However, we don't have C++11 regex, so we just handroll the test.
143 std::string filename_str = filename;
agl 2014/06/11 17:52:40 const for these?
Anton 2014/06/12 09:31:20 Done.
144 std::string prefix = "lib/";
145 std::string suffix = ".so";
146
147 if (filename_str.length() < suffix.length() + prefix.length()) {
148 // too short
149 return false;
150 }
151
152 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
153 // does not start with "lib/"
154 return false;
155 }
156
157 if (filename_str.compare(filename_str.length() - suffix.length(),
158 suffix.length(),
159 suffix) != 0) {
160 // does not end with ".so"
161 return false;
162 }
163
164 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.
165 if (last_slash < prefix.length()) {
166 // Only one slash
167 return false;
168 }
169
170 size_t second_slash = filename_str.find_first_of('/', prefix.length());
171 if (second_slash != last_slash) {
172 // filename_str contains more than two slashes.
173 return false;
174 }
175
176 std::string libprefix = std::string(base_prefix) + "lib";
177 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
178 0) {
179 // basename piece does not start with <base_prefix>"lib"
180 return false;
181 }
182
183 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
184 if (filename_str.compare(last_slash + 1, linker.length(), linker) == 0 &&
185 last_slash + 1 + linker.length() == filename_str.length()) {
186 // Do not match the linker.
187 return false;
188 }
189 return true;
190 }
191
192 static bool IsLibraryFilename(const char* filename) {
193 return IsPrefixLibraryFilename(filename, "");
194 }
195
196 static bool IsCrazyLibraryFilename(const char* filename) {
197 return IsPrefixLibraryFilename(filename, "crazy.");
198 }
199
200 static std::string RenameLibrary(const char* in_filename) {
201 if (!IsLibraryFilename(in_filename)) {
202 // Don't rename
203 return in_filename;
204 }
205
206 std::string filename_str = in_filename;
207 size_t last_slash = filename_str.find_last_of('/');
208 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
209 last_slash != filename_str.length() - 1) {
210 return in_filename;
211 }
212
213 // We rename the library, so that the Android Package Manager
214 // no longer extracts the library.
215 std::string basename_prefix = "crazy.";
216 return filename_str.substr(0, last_slash + 1) + basename_prefix +
217 filename_str.substr(last_slash + 1);
218 }
219
220 static int PageAlignCrazyLibrary(const char* in_filename,
221 unzFile in_file,
222 char* extra_buffer,
223 int size) {
224 if (!IsCrazyLibraryFilename(in_filename)) {
225 return size;
226 }
227 ZPOS64_T pos = unzGetCurrentFileZStreamPos64(in_file);
228 int padding = kPageSize - (pos % kPageSize);
229 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.
230 return size;
231 }
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
232
233 assert(padding + size < kMaxExtraFieldInZip);
234 memset(extra_buffer + size, 0, padding);
235 return size + padding;
236 }
237
238 // As only the read side API provides offsets, we check that we added the
239 // correct amount of padding by reading the zip file we just generated.
240 static bool CheckPageAlign(const char* out_zip_filename) {
241 unzFile in_file = unzOpen(out_zip_filename);
242 if (in_file == NULL) {
243 LOG(ERROR) << "failed to open zipfile " << out_zip_filename;
244 return false;
245 }
246 UnzipCloser unzipCloser(in_file, out_zip_filename);
247
248 int err = 0;
249 bool checked = false;
250 do {
251 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
252 // Get info and extra field for current file.
253 unz_file_info in_info;
254 err = unzGetCurrentFileInfo(in_file,
255 &in_info,
256 in_filename,
257 sizeof(in_filename) - 1,
258 NULL,
259 0,
260 NULL,
261 0);
262 if (err != UNZ_OK) {
263 LOG(ERROR) << "failed to get filename" << out_zip_filename;
264 return false;
265 }
266
267 if (IsCrazyLibraryFilename(in_filename)) {
268 err = unzOpenCurrentFile(in_file);
269 if (err != UNZ_OK) {
270 LOG(ERROR) << "failed to open subfile" << out_zip_filename << " "
271 << in_filename;
272 return false;
273 }
274
275 ZPOS64_T pos = unzGetCurrentFileZStreamPos64(in_file);
276 int alignment = pos % kPageSize;
277 checked = (alignment == 0);
278 if (!checked) {
279 LOG(ERROR) << "Failed to page align library " << in_filename
280 << ", position " << pos << " alignment " << alignment;
281 }
282
283 err = unzCloseCurrentFile(in_file);
284 if (err != UNZ_OK) {
285 LOG(ERROR) << "failed to close subfile" << out_zip_filename << " "
286 << in_filename;
287 return false;
288 }
289 }
290
291 int next = unzGoToNextFile(in_file);
292 if (next == UNZ_END_OF_LIST_OF_FILE) {
293 break;
294 }
295 if (next != UNZ_OK) {
296 LOG(ERROR) << "failed to go to next file" << out_zip_filename;
297 return false;
298 }
299 } 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)
300 return checked;
301 }
302
303 // Copy files from one archive to another applying alignment, rename and
304 // inflate transformations if given.
305 static bool Rezip(const char* in_zip_filename,
306 const char* out_zip_filename,
307 AlignFun align_fun,
308 RenameFun rename_fun,
309 InflateFun inflate_fun) {
310 unzFile in_file = unzOpen(in_zip_filename);
311 if (in_file == NULL) {
312 LOG(ERROR) << "failed to open zipfile " << in_zip_filename;
313 return false;
314 }
315 UnzipCloser unzipCloser(in_file, in_zip_filename);
316
317 zipFile out_file = zipOpen(out_zip_filename, APPEND_STATUS_CREATE);
318 if (unzGoToFirstFile(in_file) != UNZ_OK) {
319 LOG(ERROR) << "failed to go to first file in " << in_zip_filename;
320 return false;
321 }
322 ZipCloser zipCloser(out_file, out_zip_filename);
323
324 int err = 0;
325 do {
326 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.
327 // Get info and extra field for current file.
328 char extra_buffer[kMaxExtraFieldInZip];
329 unz_file_info in_info;
330 err = unzGetCurrentFileInfo(in_file,
331 &in_info,
332 in_filename,
333 sizeof(in_filename) - 1,
334 &extra_buffer,
335 sizeof(extra_buffer),
336 NULL,
337 0);
338 if (err != UNZ_OK) {
339 LOG(ERROR) << "failed to get filename " << in_zip_filename;
340 return false;
341 }
342
343 std::string out_filename = in_filename;
344 if (rename_fun != NULL) {
345 out_filename = rename_fun(in_filename);
346 } else {
347 out_filename = in_filename;
agl 2014/06/11 17:52:41 this else can be removed.
Anton 2014/06/12 09:31:20 Done.
348 }
349
350 bool inflate = false;
351 if (inflate_fun != NULL) {
352 inflate = inflate_fun(in_filename);
353 }
354
355 // Open the current file.
356 int method = 0;
357 int level = 0;
358 int raw = !inflate;
359 err = unzOpenCurrentFile2(in_file, &method, &level, raw);
360 if (inflate) {
361 method = Z_NO_COMPRESSION;
362 level = 0;
363 }
364
365 if (err != UNZ_OK) {
366 LOG(ERROR) << "failed to open subfile " << in_zip_filename << " "
367 << in_filename;
368 return false;
369 }
370
371 // Get the extra field from the local header.
372 char local_extra_buffer[kMaxExtraFieldInZip];
373 int local_extra_size = unzGetLocalExtrafield(
374 in_file, &local_extra_buffer, sizeof(local_extra_buffer));
375
376 if (align_fun != NULL) {
377 local_extra_size =
378 align_fun(in_filename, in_file, local_extra_buffer, local_extra_size);
379 }
380
381 const char* local_extra = local_extra_size > 0 ? local_extra_buffer : NULL;
382 const char* extra = in_info.size_file_extra > 0 ? extra_buffer : NULL;
383
384 // Build the output info structure from the input info structure.
385 zip_fileinfo out_info = BuildOutInfo(in_info);
386
387 int ret = zipOpenNewFileInZip4(out_file,
388 out_filename.c_str(),
389 &out_info,
390 local_extra,
391 local_extra_size,
392 extra,
393 in_info.size_file_extra,
394 /* comment */ NULL,
395 method,
396 level,
397 /* raw */ 1,
398 /* windowBits */ 0,
399 /* memLevel */ 0,
400 /* strategy */ 0,
401 /* password */ NULL,
402 /* crcForCrypting */ 0,
403 in_info.version,
404 /*flagBase */ 0);
405
406 if (ZIP_OK != ret) {
407 LOG(ERROR) << "failed to open subfile " << out_zip_filename << " "
408 << out_filename;
409 return false;
410 }
411
412 if (!CopySubfile(in_file,
413 out_file,
414 in_zip_filename,
415 out_zip_filename,
416 in_filename,
417 out_filename.c_str())) {
418 return false;
419 }
420
421 if (ZIP_OK != zipCloseFileInZipRaw(
422 out_file, in_info.uncompressed_size, in_info.crc)) {
423 LOG(ERROR) << "failed to close subfile " << out_zip_filename << " "
424 << out_filename;
425 return false;
426 }
427
428 err = unzCloseCurrentFile(in_file);
429 if (err != UNZ_OK) {
430 LOG(ERROR) << "failed to close subfile " << in_zip_filename << " "
431 << in_filename;
432 return false;
433 }
434 int next = unzGoToNextFile(in_file);
435 if (next == UNZ_END_OF_LIST_OF_FILE) {
436 break;
437 }
438 if (next != UNZ_OK) {
439 LOG(ERROR) << "failed to go to next file" << in_zip_filename;
440 return false;
441 }
442 } 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)
443
444 return true;
445 }
446
447 int main(int argc, const char* argv[]) {
448 if (argc != 4) {
449 LOG(ERROR) << "Usage: <action> <in_zipfile> <out_zipfile>";
450 LOG(ERROR) << " <action> is 'inflatealign', 'dropdescriptors' or 'rename'";
451 exit(1);
452 }
453
454 const char* action = argv[1];
455 const char* in_zip_filename = argv[2];
456 const char* out_zip_filename = argv[3];
457
458 InflateFun inflate_fun = NULL;
459 AlignFun align_fun = NULL;
460 RenameFun rename_fun = NULL;
461 bool checkPageAlign = false;
462 if (strcmp("inflatealign", action) == 0) {
463 inflate_fun = &IsCrazyLibraryFilename;
464 align_fun = &PageAlignCrazyLibrary;
465 checkPageAlign = true;
466 } else if (strcmp("rename", action) == 0) {
467 rename_fun = &RenameLibrary;
468 } else if (strcmp("dropdescriptors", action) == 0) {
469 // Minizip does not know about data descriptors, so the default
470 // copying action will drop the descriptors. This should be fine
471 // as data descriptors are complete redundant information.
agl 2014/06/11 17:52:40 s/complete//
Anton 2014/06/12 09:31:20 Done.
472 // Note we need to explicitly drop the descriptors before trying to
473 // do alignment otherwise we will miscalculate the position because
474 // we don't know about the data descriptors.
475 } else {
476 LOG(ERROR) << "Usage: <action> should be 'inflatealign', "
477 "'dropdescriptors' or 'rename'";
478 exit(1);
479 }
480
481 if (!Rezip(in_zip_filename,
482 out_zip_filename,
483 align_fun,
484 rename_fun,
485 inflate_fun)) {
486 exit(1);
487 }
488 if (checkPageAlign && !CheckPageAlign(out_zip_filename)) {
489 exit(1);
490 }
491 return 0;
492 }
OLDNEW
« 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