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

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