| // 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<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. | 
 |   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<const CDH *>(ecd); | 
 |     preamble_size_ = mapped_file_.offset(cdh_) - cen_position; | 
 |   } else { | 
 |     auto ecd64loc = reinterpret_cast<const ECD64Locator *>( | 
 |         ziph::byte_ptr(ecd) - sizeof(ECD64Locator)); | 
 |     if (ecd64loc->is()) { | 
 |       auto ecd64 = reinterpret_cast<const ECD64 *>(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<const CDH *>(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<const CDH *>(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; | 
 | } |