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