// 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%zx" " bytes long, should be at least 0x%zx 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(ecd_ptr)->is()) { ecd = reinterpret_cast(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. uint32_t cen_position = ecd->cen_offset32(); if (!ziph::zfield_has_ext64(cen_position)) { if (!mapped_file_.mapped(mapped_file_.address(cen_position))) { diag_warnx("%s:%d: %s is corrupt: Central Directory location 0x%" PRIx32 " 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%" PRIx32, __FILE__, __LINE__, path.c_str(), mapped_file_.offset(ecd), cen_position); mapped_file_.Close(); return false; } } uint32_t cen_size = ecd->cen_size32(); if (!ziph::zfield_has_ext64(cen_size)) { if (cen_size > mapped_file_.offset(ecd)) { diag_warnx("%s:%d: %s is corrupt: Central Directory size 0x%" PRIx32 " is too large", __FILE__, __LINE__, path.c_str(), cen_size); mapped_file_.Close(); return false; } } if (cen_size == 0) { // Empty archive, let cdh_ point to End of Central Directory. cdh_ = reinterpret_cast(ecd); preamble_size_ = mapped_file_.offset(cdh_) - cen_position; } else { auto ecd64loc = reinterpret_cast( ziph::byte_ptr(ecd) - sizeof(ECD64Locator)); if (ecd64loc->is()) { auto ecd64 = reinterpret_cast(ziph::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(ziph::byte_ptr(ecd64) - ecd64->cen_size()); preamble_size_ = mapped_file_.offset(cdh_) - ecd64->cen_offset(); // Find CEN and preamble size. } else { if (ziph::zfield_has_ext64(cen_size) || ziph::zfield_has_ext64(cen_position)) { 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(ziph::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; }