diff options
Diffstat (limited to 'src/tools/singlejar/input_jar.cc')
-rw-r--r-- | src/tools/singlejar/input_jar.cc | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/tools/singlejar/input_jar.cc b/src/tools/singlejar/input_jar.cc new file mode 100644 index 0000000000..980a90c6f8 --- /dev/null +++ b/src/tools/singlejar/input_jar.cc @@ -0,0 +1,149 @@ +// 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. + +#include "src/tools/singlejar/input_jar.h" + +bool InputJar::Open(const std::string &path) { + if (!path_.empty()) { + diag_errx(1, "%s:%d: This instance is already handling %s\n", __FILE__, + __LINE__, path_.c_str()); + } + if (!mapped_file_.Open(path)) { + diag_warn("%s:%d: Cannot open input jar %s", __FILE__, __LINE__, + path.c_str()); + mapped_file_.Close(); + return false; + } + if (mapped_file_.size() < sizeof(ECD)) { + diag_warnx( + "%s:%d: %s is only 0x%lx" + " bytes long, should be at least 0x%lx bytes long", + __FILE__, __LINE__, path.c_str(), mapped_file_.size(), sizeof(ECD)); + mapped_file_.Close(); + return false; + } + + // Now locate End of Central Directory (ECD) record. + auto ecd_min = mapped_file_.end() - 65536 - sizeof(ECD); + if (ecd_min < mapped_file_.start()) { + ecd_min = mapped_file_.start(); + } + + const ECD *ecd = nullptr; + for (auto ecd_ptr = mapped_file_.end() - sizeof(ECD); ecd_ptr >= ecd_min; + --ecd_ptr) { + if (reinterpret_cast<const ECD *>(ecd_ptr)->is()) { + ecd = reinterpret_cast<const ECD *>(ecd_ptr); + break; + } + } + if (ecd == nullptr) { + diag_warnx("%s:%d: Cannot locate ECD record in %s", __FILE__, __LINE__, + path.c_str()); + mapped_file_.Close(); + return false; + } + + /* Find Central Directory and preamble size. We want to handle the case + * where a Jar/Zip file contains a preamble (an arbitrary data before the + * first entry) and 'zip -A' was not called to adjust the offsets, so all + * the offsets are off by the preamble size. In the 32-bit case (that is, + * there is no ECD64Locator+ECD64), ECD immediately follows the last CDH, + * ECD immediately follows the Central Directory, and contains its size, so + * Central Directory can be found reliably. We then use its stated location, + * which ECD contains, too, to calculate the preamble size. In the 64-bit + * case, there are ECD64 and ECD64Locator records between the end of the + * Central Directory and the ECD, the calculation is similar, with the + * exception of the logic to find the actual start of the ECD64. + * ECD64Locator contains only its position in the file, which is off by + * preamble size, but does not contain the actual size of ECD64, which in + * theory is variable (the fixed fields may be followed by some custom data, + * with the total size saved in ECD64::remaining_size and thus unavailable + * until we find ECD64. We assume that the custom data is missing. + */ + + // First, sanity checks. + uint64_t cen_position = ecd->cen_offset32(); + if (cen_position != 0xFFFFFFFF) { + if (!mapped_file_.mapped(mapped_file_.address(cen_position))) { + diag_warnx("%s:%d: %s is corrupt: Central Directory location 0x%" PRIx64 + " is invalid", + __FILE__, __LINE__, path.c_str(), cen_position); + mapped_file_.Close(); + return false; + } + if (mapped_file_.offset(ecd) <= cen_position) { + diag_warnx("%s:%d: %s is corrupt: End of Central Directory at 0x%" PRIx64 + " precedes Central Directory at 0x%" PRIx64, + __FILE__, __LINE__, path.c_str(), mapped_file_.offset(ecd), + cen_position); + mapped_file_.Close(); + return false; + } + } + uint64_t cen_size = ecd->cen_size32(); + if (cen_size != 0xFFFFFFFF) { + if (cen_size > mapped_file_.offset(ecd)) { + diag_warnx("%s:%d: %s is corrupt: Central Directory size 0x%" PRIx64 + " is too large", + __FILE__, __LINE__, path.c_str(), cen_size); + mapped_file_.Close(); + return false; + } + } + + auto ecd64loc = reinterpret_cast<const ECD64Locator *>(byte_ptr(ecd) - + sizeof(ECD64Locator)); + if (ecd64loc->is()) { + auto ecd64 = + reinterpret_cast<const ECD64 *>(byte_ptr(ecd64loc) - sizeof(ECD64)); + if (!ecd64->is()) { + diag_warnx( + "%s:%d: %s is corrupt, expected ECD64 record at offset 0x%" PRIx64 + " is missing", + __FILE__, __LINE__, path.c_str(), mapped_file_.offset(ecd64)); + mapped_file_.Close(); + return false; + } + cdh_ = reinterpret_cast<const CDH *>(byte_ptr(ecd64) - ecd64->cen_size()); + preamble_size_ = mapped_file_.offset(cdh_) - ecd64->cen_offset(); + // Find CEN and preamble size. + } else { + if (cen_size == 0xFFFFFFFF || cen_position == 0xFFFFFFFF) { + diag_warnx( + "%s:%d: %s is corrupt, expected ECD64 locator record at " + "offset 0x%" PRIx64 " is missing", + __FILE__, __LINE__, path.c_str(), mapped_file_.offset(ecd64loc)); + return false; + } + cdh_ = reinterpret_cast<const CDH *>(byte_ptr(ecd) - cen_size); + preamble_size_ = mapped_file_.offset(cdh_) - cen_position; + } + if (!cdh_->is()) { + diag_warnx( + "%s:%d: In %s, expected central file header signature at " + "offset0x%" PRIx64, + __FILE__, __LINE__, path.c_str(), mapped_file_.offset(cdh_)); + mapped_file_.Close(); + return false; + } + path_ = path; + return true; +} + +bool InputJar::Close() { + mapped_file_.Close(); + path_.clear(); + return true; +} |