Change log:95336cb92b..191d55580eFull diff:95336cb92b..191d55580eRoll chromium third_party 4e16929f46..3a8f2a9e1e Change log:4e16929f46..3a8f2a9e1eChanged dependencies: * src/tools:c44a3f5eca..f524a53b81DEPS diff:95336cb92b..191d55580e/DEPS No update to Clang. TBR=titovartem@google.com, BUG=None CQ_INCLUDE_TRYBOTS=master.internal.tryserver.corp.webrtc:linux_internal Change-Id: Ic9c4a62b050383646e9fcf5cc07a5653c14ac06e Reviewed-on: https://webrtc-review.googlesource.com/76120 Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Artem Titov <titovartem@webrtc.org> Commit-Queue: Artem Titov <titovartem@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23205}
406 lines
11 KiB
C++
406 lines
11 KiB
C++
// Copyright 2015 The Bazel Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//
|
|
// Zip / Unzip file using ijar zip implementation.
|
|
//
|
|
// Note that this Zip implementation intentionally don't compute CRC-32
|
|
// because it is useless computation for jar because Java doesn't care.
|
|
// CRC-32 of all files in the zip file will be set to 0.
|
|
//
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <memory>
|
|
|
|
#include "third_party/ijar/zip.h"
|
|
|
|
namespace devtools_ijar {
|
|
|
|
#define SYSCALL(expr) do { \
|
|
if ((expr) < 0) { \
|
|
perror(#expr); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
|
|
//
|
|
// A ZipExtractorProcessor that extract all files in the ZIP file.
|
|
//
|
|
class UnzipProcessor : public ZipExtractorProcessor {
|
|
public:
|
|
// Create a processor who will extract the files into output_root
|
|
// if "extract" is set to true and will print the list of files and
|
|
// their unix modes if "verbose" is set to true.
|
|
UnzipProcessor(const char *output_root, bool verbose, bool extract)
|
|
: output_root_(output_root), verbose_(verbose), extract_(extract) {}
|
|
virtual ~UnzipProcessor() {}
|
|
|
|
virtual void Process(const char* filename, const u4 attr,
|
|
const u1* data, const size_t size);
|
|
virtual bool Accept(const char* filename, const u4 attr) {
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const char *output_root_;
|
|
const bool verbose_;
|
|
const bool extract_;
|
|
};
|
|
|
|
// Concatene 2 path, path1 and path2, using / as a directory separator and
|
|
// puting the result in "out". "size" specify the size of the output buffer
|
|
void concat_path(char* out, const size_t size,
|
|
const char *path1, const char *path2) {
|
|
int len1 = strlen(path1);
|
|
size_t l = len1;
|
|
strncpy(out, path1, size - 1);
|
|
out[size-1] = 0;
|
|
if (l < size - 1 && path1[len1] != '/' && path2[0] != '/') {
|
|
out[l] = '/';
|
|
l++;
|
|
out[l] = 0;
|
|
}
|
|
if (l < size - 1) {
|
|
strncat(out, path2, size - 1 - l);
|
|
}
|
|
}
|
|
|
|
// Do a recursive mkdir of all folders of path except the last path
|
|
// segment (if path ends with a / then the last path segment is empty).
|
|
// All folders are created using "mode" for creation mode.
|
|
void mkdirs(const char *path, mode_t mode) {
|
|
char path_[PATH_MAX];
|
|
struct stat statst;
|
|
strncpy(path_, path, PATH_MAX);
|
|
path_[PATH_MAX-1] = 0;
|
|
char *pointer = path_;
|
|
while ((pointer = strchr(pointer, '/')) != NULL) {
|
|
if (path_ != pointer) { // skip leading slash
|
|
*pointer = 0;
|
|
if (stat(path_, &statst) != 0) {
|
|
if (mkdir(path_, mode) < 0) {
|
|
fprintf(stderr, "Cannot create folder %s: %s\n",
|
|
path_, strerror(errno));
|
|
abort();
|
|
}
|
|
}
|
|
*pointer = '/';
|
|
}
|
|
pointer++;
|
|
}
|
|
}
|
|
|
|
void UnzipProcessor::Process(const char* filename, const u4 attr,
|
|
const u1* data, const size_t size) {
|
|
mode_t mode = zipattr_to_mode(attr);
|
|
mode_t perm = mode & 0777;
|
|
bool isdir = (mode & S_IFDIR) != 0;
|
|
if (attr == 0) {
|
|
// Fallback when the external attribute is not set.
|
|
isdir = filename[strlen(filename)-1] == '/';
|
|
perm = 0777;
|
|
}
|
|
if (verbose_) {
|
|
printf("%c %o %s\n", isdir ? 'd' : 'f', perm, filename);
|
|
}
|
|
if (extract_) {
|
|
char path[PATH_MAX];
|
|
int fd;
|
|
concat_path(path, PATH_MAX, output_root_, filename);
|
|
mkdirs(path, perm);
|
|
if (!isdir) {
|
|
fd = open(path, O_CREAT | O_WRONLY, perm);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Cannot open file %s for writing: %s\n",
|
|
path, strerror(errno));
|
|
abort();
|
|
}
|
|
SYSCALL(write(fd, data, size));
|
|
SYSCALL(close(fd));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the basename of path and store it in output. output_size
|
|
// is the size of the output buffer.
|
|
void basename(const char *path, char *output, size_t output_size) {
|
|
const char *pointer = strrchr(path, '/');
|
|
if (pointer == NULL) {
|
|
pointer = path;
|
|
} else {
|
|
pointer++; // Skip the leading slash.
|
|
}
|
|
strncpy(output, pointer, output_size);
|
|
output[output_size-1] = 0;
|
|
}
|
|
|
|
// copy size bytes from file descriptor fd into buffer.
|
|
int copy_file_to_buffer(int fd, size_t size, void *buffer) {
|
|
size_t nb_read = 0;
|
|
while (nb_read < size) {
|
|
size_t to_read = size - nb_read;
|
|
if (to_read > 16384 /* 16K */) {
|
|
to_read = 16384;
|
|
}
|
|
ssize_t r = read(fd, static_cast<uint8_t *>(buffer) + nb_read, to_read);
|
|
if (r < 0) {
|
|
return -1;
|
|
}
|
|
nb_read += r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Execute the extraction (or just listing if just v is provided)
|
|
int extract(char *zipfile, bool verbose, bool extract) {
|
|
char output_root[PATH_MAX];
|
|
if (getcwd(output_root, PATH_MAX) == NULL) {
|
|
fprintf(stderr, "getcwd() failed: %s.\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
UnzipProcessor processor(output_root, verbose, extract);
|
|
std::unique_ptr<ZipExtractor> extractor(ZipExtractor::Create(zipfile,
|
|
&processor));
|
|
if (extractor.get() == NULL) {
|
|
fprintf(stderr, "Unable to open zip file %s: %s.\n", zipfile,
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (extractor->ProcessAll() < 0) {
|
|
fprintf(stderr, "%s.\n", extractor->GetError());
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// add a file to the zip
|
|
int add_file(std::unique_ptr<ZipBuilder> const &builder, char *file,
|
|
bool flatten, bool verbose, bool compress) {
|
|
struct stat statst;
|
|
if (stat(file, &statst) < 0) {
|
|
fprintf(stderr, "Cannot stat file %s: %s.\n", file, strerror(errno));
|
|
return -1;
|
|
}
|
|
bool isdir = (statst.st_mode & S_IFDIR) != 0;
|
|
|
|
if (flatten && isdir) {
|
|
return 0;
|
|
}
|
|
|
|
// Compute the path, flattening it if requested
|
|
char path[PATH_MAX];
|
|
size_t len = strlen(file);
|
|
if (len > PATH_MAX) {
|
|
fprintf(stderr, "Path too long: %s.\n", file);
|
|
return -1;
|
|
}
|
|
if (flatten) {
|
|
basename(file, path, PATH_MAX);
|
|
} else {
|
|
strncpy(path, file, PATH_MAX);
|
|
path[PATH_MAX - 1] = 0;
|
|
if (isdir && len < PATH_MAX - 1) {
|
|
// Add the trailing slash for folders
|
|
path[len] = '/';
|
|
path[len + 1] = 0;
|
|
}
|
|
}
|
|
|
|
if (verbose) {
|
|
mode_t perm = statst.st_mode & 0777;
|
|
printf("%c %o %s\n", isdir ? 'd' : 'f', perm, path);
|
|
}
|
|
|
|
u1 *buffer = builder->NewFile(path, mode_to_zipattr(statst.st_mode));
|
|
if (isdir || statst.st_size == 0) {
|
|
builder->FinishFile(0);
|
|
} else {
|
|
// read the input file
|
|
int fd = open(file, O_RDONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Can't open file %s for reading: %s.\n", file,
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
if (copy_file_to_buffer(fd, statst.st_size, buffer) < 0) {
|
|
fprintf(stderr, "Can't read file %s: %s.\n", file, strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
builder->FinishFile(statst.st_size, compress, true);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Read a list of files separated by newlines. The resulting array can be
|
|
// freed using the free method.
|
|
char **read_filelist(char *filename) {
|
|
struct stat statst;
|
|
int fd = open(filename, O_RDONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Can't open file %s for reading: %s.\n", filename,
|
|
strerror(errno));
|
|
return NULL;
|
|
}
|
|
if (fstat(fd, &statst) < 0) {
|
|
fprintf(stderr, "Cannot stat file %s: %s.\n", filename, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
char *data = static_cast<char *>(malloc(statst.st_size));
|
|
if (copy_file_to_buffer(fd, statst.st_size, data) < 0) {
|
|
fprintf(stderr, "Can't read file %s: %s.\n", filename, strerror(errno));
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
close(fd);
|
|
|
|
int nb_entries = 1;
|
|
for (int i = 0; i < statst.st_size; i++) {
|
|
if (data[i] == '\n') {
|
|
nb_entries++;
|
|
}
|
|
}
|
|
|
|
size_t sizeof_array = sizeof(char *) * (nb_entries + 1);
|
|
void *result = malloc(sizeof_array + statst.st_size);
|
|
// copy the content
|
|
char **filelist = static_cast<char **>(result);
|
|
char *content = static_cast<char *>(result) + sizeof_array;
|
|
memcpy(content, data, statst.st_size);
|
|
free(data);
|
|
// Create the corresponding array
|
|
int j = 1;
|
|
filelist[0] = content;
|
|
for (int i = 0; i < statst.st_size; i++) {
|
|
if (content[i] == '\n') {
|
|
content[i] = 0;
|
|
if (i + 1 < statst.st_size) {
|
|
filelist[j] = content + i + 1;
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
filelist[j] = NULL;
|
|
return filelist;
|
|
}
|
|
|
|
// Execute the create operation
|
|
int create(char *zipfile, char **files, bool flatten, bool verbose,
|
|
bool compress) {
|
|
u8 size = ZipBuilder::EstimateSize(files);
|
|
if (size == 0) {
|
|
return -1;
|
|
}
|
|
std::unique_ptr<ZipBuilder> builder(ZipBuilder::Create(zipfile, size));
|
|
if (builder.get() == NULL) {
|
|
fprintf(stderr, "Unable to create zip file %s: %s.\n",
|
|
zipfile, strerror(errno));
|
|
return -1;
|
|
}
|
|
for (int i = 0; files[i] != NULL; i++) {
|
|
if (add_file(builder, files[i], flatten, verbose, compress) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
if (builder->Finish() < 0) {
|
|
fprintf(stderr, "%s\n", builder->GetError());
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // namespace devtools_ijar
|
|
|
|
//
|
|
// main method
|
|
//
|
|
static void usage(char *progname) {
|
|
fprintf(stderr, "Usage: %s [vxc[fC]] x.zip [file1...filen]\n", progname);
|
|
fprintf(stderr, " v verbose - list all file in x.zip\n");
|
|
fprintf(stderr, " x extract - extract file in x.zip in current directory\n");
|
|
fprintf(stderr, " c create - add files to x.zip\n");
|
|
fprintf(stderr, " f flatten - flatten files to use with create operation\n");
|
|
fprintf(stderr,
|
|
" C compress - compress files when using the create operation\n");
|
|
fprintf(stderr, "x and c cannot be used in the same command-line.\n");
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
bool extract = false;
|
|
bool verbose = false;
|
|
bool create = false;
|
|
bool compress = false;
|
|
bool flatten = false;
|
|
|
|
if (argc < 3) {
|
|
usage(argv[0]);
|
|
}
|
|
|
|
for (int i = 0; argv[1][i] != 0; i++) {
|
|
switch (argv[1][i]) {
|
|
case 'x':
|
|
extract = true;
|
|
break;
|
|
case 'v':
|
|
verbose = true;
|
|
break;
|
|
case 'c':
|
|
create = true;
|
|
break;
|
|
case 'f':
|
|
flatten = true;
|
|
break;
|
|
case 'C':
|
|
compress = true;
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
}
|
|
}
|
|
if (create) {
|
|
if (extract) {
|
|
usage(argv[0]);
|
|
}
|
|
// Create a zip
|
|
char **filelist = argv + 3;
|
|
if (argc == 4 && argv[3][0] == '@') {
|
|
// We never free that list because it needs to be allocated during the
|
|
// whole execution, the system will reclaim memory.
|
|
filelist = devtools_ijar::read_filelist(argv[3] + 1);
|
|
if (filelist == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
return devtools_ijar::create(argv[2], filelist, flatten, verbose, compress);
|
|
} else {
|
|
if (flatten) {
|
|
usage(argv[0]);
|
|
}
|
|
// Extraction / list mode
|
|
return devtools_ijar::extract(argv[2], verbose, extract);
|
|
}
|
|
}
|