Home | History | Annotate | Download | only in ijar
      1 // Copyright 2015 Google Inc. All rights reserved.
      2 //
      3 // Author: Alan Donovan <adonovan (at) 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 //
     18 // Zip / Unzip file using ijar zip implementation.
     19 //
     20 // Note that this Zip implementation intentionally don't compute CRC-32
     21 // because it is useless computation for jar because Java doesn't care.
     22 // CRC-32 of all files in the zip file will be set to 0.
     23 //
     24 
     25 #include <stdio.h>
     26 #include <string.h>
     27 #include <stdlib.h>
     28 #include <limits.h>
     29 #include <fcntl.h>
     30 #include <unistd.h>
     31 #include <sys/mman.h>
     32 #include <errno.h>
     33 #include <memory>
     34 
     35 #include "zip.h"
     36 
     37 namespace devtools_ijar {
     38 
     39 #define SYSCALL(expr)  do { \
     40                          if ((expr) < 0) { \
     41                            perror(#expr); \
     42                            abort(); \
     43                          } \
     44                        } while (0)
     45 
     46 //
     47 // A ZipExtractorProcessor that extract all files in the ZIP file.
     48 //
     49 class UnzipProcessor : public ZipExtractorProcessor {
     50  public:
     51   // Create a processor who will extract the files into output_root
     52   // if "extract" is set to true and will print the list of files and
     53   // their unix modes if "verbose" is set to true.
     54   UnzipProcessor(const char *output_root, bool verbose, bool extract)
     55     : output_root_(output_root), verbose_(verbose), extract_(extract) {}
     56   virtual ~UnzipProcessor() {}
     57 
     58   virtual void Process(const char* filename, const u4 attr,
     59                        const u1* data, const size_t size);
     60   virtual bool Accept(const char* filename, const u4 attr) {
     61     return true;
     62   }
     63 
     64  private:
     65   const char *output_root_;
     66   const bool verbose_;
     67   const bool extract_;
     68 };
     69 
     70 // Concatene 2 path, path1 and path2, using / as a directory separator and
     71 // puting the result in "out". "size" specify the size of the output buffer
     72 void concat_path(char* out, const size_t size,
     73                  const char *path1, const char *path2) {
     74   int len1 = strlen(path1);
     75   size_t l = len1;
     76   strncpy(out, path1, size - 1);
     77   out[size-1] = 0;
     78   if (l < size - 1 && path1[len1] != '/' && path2[0] != '/') {
     79     out[l] = '/';
     80     l++;
     81     out[l] = 0;
     82   }
     83   if (l < size - 1) {
     84     strncat(out, path2, size - 1 - l);
     85   }
     86 }
     87 
     88 // Do a recursive mkdir of all folders of path except the last path
     89 // segment (if path ends with a / then the last path segment is empty).
     90 // All folders are created using "mode" for creation mode.
     91 void mkdirs(const char *path, mode_t mode) {
     92   char path_[PATH_MAX];
     93   struct stat statst;
     94   strncpy(path_, path, PATH_MAX);
     95   path_[PATH_MAX-1] = 0;
     96   char *pointer = path_;
     97   while ((pointer = strchr(pointer, '/')) != NULL) {
     98     if (path_ != pointer) {  // skip leading slash
     99       *pointer = 0;
    100       if (stat(path_, &statst) != 0) {
    101         if (mkdir(path_, mode) < 0) {
    102           fprintf(stderr, "Cannot create folder %s: %s\n",
    103                   path_, strerror(errno));
    104           abort();
    105         }
    106       }
    107       *pointer = '/';
    108     }
    109     pointer++;
    110   }
    111 }
    112 
    113 void UnzipProcessor::Process(const char* filename, const u4 attr,
    114                              const u1* data, const size_t size) {
    115   mode_t mode = zipattr_to_mode(attr);
    116   mode_t perm = mode & 0777;
    117   bool isdir = (mode & S_IFDIR) != 0;
    118   if (attr == 0) {
    119     // Fallback when the external attribute is not set.
    120     isdir = filename[strlen(filename)-1] == '/';
    121     perm = 0777;
    122   }
    123   if (verbose_) {
    124     printf("%c %o %s\n", isdir ? 'd' : 'f', perm, filename);
    125   }
    126   if (extract_) {
    127     char path[PATH_MAX];
    128     int fd;
    129     concat_path(path, PATH_MAX, output_root_, filename);
    130     mkdirs(path, perm);
    131     if (!isdir) {
    132       fd = open(path, O_CREAT | O_WRONLY, perm);
    133       if (fd < 0) {
    134         fprintf(stderr, "Cannot open file %s for writing: %s\n",
    135                 path, strerror(errno));
    136         abort();
    137       }
    138       SYSCALL(write(fd, data, size));
    139       SYSCALL(close(fd));
    140     }
    141   }
    142 }
    143 
    144 // Get the basename of path and store it in output. output_size
    145 // is the size of the output buffer.
    146 void basename(const char *path, char *output, size_t output_size) {
    147   const char *pointer = strrchr(path, '/');
    148   if (pointer == NULL) {
    149     pointer = path;
    150   } else {
    151     pointer++;  // Skip the leading slash.
    152   }
    153   strncpy(output, pointer, output_size);
    154   output[output_size-1] = 0;
    155 }
    156 
    157 
    158 // Execute the extraction (or just listing if just v is provided)
    159 int extract(char *zipfile, bool verbose, bool extract) {
    160   char output_root[PATH_MAX];
    161   getcwd(output_root, PATH_MAX);
    162 
    163   UnzipProcessor processor(output_root, verbose, extract);
    164   std::unique_ptr<ZipExtractor> extractor(ZipExtractor::Create(zipfile,
    165                                                                &processor));
    166   if (extractor.get() == NULL) {
    167     fprintf(stderr, "Unable to open zip file %s: %s.\n", zipfile,
    168             strerror(errno));
    169     return -1;
    170   }
    171 
    172   if (extractor->ProcessAll() < 0) {
    173     fprintf(stderr, "%s.\n", extractor->GetError());
    174     return -1;
    175   }
    176   return 0;
    177 }
    178 
    179 // Execute the create operation
    180 int create(char *zipfile, char **files, bool flatten, bool verbose,
    181            bool compress) {
    182   struct stat statst;
    183   u8 size = ZipBuilder::EstimateSize(files);
    184   if (size == 0) {
    185     return -1;
    186   }
    187   std::unique_ptr<ZipBuilder> builder(ZipBuilder::Create(zipfile, size));
    188   if (builder.get() == NULL) {
    189     fprintf(stderr, "Unable to create zip file %s: %s.\n",
    190             zipfile, strerror(errno));
    191     return -1;
    192   }
    193   for (int i = 0; files[i] != NULL; i++) {
    194     stat(files[i], &statst);
    195     char path[PATH_MAX];
    196     bool isdir = (statst.st_mode & S_IFDIR) != 0;
    197 
    198     if (flatten && isdir) {
    199       continue;
    200     }
    201 
    202     // Compute the path, flattening it if requested
    203     if (flatten) {
    204       basename(files[i], path, PATH_MAX);
    205     } else {
    206       strncpy(path, files[i], PATH_MAX);
    207       path[PATH_MAX-1] = 0;
    208       size_t len = strlen(path);
    209       if (isdir && len < PATH_MAX - 1) {
    210         // Add the trailing slash for folders
    211         path[len] = '/';
    212         path[len+1] = 0;
    213       }
    214     }
    215 
    216     if (verbose) {
    217       mode_t perm = statst.st_mode & 0777;
    218       printf("%c %o %s\n", isdir ? 'd' : 'f', perm, path);
    219     }
    220 
    221     u1 *buffer = builder->NewFile(path, mode_to_zipattr(statst.st_mode));
    222     if (isdir || statst.st_size == 0) {
    223       builder->FinishFile(0);
    224     } else {
    225       // mmap the input file and memcpy
    226       int fd = open(files[i], O_RDONLY);
    227       if (fd < 0) {
    228         fprintf(stderr, "Can't open file %s for reading: %s.\n",
    229                 files[i], strerror(errno));
    230         return -1;
    231       }
    232       void *data = mmap(NULL, statst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    233       if (data == MAP_FAILED) {
    234         fprintf(stderr, "Can't mmap file %s for reading: %s.\n",
    235                 files[i], strerror(errno));
    236         return -1;
    237       }
    238       memcpy(buffer, data, statst.st_size);
    239       munmap(data, statst.st_size);
    240       builder->FinishFile(statst.st_size, compress, true);
    241     }
    242   }
    243   if (builder->Finish() < 0) {
    244     fprintf(stderr, "%s\n", builder->GetError());
    245     return -1;
    246   }
    247   return 0;
    248 }
    249 
    250 }  // namespace devtools_ijar
    251 
    252 //
    253 // main method
    254 //
    255 static void usage(char *progname) {
    256   fprintf(stderr, "Usage: %s [vxc[fC]] x.zip [file1...filen]\n", progname);
    257   fprintf(stderr, "  v verbose - list all file in x.zip\n");
    258   fprintf(stderr, "  x extract - extract file in x.zip in current directory\n");
    259   fprintf(stderr, "  c create  - add files to x.zip\n");
    260   fprintf(stderr, "  f flatten - flatten files to use with create operation\n");
    261   fprintf(stderr,
    262           "  C compress - compress files when using the create operation\n");
    263   fprintf(stderr, "x and c cannot be used in the same command-line.\n");
    264   exit(1);
    265 }
    266 
    267 int main(int argc, char **argv) {
    268   bool extract = false;
    269   bool verbose = false;
    270   bool create = false;
    271   bool compress = false;
    272   bool flatten = false;
    273 
    274   if (argc < 3) {
    275     usage(argv[0]);
    276   }
    277 
    278   for (int i = 0; argv[1][i] != 0; i++) {
    279     switch (argv[1][i]) {
    280     case 'x':
    281       extract = true;
    282       break;
    283     case 'v':
    284       verbose = true;
    285       break;
    286     case 'c':
    287       create = true;
    288       break;
    289     case 'f':
    290       flatten = true;
    291       break;
    292     case 'C':
    293       compress = true;
    294       break;
    295     default:
    296       usage(argv[0]);
    297     }
    298   }
    299   if (create) {
    300     if (extract) {
    301       usage(argv[0]);
    302     }
    303     // Create a zip
    304     return devtools_ijar::create(argv[2], argv + 3, flatten, verbose, compress);
    305   } else {
    306     if (flatten) {
    307       usage(argv[0]);
    308     }
    309     // Extraction / list mode
    310     return devtools_ijar::extract(argv[2], verbose, extract);
    311   }
    312 }
    313