OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 Google Inc. 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 // |
| 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 "third_party/ijar/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 int 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); |
| 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 } |
OLD | NEW |