• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2007 Alan Donovan. All rights reserved.
2 //
3 // Author: Alan Donovan <adonovan@google.com>
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //    http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 // zip.cc -- .zip (.jar) file reading/writing routines.
18 //
19 
20 // See README.txt for details.
21 //
22 // See http://www.pkware.com/documents/casestudies/APPNOTE.TXT
23 // for definition of PKZIP file format.
24 
25 #define _FILE_OFFSET_BITS 64  // Support zip files larger than 2GB
26 
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stddef.h>
30 #include <stdint.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/mman.h>
36 #include <unistd.h>
37 #include <limits.h>
38 #include <limits>
39 #include <vector>
40 
41 #include "zip.h"
42 #include <zlib.h>
43 
44 #define LOCAL_FILE_HEADER_SIGNATURE           0x04034b50
45 #define CENTRAL_FILE_HEADER_SIGNATURE         0x02014b50
46 #define END_OF_CENTRAL_DIR_SIGNATURE          0x06054b50
47 #define DATA_DESCRIPTOR_SIGNATURE             0x08074b50
48 
49 // version to extract: 1.0 - default value from APPNOTE.TXT.
50 // Output JAR files contain no extra ZIP features, so this is enough.
51 #define ZIP_VERSION_TO_EXTRACT                10
52 #define COMPRESSION_METHOD_STORED             0   // no compression
53 #define COMPRESSION_METHOD_DEFLATED           8
54 
55 #define GENERAL_PURPOSE_BIT_FLAG_COMPRESSED (1 << 3)
56 #define GENERAL_PURPOSE_BIT_FLAG_UTF8_ENCODED (1 << 11)
57 #define GENERAL_PURPOSE_BIT_FLAG_COMPRESSION_SPEED ((1 << 2) | (1 << 1))
58 #define GENERAL_PURPOSE_BIT_FLAG_SUPPORTED \
59   (GENERAL_PURPOSE_BIT_FLAG_COMPRESSED \
60   | GENERAL_PURPOSE_BIT_FLAG_UTF8_ENCODED \
61   | GENERAL_PURPOSE_BIT_FLAG_COMPRESSION_SPEED)
62 
63 namespace devtools_ijar {
64 // In the absence of ZIP64 support, zip files are limited to 4GB.
65 // http://www.info-zip.org/FAQ.html#limits
66 static const u8 kMaximumOutputSize = std::numeric_limits<uint32_t>::max();
67 
68 static bool ProcessCentralDirEntry(const u1 *&p,
69                                    size_t *compressed_size,
70                                    size_t *uncompressed_size,
71                                    char *filename,
72                                    size_t filename_size,
73                                    u4 *attr,
74                                    u4 *offset);
75 
76 //
77 // A class representing a ZipFile for reading. Its public API is exposed
78 // using the ZipExtractor abstract class.
79 //
80 class InputZipFile : public ZipExtractor {
81  public:
82   InputZipFile(ZipExtractorProcessor *processor, int fd, off_t in_length,
83                off_t in_offset, const u1* zipdata_in, const u1* central_dir);
84   virtual ~InputZipFile();
85 
GetError()86   virtual const char* GetError() {
87     if (errmsg[0] == 0) {
88       return NULL;
89     }
90     return errmsg;
91   }
92 
93   virtual bool ProcessNext();
94   virtual void Reset();
GetSize()95   virtual size_t GetSize() {
96     return in_length_;
97   }
98 
99   virtual u8 CalculateOutputLength();
100 
101  private:
102   ZipExtractorProcessor *processor;
103 
104   int fd_in;  // Input file descripor
105 
106   // InputZipFile is responsible for maintaining the following
107   // pointers. They are allocated by the Create() method before
108   // the object is actually created using mmap.
109   const u1 * const zipdata_in_;   // start of input file mmap
110   const u1 * zipdata_in_mapped_;  // start of still mapped region
111   const u1 * const central_dir_;  // central directory in input file
112 
113   size_t in_length_;  // size of the input file
114   size_t in_offset_;  // offset  the input file
115 
116   const u1 *p;  // input cursor
117 
118   const u1* central_dir_current_;  // central dir input cursor
119 
120   // Buffer size is initially INITIAL_BUFFER_SIZE. It doubles in size every
121   // time it is found too small, until it reaches MAX_BUFFER_SIZE. If that is
122   // not enough, we bail out. We only decompress class files, so they should
123   // be smaller than 64K anyway, but we give a little leeway.
124   // MAX_BUFFER_SIZE must be bigger than the size of the biggest file in the
125   // ZIP. It is set to 128M here so we can uncompress the Bazel server with
126   // this library.
127   static const size_t INITIAL_BUFFER_SIZE = 256 * 1024;  // 256K
128   static const size_t MAX_BUFFER_SIZE = 128 * 1024 * 1024;
129   static const size_t MAX_MAPPED_REGION = 32 * 1024 * 1024;
130 
131   // These metadata fields are the fields of the ZIP header of the file being
132   // processed.
133   u2 extract_version_;
134   u2 general_purpose_bit_flag_;
135   u2 compression_method_;
136   u4 uncompressed_size_;
137   u4 compressed_size_;
138   u2 file_name_length_;
139   u2 extra_field_length_;
140   const u1 *file_name_;
141   const u1 *extra_field_;
142 
143   // Administration of memory reserved for decompressed data. We use the same
144   // buffer for each file to avoid some malloc()/free() calls and free the
145   // memory only in the dtor. C-style memory management is used so that we
146   // can call realloc.
147   u1 *uncompressed_data_;
148   size_t uncompressed_data_allocated_;
149 
150   // Copy of the last filename entry - Null-terminated.
151   char filename[PATH_MAX];
152   // The external file attribute field
153   u4 attr;
154 
155   // last error
156   char errmsg[4*PATH_MAX];
157 
error(const char * fmt,...)158   int error(const char *fmt, ...) {
159     va_list ap;
160     va_start(ap, fmt);
161     vsnprintf(errmsg, 4*PATH_MAX, fmt, ap);
162     va_end(ap);
163     return -1;
164   }
165 
166   // Check that at least n bytes remain in the input file, otherwise
167   // abort with an error message.  "state" is the name of the field
168   // we're about to read, for diagnostics.
EnsureRemaining(size_t n,const char * state)169   int EnsureRemaining(size_t n, const char *state) {
170     size_t in_offset = p - zipdata_in_;
171     size_t remaining = in_length_ - in_offset;
172     if (n > remaining) {
173       return error("Premature end of file (at offset %zd, state=%s); "
174                    "expected %zd more bytes but found %zd.\n",
175                    in_offset, state, n, remaining);
176     }
177     return 0;
178   }
179 
180   // Read one entry from input zip file
181   int ProcessLocalFileEntry(size_t compressed_size, size_t uncompressed_size);
182 
183   // Uncompress a file from the archive using zlib. The pointer returned
184   // is owned by InputZipFile, so it must not be freed. Advances the input
185   // cursor to the first byte after the compressed data.
186   u1* UncompressFile();
187 
188   // Skip a file
189   int SkipFile(const bool compressed);
190 
191   // Process a file
192   int ProcessFile(const bool compressed);
193 };
194 
195 //
196 // A class implementing ZipBuilder that represent an open zip file for writing.
197 //
198 class OutputZipFile : public ZipBuilder {
199  public:
OutputZipFile(int fd,u1 * const zipdata_out)200   OutputZipFile(int fd, u1 * const zipdata_out) :
201       fd_out(fd),
202       zipdata_out_(zipdata_out),
203       q(zipdata_out) {
204     errmsg[0] = 0;
205   }
206 
GetError()207   virtual const char* GetError() {
208     if (errmsg[0] == 0) {
209       return NULL;
210     }
211     return errmsg;
212   }
213 
~OutputZipFile()214   virtual ~OutputZipFile() { Finish(); }
215   virtual u1* NewFile(const char* filename, const u4 attr);
216   virtual int FinishFile(size_t filelength, bool compress = false,
217                          bool compute_crc = false);
218   virtual int WriteEmptyFile(const char *filename);
GetSize()219   virtual size_t GetSize() {
220     return Offset(q);
221   }
GetNumberFiles()222   virtual int GetNumberFiles() {
223     return entries_.size();
224   }
225   virtual int Finish();
226 
227  private:
228   struct LocalFileEntry {
229     // Start of the local header (in the output buffer).
230     size_t local_header_offset;
231 
232     // Sizes of the file entry
233     size_t uncompressed_length;
234     size_t compressed_length;
235 
236     // Compression method
237     u2 compression_method;
238 
239     // CRC32
240     u4 crc32;
241 
242     // external attributes field
243     u4 external_attr;
244 
245     // Start/length of the file_name in the local header.
246     u1 *file_name;
247     u2 file_name_length;
248 
249     // Start/length of the extra_field in the local header.
250     const u1 *extra_field;
251     u2 extra_field_length;
252   };
253 
254   int fd_out;  // file descriptor for the output file
255 
256   // OutputZipFile is responsible for maintaining the following
257   // pointers. They are allocated by the Create() method before
258   // the object is actually created using mmap.
259   u1 * const zipdata_out_;        // start of output file mmap
260   u1 *q;  // output cursor
261 
262   u1 *header_ptr;  // Current pointer to "compression method" entry.
263 
264   // List of entries to write the central directory
265   std::vector<LocalFileEntry*> entries_;
266 
267   // last error
268   char errmsg[4*PATH_MAX];
269 
error(const char * fmt,...)270   int error(const char *fmt, ...) {
271     va_list ap;
272     va_start(ap, fmt);
273     vsnprintf(errmsg, 4*PATH_MAX, fmt, ap);
274     va_end(ap);
275     return -1;
276   }
277 
278   // Write the ZIP central directory structure for each local file
279   // entry in "entries".
280   void WriteCentralDirectory();
281 
282   // Returns the offset of the pointer relative to the start of the
283   // output zip file.
Offset(const u1 * const x)284   size_t Offset(const u1 *const x) {
285     return x - zipdata_out_;
286   }
287 
288   // Write ZIP file header in the output. Since the compressed size is not
289   // known in advance, it must be recorded later. This method returns a pointer
290   // to "compressed size" in the file header that should be passed to
291   // WriteFileSizeInLocalFileHeader() later.
292   u1* WriteLocalFileHeader(const char *filename, const u4 attr);
293 
294   // Fill in the "compressed size" and "uncompressed size" fields in a local
295   // file header previously written by WriteLocalFileHeader().
296   size_t WriteFileSizeInLocalFileHeader(u1 *header_ptr,
297                                         size_t out_length,
298                                         bool compress = false,
299                                         const u4 crc = 0);
300 };
301 
302 //
303 // Implementation of InputZipFile
304 //
ProcessNext()305 bool InputZipFile::ProcessNext() {
306   // Process the next entry in the central directory. Also make sure that the
307   // content pointer is in sync.
308   size_t compressed, uncompressed;
309   u4 offset;
310   if (!ProcessCentralDirEntry(central_dir_current_, &compressed, &uncompressed,
311                               filename, PATH_MAX, &attr, &offset)) {
312     return false;
313   }
314 
315   // There might be an offset specified in the central directory that does
316   // not match the file offset, if so, correct the pointer.
317   if (offset != 0 && (p != (zipdata_in_ + in_offset_ + offset))) {
318     p = zipdata_in_ + offset;
319   }
320 
321   if (EnsureRemaining(4, "signature") < 0) {
322     return false;
323   }
324   u4 signature = get_u4le(p);
325   if (signature == LOCAL_FILE_HEADER_SIGNATURE) {
326     if (ProcessLocalFileEntry(compressed, uncompressed) < 0) {
327       return false;
328     }
329   } else {
330     error("local file header signature for file %s not found\n", filename);
331     return false;
332   }
333 
334   return true;
335 }
336 
ProcessLocalFileEntry(size_t compressed_size,size_t uncompressed_size)337 int InputZipFile::ProcessLocalFileEntry(
338     size_t compressed_size, size_t uncompressed_size) {
339   if (EnsureRemaining(26, "extract_version") < 0) {
340     return -1;
341   }
342   extract_version_ = get_u2le(p);
343   general_purpose_bit_flag_ = get_u2le(p);
344 
345   if ((general_purpose_bit_flag_ & ~GENERAL_PURPOSE_BIT_FLAG_SUPPORTED) != 0) {
346     return error("Unsupported value (0x%04x) in general purpose bit flag.\n",
347                  general_purpose_bit_flag_);
348   }
349 
350   compression_method_ = get_u2le(p);
351 
352   if (compression_method_ != COMPRESSION_METHOD_DEFLATED &&
353       compression_method_ != COMPRESSION_METHOD_STORED) {
354     return error("Unsupported compression method (%d).\n",
355                  compression_method_);
356   }
357 
358   // skip over: last_mod_file_time, last_mod_file_date, crc32
359   p += 2 + 2 + 4;
360   compressed_size_ = get_u4le(p);
361   uncompressed_size_ = get_u4le(p);
362   file_name_length_ = get_u2le(p);
363   extra_field_length_ = get_u2le(p);
364 
365   if (EnsureRemaining(file_name_length_, "file_name") < 0) {
366     return -1;
367   }
368   file_name_ = p;
369   p += file_name_length_;
370 
371   if (EnsureRemaining(extra_field_length_, "extra_field") < 0) {
372     return -1;
373   }
374   extra_field_ = p;
375   p += extra_field_length_;
376 
377   bool is_compressed = compression_method_ == COMPRESSION_METHOD_DEFLATED;
378 
379   // If the zip is compressed, compressed and uncompressed size members are
380   // zero in the local file header. If not, check that they are the same as the
381   // lengths from the central directory, otherwise, just believe the central
382   // directory
383   if (compressed_size_ == 0) {
384     compressed_size_ = compressed_size;
385   } else {
386     if (compressed_size_ != compressed_size) {
387       return error("central directory and file header inconsistent\n");
388     }
389   }
390 
391   if (uncompressed_size_ == 0) {
392     uncompressed_size_ = uncompressed_size;
393   } else {
394     if (uncompressed_size_ != uncompressed_size) {
395       return error("central directory and file header inconsistent\n");
396     }
397   }
398 
399   if (processor->Accept(filename, attr)) {
400     if (ProcessFile(is_compressed) < 0) {
401       return -1;
402     }
403   } else {
404     if (SkipFile(is_compressed) < 0) {
405       return -1;
406     }
407   }
408 
409   if (general_purpose_bit_flag_ & GENERAL_PURPOSE_BIT_FLAG_COMPRESSED) {
410     // Skip the data descriptor. Some implementations do not put the signature
411     // here, so check if the next 4 bytes are a signature, and if so, skip the
412     // next 12 bytes (for CRC, compressed/uncompressed size), otherwise skip
413     // the next 8 bytes (because the value just read was the CRC).
414     u4 signature = get_u4le(p);
415     if (signature == DATA_DESCRIPTOR_SIGNATURE) {
416       p += 4 * 3;
417     } else {
418       p += 4 * 2;
419     }
420   }
421 
422   if (p > zipdata_in_mapped_ + MAX_MAPPED_REGION) {
423     munmap(const_cast<u1 *>(zipdata_in_mapped_), MAX_MAPPED_REGION);
424     zipdata_in_mapped_ += MAX_MAPPED_REGION;
425   }
426 
427   return 0;
428 }
429 
SkipFile(const bool compressed)430 int InputZipFile::SkipFile(const bool compressed) {
431   if (!compressed) {
432     // In this case, compressed_size_ == uncompressed_size_ (since the file is
433     // uncompressed), so we can use either.
434     if (compressed_size_ != uncompressed_size_) {
435       return error("compressed size != uncompressed size, although the file "
436                    "is uncompressed.\n");
437     }
438   }
439 
440   if (EnsureRemaining(compressed_size_, "file_data") < 0) {
441     return -1;
442   }
443   p += compressed_size_;
444   return 0;
445 }
446 
UncompressFile()447 u1* InputZipFile::UncompressFile() {
448   size_t in_offset = p - zipdata_in_;
449   size_t remaining = in_length_ - in_offset;
450   z_stream stream;
451 
452   stream.zalloc = Z_NULL;
453   stream.zfree = Z_NULL;
454   stream.opaque = Z_NULL;
455   stream.avail_in = remaining;
456   stream.next_in = (Bytef *) p;
457 
458   int ret = inflateInit2(&stream, -MAX_WBITS);
459   if (ret != Z_OK) {
460     error("inflateInit: %d\n", ret);
461     return NULL;
462   }
463 
464   int uncompressed_until_now = 0;
465 
466   while (true) {
467     stream.avail_out = uncompressed_data_allocated_ - uncompressed_until_now;
468     stream.next_out = uncompressed_data_ + uncompressed_until_now;
469     int old_avail_out = stream.avail_out;
470 
471     ret = inflate(&stream, Z_SYNC_FLUSH);
472     int uncompressed_now = old_avail_out - stream.avail_out;
473     uncompressed_until_now += uncompressed_now;
474 
475     switch (ret) {
476       case Z_STREAM_END: {
477         // zlib said that there is no more data to decompress.
478 
479         u1 *new_p = reinterpret_cast<u1*>(stream.next_in);
480         compressed_size_ = new_p - p;
481         uncompressed_size_ = uncompressed_until_now;
482         p = new_p;
483         inflateEnd(&stream);
484         return uncompressed_data_;
485       }
486 
487       case Z_OK: {
488         // zlib said that there is no more room in the buffer allocated for
489         // the decompressed data. Enlarge that buffer and try again.
490 
491         if (uncompressed_data_allocated_ == MAX_BUFFER_SIZE) {
492           error("ijar does not support decompressing files "
493                 "larger than %dMB.\n",
494                 (int) (MAX_BUFFER_SIZE/(1024*1024)));
495           return NULL;
496         }
497 
498         uncompressed_data_allocated_ *= 2;
499         if (uncompressed_data_allocated_ > MAX_BUFFER_SIZE) {
500           uncompressed_data_allocated_ = MAX_BUFFER_SIZE;
501         }
502 
503         uncompressed_data_ = reinterpret_cast<u1*>(
504             realloc(uncompressed_data_, uncompressed_data_allocated_));
505         break;
506       }
507 
508       case Z_DATA_ERROR:
509       case Z_BUF_ERROR:
510       case Z_STREAM_ERROR:
511       case Z_NEED_DICT:
512       default: {
513         error("zlib returned error code %d during inflate.\n", ret);
514         return NULL;
515       }
516     }
517   }
518 }
519 
ProcessFile(const bool compressed)520 int InputZipFile::ProcessFile(const bool compressed) {
521   const u1 *file_data;
522   if (compressed) {
523     file_data = UncompressFile();
524     if (file_data == NULL) {
525       return -1;
526     }
527   } else {
528     // In this case, compressed_size_ == uncompressed_size_ (since the file is
529     // uncompressed), so we can use either.
530     if (compressed_size_ != uncompressed_size_) {
531       return error("compressed size != uncompressed size, although the file "
532                    "is uncompressed.\n");
533     }
534 
535     if (EnsureRemaining(compressed_size_, "file_data") < 0) {
536       return -1;
537     }
538     file_data = p;
539     p += compressed_size_;
540   }
541   processor->Process(filename, attr, file_data, uncompressed_size_);
542   return 0;
543 }
544 
545 
546 // Reads and returns some metadata of the next file from the central directory:
547 // - compressed size
548 // - uncompressed size
549 // - whether the entry is a class file (to be included in the output).
550 // Precondition: p points to the beginning of an entry in the central dir
551 // Postcondition: p points to the beginning of the next entry in the central dir
552 // Returns true if the central directory contains another file and false if not.
553 // Of course, in the latter case, the size output variables are not changed.
554 // Note that the central directory is always followed by another data structure
555 // that has a signature, so parsing it this way is safe.
ProcessCentralDirEntry(const u1 * & p,size_t * compressed_size,size_t * uncompressed_size,char * filename,size_t filename_size,u4 * attr,u4 * offset)556 static bool ProcessCentralDirEntry(
557     const u1 *&p, size_t *compressed_size, size_t *uncompressed_size,
558     char *filename, size_t filename_size, u4 *attr, u4 *offset) {
559   u4 signature = get_u4le(p);
560   if (signature != CENTRAL_FILE_HEADER_SIGNATURE) {
561     return false;
562   }
563 
564   p += 16;  // skip to 'compressed size' field
565   *compressed_size = get_u4le(p);
566   *uncompressed_size = get_u4le(p);
567   u2 file_name_length = get_u2le(p);
568   u2 extra_field_length = get_u2le(p);
569   u2 file_comment_length = get_u2le(p);
570   p += 4;  // skip to external file attributes field
571   *attr = get_u4le(p);
572   *offset = get_u4le(p);
573   {
574     size_t len = (file_name_length < filename_size)
575       ? file_name_length
576       : (filename_size - 1);
577     memcpy(reinterpret_cast<void*>(filename), p, len);
578     filename[len] = 0;
579   }
580   p += file_name_length;
581   p += extra_field_length;
582   p += file_comment_length;
583   return true;
584 }
585 
586 // Gives a maximum bound on the size of the interface JAR. Basically, adds
587 // the difference between the compressed and uncompressed sizes to the size
588 // of the input file.
CalculateOutputLength()589 u8 InputZipFile::CalculateOutputLength() {
590   const u1* current = central_dir_;
591 
592   u8 compressed_size = 0;
593   u8 uncompressed_size = 0;
594   u8 skipped_compressed_size = 0;
595   u4 attr;
596   u4 offset;
597   char filename[PATH_MAX];
598 
599   while (true) {
600     size_t file_compressed, file_uncompressed;
601     if (!ProcessCentralDirEntry(current,
602                                 &file_compressed, &file_uncompressed,
603                                 filename, PATH_MAX, &attr, &offset)) {
604       break;
605     }
606 
607     if (processor->Accept(filename, attr)) {
608       compressed_size += (u8) file_compressed;
609       uncompressed_size += (u8) file_uncompressed;
610     } else {
611       skipped_compressed_size += file_compressed;
612     }
613   }
614 
615   // The worst case is when the output is simply the input uncompressed. The
616   // metadata in the zip file will stay the same, so the file will grow by the
617   // difference between the compressed and uncompressed sizes.
618   return (u8) in_length_ - skipped_compressed_size
619       + (uncompressed_size - compressed_size);
620 }
621 
622 // Given the data in the zip file, returns the offset of the central directory
623 // and the number of files contained in it.
FindZipCentralDirectory(const u1 * bytes,size_t in_length,u4 * offset,const u1 ** central_dir)624 bool FindZipCentralDirectory(const u1* bytes, size_t in_length,
625                              u4* offset, const u1** central_dir) {
626   static const int MAX_COMMENT_LENGTH = 0xffff;
627   static const int CENTRAL_DIR_LOCATOR_SIZE = 22;
628   // Maximum distance of start of central dir locator from end of file
629   static const int MAX_DELTA = MAX_COMMENT_LENGTH + CENTRAL_DIR_LOCATOR_SIZE;
630   const u1* last_pos_to_check = in_length < MAX_DELTA
631       ? bytes
632       : bytes + (in_length - MAX_DELTA);
633   const u1* current;
634   bool found = false;
635 
636   for (current = bytes + in_length - CENTRAL_DIR_LOCATOR_SIZE;
637        current >= last_pos_to_check;
638        current-- ) {
639     const u1* p = current;
640     if (get_u4le(p) != END_OF_CENTRAL_DIR_SIGNATURE) {
641       continue;
642     }
643 
644     p += 16;  // skip to comment length field
645     u2 comment_length = get_u2le(p);
646 
647     // Does the comment go exactly till the end of the file?
648     if (current + comment_length + CENTRAL_DIR_LOCATOR_SIZE
649         != bytes + in_length) {
650       continue;
651     }
652 
653     // Hooray, we found it!
654     found = true;
655     break;
656   }
657 
658   if (!found) {
659     fprintf(stderr, "file is invalid or corrupted (missing end of central "
660                     "directory record)\n");
661     return false;
662   }
663 
664   const u1* end_of_central_dir = current;
665   get_u4le(current);  // central directory locator signature, already checked
666   u2 number_of_this_disk = get_u2le(current);
667   u2 disk_with_central_dir = get_u2le(current);
668   u2 central_dir_entries_on_this_disk = get_u2le(current);
669   u2 central_dir_entries = get_u2le(current);
670   u4 central_dir_size = get_u4le(current);
671   u4 central_dir_offset = get_u4le(current);
672   u2 file_comment_length = get_u2le(current);
673   current += file_comment_length;  // set current to the end of the central dir
674 
675   if (number_of_this_disk != 0
676     || disk_with_central_dir != 0
677     || central_dir_entries_on_this_disk != central_dir_entries) {
678     fprintf(stderr, "multi-disk JAR files are not supported\n");
679     return false;
680   }
681 
682   // Do not change output values before determining that they are OK.
683   *offset = central_dir_offset;
684   // Central directory start can then be used to determine the actual
685   // starts of the zip file (which can be different in case of a non-zip
686   // header like for auto-extractable binaries).
687   *central_dir = end_of_central_dir - central_dir_size;
688   return true;
689 }
690 
Reset()691 void InputZipFile::Reset() {
692   central_dir_current_ = central_dir_;
693   zipdata_in_mapped_ = zipdata_in_;
694   p = zipdata_in_ + in_offset_;
695 }
696 
ProcessAll()697 int ZipExtractor::ProcessAll() {
698   while (ProcessNext()) {}
699   if (GetError() != NULL) {
700     return -1;
701   }
702   return 0;
703 }
704 
Create(const char * filename,ZipExtractorProcessor * processor)705 ZipExtractor* ZipExtractor::Create(const char* filename,
706                                    ZipExtractorProcessor *processor) {
707   int fd_in = open(filename, O_RDONLY);
708   if (fd_in < 0) {
709     return NULL;
710   }
711 
712   off_t length = lseek(fd_in, 0, SEEK_END);
713   if (length < 0) {
714     return NULL;
715   }
716 
717   void *zipdata_in = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd_in, 0);
718   if (zipdata_in == MAP_FAILED) {
719     return NULL;
720   }
721 
722   u4 central_dir_offset;
723   const u1 *central_dir = NULL;
724 
725   if (!devtools_ijar::FindZipCentralDirectory(
726           static_cast<const u1*>(zipdata_in), length,
727           &central_dir_offset, &central_dir)) {
728     errno = EIO;  // we don't really have a good error number
729     return NULL;
730   }
731   const u1 *zipdata_start = static_cast<const u1*>(zipdata_in);
732   off_t offset = - static_cast<off_t>(zipdata_start
733                                       + central_dir_offset
734                                       - central_dir);
735 
736   return new InputZipFile(processor, fd_in, length, offset,
737                           zipdata_start, central_dir);
738 }
739 
InputZipFile(ZipExtractorProcessor * processor,int fd,off_t in_length,off_t in_offset,const u1 * zipdata_in,const u1 * central_dir)740 InputZipFile::InputZipFile(ZipExtractorProcessor *processor, int fd,
741                            off_t in_length, off_t in_offset,
742                            const u1* zipdata_in, const u1* central_dir)
743   : processor(processor), fd_in(fd),
744     zipdata_in_(zipdata_in), zipdata_in_mapped_(zipdata_in),
745     central_dir_(central_dir), in_length_(in_length), in_offset_(in_offset),
746     p(zipdata_in + in_offset), central_dir_current_(central_dir) {
747   uncompressed_data_allocated_ = INITIAL_BUFFER_SIZE;
748   uncompressed_data_ =
749     reinterpret_cast<u1*>(malloc(uncompressed_data_allocated_));
750   errmsg[0] = 0;
751 }
752 
~InputZipFile()753 InputZipFile::~InputZipFile() {
754   free(uncompressed_data_);
755   close(fd_in);
756 }
757 
758 
759 //
760 // Implementation of OutputZipFile
761 //
WriteEmptyFile(const char * filename)762 int OutputZipFile::WriteEmptyFile(const char *filename) {
763   const u1* file_name = (const u1*) filename;
764   size_t file_name_length = strlen(filename);
765 
766   LocalFileEntry *entry = new LocalFileEntry;
767   entry->local_header_offset = Offset(q);
768   entry->external_attr = 0;
769   entry->crc32 = 0;
770 
771   // Output the ZIP local_file_header:
772   put_u4le(q, LOCAL_FILE_HEADER_SIGNATURE);
773   put_u2le(q, 10);  // extract_version
774   put_u2le(q, 0);  // general_purpose_bit_flag
775   put_u2le(q, 0);  // compression_method
776   put_u2le(q, 0);  // last_mod_file_time
777   put_u2le(q, 0);  // last_mod_file_date
778   put_u4le(q, entry->crc32);  // crc32
779   put_u4le(q, 0);  // compressed_size
780   put_u4le(q, 0);  // uncompressed_size
781   put_u2le(q, file_name_length);
782   put_u2le(q, 0);  // extra_field_length
783   put_n(q, file_name, file_name_length);
784 
785   entry->file_name_length = file_name_length;
786   entry->extra_field_length = 0;
787   entry->compressed_length = 0;
788   entry->uncompressed_length = 0;
789   entry->compression_method = 0;
790   entry->extra_field = (const u1 *)"";
791   entry->file_name = (u1*) strdup((const char *) file_name);
792   entries_.push_back(entry);
793 
794   return 0;
795 }
796 
WriteCentralDirectory()797 void OutputZipFile::WriteCentralDirectory() {
798   // central directory:
799   const u1 *central_directory_start = q;
800   for (size_t ii = 0; ii < entries_.size(); ++ii) {
801     LocalFileEntry *entry = entries_[ii];
802     put_u4le(q, CENTRAL_FILE_HEADER_SIGNATURE);
803     put_u2le(q, 0);  // version made by
804 
805     put_u2le(q, ZIP_VERSION_TO_EXTRACT);  // version to extract
806     put_u2le(q, 0);  // general purpose bit flag
807     put_u2le(q, entry->compression_method);  // compression method:
808     put_u2le(q, 0);                          // last_mod_file_time
809     put_u2le(q, 0);  // last_mod_file_date
810     put_u4le(q, entry->crc32);  // crc32
811     put_u4le(q, entry->compressed_length);    // compressed_size
812     put_u4le(q, entry->uncompressed_length);  // uncompressed_size
813     put_u2le(q, entry->file_name_length);
814     put_u2le(q, entry->extra_field_length);
815 
816     put_u2le(q, 0);  // file comment length
817     put_u2le(q, 0);  // disk number start
818     put_u2le(q, 0);  // internal file attributes
819     put_u4le(q, entry->external_attr);  // external file attributes
820     // relative offset of local header:
821     put_u4le(q, entry->local_header_offset);
822 
823     put_n(q, entry->file_name, entry->file_name_length);
824     put_n(q, entry->extra_field, entry->extra_field_length);
825   }
826   u4 central_directory_size = q - central_directory_start;
827 
828   put_u4le(q, END_OF_CENTRAL_DIR_SIGNATURE);
829   put_u2le(q, 0);  // number of this disk
830   put_u2le(q, 0);  // number of the disk with the start of the central directory
831   put_u2le(q, entries_.size());  // # central dir entries on this disk
832   put_u2le(q, entries_.size());  // total # entries in the central directory
833   put_u4le(q, central_directory_size);  // size of the central directory
834   put_u4le(q, Offset(central_directory_start));  // offset of start of central
835                                                  // directory wrt starting disk
836   put_u2le(q, 0);  // .ZIP file comment length
837 }
838 
WriteLocalFileHeader(const char * filename,const u4 attr)839 u1* OutputZipFile::WriteLocalFileHeader(const char* filename, const u4 attr) {
840   off_t file_name_length_ = strlen(filename);
841   LocalFileEntry *entry = new LocalFileEntry;
842   entry->local_header_offset = Offset(q);
843   entry->file_name_length = file_name_length_;
844   entry->file_name = new u1[file_name_length_];
845   entry->external_attr = attr;
846   memcpy(entry->file_name, filename, file_name_length_);
847   entry->extra_field_length = 0;
848   entry->extra_field = (const u1 *)"";
849   entry->crc32 = 0;
850 
851   // Output the ZIP local_file_header:
852   put_u4le(q, LOCAL_FILE_HEADER_SIGNATURE);
853   put_u2le(q, ZIP_VERSION_TO_EXTRACT);     // version to extract
854   put_u2le(q, 0);                          // general purpose bit flag
855   u1 *header_ptr = q;
856   put_u2le(q, COMPRESSION_METHOD_STORED);  // compression method = placeholder
857   put_u2le(q, 0);                          // last_mod_file_time
858   put_u2le(q, 0);                          // last_mod_file_date
859   put_u4le(q, entry->crc32);               // crc32
860   put_u4le(q, 0);  // compressed_size = placeholder
861   put_u4le(q, 0);  // uncompressed_size = placeholder
862   put_u2le(q, entry->file_name_length);
863   put_u2le(q, entry->extra_field_length);
864 
865   put_n(q, entry->file_name, entry->file_name_length);
866   put_n(q, entry->extra_field, entry->extra_field_length);
867   entries_.push_back(entry);
868 
869   return header_ptr;
870 }
871 
872 // Try to compress a file entry in memory using the deflate algorithm.
873 // It will compress buf (of size length) unless the compressed size is bigger
874 // than the input size. The result will overwrite the content of buf and the
875 // final size is returned.
TryDeflate(u1 * buf,size_t length)876 size_t TryDeflate(u1 *buf, size_t length) {
877   u1 *outbuf = reinterpret_cast<u1 *>(malloc(length));
878   z_stream stream;
879 
880   // Initialize the z_stream strcut for reading from buf and wrinting in outbuf.
881   stream.zalloc = Z_NULL;
882   stream.zfree = Z_NULL;
883   stream.opaque = Z_NULL;
884   stream.total_in = length;
885   stream.avail_in = length;
886   stream.total_out = length;
887   stream.avail_out = length;
888   stream.next_in = buf;
889   stream.next_out = outbuf;
890 
891   // deflateInit2 negative windows size prevent the zlib wrapper to be used.
892   if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
893                   -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
894     // Failure to compress => return the buffer uncompressed
895     free(outbuf);
896     return length;
897   }
898 
899   if (deflate(&stream, Z_FINISH) == Z_STREAM_END) {
900     // Compression successful and fits in outbuf, let's copy the result in buf.
901     length = stream.total_out;
902     memcpy(buf, outbuf, length);
903   }
904 
905   deflateEnd(&stream);
906   free(outbuf);
907 
908   // Return the length of the resulting buffer
909   return length;
910 }
911 
WriteFileSizeInLocalFileHeader(u1 * header_ptr,size_t out_length,bool compress,const u4 crc)912 size_t OutputZipFile::WriteFileSizeInLocalFileHeader(u1 *header_ptr,
913                                                      size_t out_length,
914                                                      bool compress,
915                                                      const u4 crc) {
916   size_t compressed_size = out_length;
917   if (compress) {
918     compressed_size = TryDeflate(q, out_length);
919   }
920   // compression method
921   if (compressed_size < out_length) {
922     put_u2le(header_ptr, COMPRESSION_METHOD_DEFLATED);
923   } else {
924     put_u2le(header_ptr, COMPRESSION_METHOD_STORED);
925   }
926   header_ptr += 4;
927   put_u4le(header_ptr, crc);              // crc32
928   put_u4le(header_ptr, compressed_size);  // compressed_size
929   put_u4le(header_ptr, out_length);       // uncompressed_size
930   return compressed_size;
931 }
932 
Finish()933 int OutputZipFile::Finish() {
934   if (fd_out > 0) {
935     WriteCentralDirectory();
936     if (ftruncate(fd_out, GetSize()) < 0) {
937       return error("ftruncate(fd_out, GetSize()): %s", strerror(errno));
938     }
939     if (close(fd_out) < 0) {
940       return error("close(fd_out): %s", strerror(errno));
941     }
942     fd_out = -1;
943   }
944   return 0;
945 }
946 
NewFile(const char * filename,const u4 attr)947 u1* OutputZipFile::NewFile(const char* filename, const u4 attr) {
948   header_ptr = WriteLocalFileHeader(filename, attr);
949   return q;
950 }
951 
FinishFile(size_t filelength,bool compress,bool compute_crc)952 int OutputZipFile::FinishFile(size_t filelength, bool compress,
953                               bool compute_crc) {
954   u4 crc = 0;
955   if (compute_crc) {
956     crc = crc32(crc, q, filelength);
957   }
958   size_t compressed_size =
959       WriteFileSizeInLocalFileHeader(header_ptr, filelength, compress, crc);
960   entries_.back()->crc32 = crc;
961   entries_.back()->compressed_length = compressed_size;
962   entries_.back()->uncompressed_length = filelength;
963   if (compressed_size < filelength) {
964     entries_.back()->compression_method = COMPRESSION_METHOD_DEFLATED;
965   } else {
966     entries_.back()->compression_method = COMPRESSION_METHOD_STORED;
967   }
968   q += compressed_size;
969   return 0;
970 }
971 
Create(const char * zip_file,u8 estimated_size)972 ZipBuilder* ZipBuilder::Create(const char* zip_file, u8 estimated_size) {
973   if (estimated_size > kMaximumOutputSize) {
974     fprintf(stderr,
975             "Uncompressed input jar has size %llu, "
976             "which exceeds the maximum supported output size %llu.\n"
977             "Assuming that ijar will be smaller and hoping for the best.\n",
978             estimated_size, kMaximumOutputSize);
979     estimated_size = kMaximumOutputSize;
980   }
981 
982   int fd_out = open(zip_file, O_CREAT|O_RDWR|O_TRUNC, 0644);
983   if (fd_out < 0) {
984     return NULL;
985   }
986 
987   // Create mmap-able sparse file
988   if (ftruncate(fd_out, estimated_size) < 0) {
989     return NULL;
990   }
991 
992   // Ensure that any buffer overflow in JarStripper will result in
993   // SIGSEGV or SIGBUS by over-allocating beyond the end of the file.
994   size_t mmap_length = std::min(estimated_size + sysconf(_SC_PAGESIZE),
995                                 (u8) std::numeric_limits<size_t>::max());
996 
997   void *zipdata_out = mmap(NULL, mmap_length, PROT_WRITE,
998                            MAP_SHARED, fd_out, 0);
999   if (zipdata_out == MAP_FAILED) {
1000     fprintf(stderr, "output_length=%llu\n", estimated_size);
1001     return NULL;
1002   }
1003 
1004   return new OutputZipFile(fd_out, (u1*) zipdata_out);
1005 }
1006 
EstimateSize(char ** files)1007 u8 ZipBuilder::EstimateSize(char **files) {
1008   struct stat statst;
1009   // Digital signature field size = 6, End of central directory = 22, Total = 28
1010   u8 size = 28;
1011   // Count the size of all the files in the input to estimate the size of the
1012   // output.
1013   for (int i = 0; files[i] != NULL; i++) {
1014     if (stat(files[i], &statst) != 0) {
1015       fprintf(stderr, "File %s does not seem to exist.", files[i]);
1016       return 0;
1017     }
1018     size += statst.st_size;
1019     // Add sizes of Zip meta data
1020     // local file header = 30 bytes
1021     // data descriptor = 12 bytes
1022     // central directory descriptor = 46 bytes
1023     //    Total: 88bytes
1024     size += 88;
1025     // The filename is stored twice (once in the central directory
1026     // and once in the local file header).
1027     size += strlen(files[i]) * 2;
1028   }
1029   return size;
1030 }
1031 
1032 }  // namespace devtools_ijar
1033