blob: c9d6677a975f2e143cdefc423011c490051074fc [file]
// Copyright 2016 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.
#ifndef BAZEL_SRC_TOOLS_SINGLEJAR_INPUT_JAR_H_
#define BAZEL_SRC_TOOLS_SINGLEJAR_INPUT_JAR_H_ 1
#include <inttypes.h>
#include <stdlib.h>
#include "src/tools/singlejar/diag.h"
#include "src/tools/singlejar/mapped_file.h"
#include "src/tools/singlejar/zip_headers.h"
/*
* An input jar. The usage pattern is:
* InputJar input_jar("path/to/file");
* if (!input_jar.Open()) { fail...}
* CDH *dir_entry;
* LH *local_header;
* while (dir_entry = input_jar.NextExtry(&local_header)) {
* // process entry.
* }
* input_jar.Close(); // actually, called by destructor, too.
*/
class InputJar {
public:
InputJar() : path_(nullptr) {}
~InputJar() { Close(); }
int fd() const { return mapped_file_.fd(); }
// Opens the file, memory maps it and locates Central Directory.
bool Open(const char *path) {
if (path_ != nullptr) {
diag_errx(1, "%s:%d: This instance is already handling %s\n", __FILE__,
__LINE__, path_);
}
if (!mapped_file_.Open(path)) {
diag_warn("%s:%d: Cannot open input jar %s", __FILE__, __LINE__, path);
mapped_file_.Close();
return false;
}
if (mapped_file_.size() < sizeof(ECD)) {
diag_warnx(
"%s:%d: %s is only %ld bytes long, should be at least %lu bytes long",
__FILE__, __LINE__, path_, mapped_file_.size(), sizeof(ECD));
mapped_file_.Close();
return false;
}
// Now locate End of Central Directory (ECD) record.
const char *ecd_min = mapped_file_.end() - 65536 - sizeof(ECD);
if (ecd_min < mapped_file_.start()) {
ecd_min = mapped_file_.start();
}
const ECD *ecd = nullptr;
for (const char *ecd_ptr = mapped_file_.end() - sizeof(ECD);
ecd_ptr >= ecd_min; --ecd_ptr) {
ecd = reinterpret_cast<const ECD *>(ecd_ptr);
if (ecd->is() && ecd) {
break;
}
}
if (!ecd) {
diag_warnx("%s:%d: Cannot locate ECD record in %s", __FILE__, __LINE__,
path);
mapped_file_.Close();
return false;
}
uint64_t offset_to_dir = ecd->cen_offset32();
if (offset_to_dir == 0xFFFFFFFF) {
const ECD64 *ecd64 = reinterpret_cast<const ECD64 *>(
mapped_file_.address(ecd->ecd64_offset()));
offset_to_dir = ecd64->cen_offset();
}
cdh_ = reinterpret_cast<const CDH *>(mapped_file_.address(offset_to_dir));
if (!cdh_->is()) {
diag_warnx("in %s, expected central file header signature at 0x%" PRIx64,
path, offset_to_dir);
mapped_file_.Close();
return false;
}
path_ = strdup(path);
return true;
}
// Returns the next Central Directory Header or NULL.
const CDH *NextEntry(const LH **local_header_ptr) {
if (!path_) {
diag_errx(1, "%s:%d: call Open() first!", __FILE__, __LINE__);
}
if (!cdh_->is()) {
return nullptr;
}
const CDH *current_cdh = cdh_;
const uint8_t *new_cdr = byte_ptr(cdh_) + cdh_->size();
if (!mapped_file_.mapped(new_cdr)) {
diag_errx(
1,
"Bad directory record at offset 0x%" PRIx64 " of %s\n"
"file name length = %u, extra_field length = %u, comment length = %u",
CentralDirectoryRecordOffset(cdh_), path_, cdh_->file_name_length(),
cdh_->extra_fields_length(), cdh_->comment_length());
}
cdh_ = reinterpret_cast<const CDH *>(new_cdr);
*local_header_ptr = LocalHeader(current_cdh);
return current_cdh;
}
// Closes the file.
bool Close() {
mapped_file_.Close();
if (path_ != nullptr) {
free(path_);
path_ = nullptr;
}
return true;
}
uint64_t CentralDirectoryRecordOffset(const void *cdr) const {
return mapped_file_.offset(static_cast<const char *>(cdr));
}
const LH *LocalHeader(const CDH *cdh) const {
return reinterpret_cast<const LH *>(
mapped_file_.address(cdh->local_header_offset()));
}
private:
char *path_;
MappedFile mapped_file_;
const CDH *cdh_; // current directory entry
};
#endif // BAZEL_SRC_TOOLS_SINGLEJAR_INPUT_JAR_H_