aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Utility/ZipFile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Utility/ZipFile.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ZipFile.cpp180
1 files changed, 180 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Utility/ZipFile.cpp b/contrib/llvm-project/lldb/source/Utility/ZipFile.cpp
new file mode 100644
index 000000000000..b8ed956cbfcb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ZipFile.cpp
@@ -0,0 +1,180 @@
+//===-- ZipFile.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/ZipFile.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/Endian.h"
+
+using namespace lldb_private;
+using namespace llvm::support;
+
+namespace {
+
+// Zip headers.
+// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
+
+// The end of central directory record.
+struct EocdRecord {
+ static constexpr char kSignature[] = {0x50, 0x4b, 0x05, 0x06};
+ char signature[sizeof(kSignature)];
+ unaligned_uint16_t disks;
+ unaligned_uint16_t cd_start_disk;
+ unaligned_uint16_t cds_on_this_disk;
+ unaligned_uint16_t cd_records;
+ unaligned_uint32_t cd_size;
+ unaligned_uint32_t cd_offset;
+ unaligned_uint16_t comment_length;
+};
+
+// Logical find limit for the end of central directory record.
+const size_t kEocdRecordFindLimit =
+ sizeof(EocdRecord) +
+ std::numeric_limits<decltype(EocdRecord::comment_length)>::max();
+
+// Central directory record.
+struct CdRecord {
+ static constexpr char kSignature[] = {0x50, 0x4b, 0x01, 0x02};
+ char signature[sizeof(kSignature)];
+ unaligned_uint16_t version_made_by;
+ unaligned_uint16_t version_needed_to_extract;
+ unaligned_uint16_t general_purpose_bit_flag;
+ unaligned_uint16_t compression_method;
+ unaligned_uint16_t last_modification_time;
+ unaligned_uint16_t last_modification_date;
+ unaligned_uint32_t crc32;
+ unaligned_uint32_t compressed_size;
+ unaligned_uint32_t uncompressed_size;
+ unaligned_uint16_t file_name_length;
+ unaligned_uint16_t extra_field_length;
+ unaligned_uint16_t comment_length;
+ unaligned_uint16_t file_start_disk;
+ unaligned_uint16_t internal_file_attributes;
+ unaligned_uint32_t external_file_attributes;
+ unaligned_uint32_t local_file_header_offset;
+};
+// Immediately after CdRecord,
+// - file name (file_name_length)
+// - extra field (extra_field_length)
+// - comment (comment_length)
+
+// Local file header.
+struct LocalFileHeader {
+ static constexpr char kSignature[] = {0x50, 0x4b, 0x03, 0x04};
+ char signature[sizeof(kSignature)];
+ unaligned_uint16_t version_needed_to_extract;
+ unaligned_uint16_t general_purpose_bit_flag;
+ unaligned_uint16_t compression_method;
+ unaligned_uint16_t last_modification_time;
+ unaligned_uint16_t last_modification_date;
+ unaligned_uint32_t crc32;
+ unaligned_uint32_t compressed_size;
+ unaligned_uint32_t uncompressed_size;
+ unaligned_uint16_t file_name_length;
+ unaligned_uint16_t extra_field_length;
+};
+// Immediately after LocalFileHeader,
+// - file name (file_name_length)
+// - extra field (extra_field_length)
+// - file data (should be compressed_size == uncompressed_size, page aligned)
+
+const EocdRecord *FindEocdRecord(lldb::DataBufferSP zip_data) {
+ // Find backward the end of central directory record from the end of the zip
+ // file to the find limit.
+ const uint8_t *zip_data_end = zip_data->GetBytes() + zip_data->GetByteSize();
+ const uint8_t *find_limit = zip_data_end - kEocdRecordFindLimit;
+ const uint8_t *p = zip_data_end - sizeof(EocdRecord);
+ for (; p >= zip_data->GetBytes() && p >= find_limit; p--) {
+ auto eocd = reinterpret_cast<const EocdRecord *>(p);
+ if (::memcmp(eocd->signature, EocdRecord::kSignature,
+ sizeof(EocdRecord::kSignature)) == 0) {
+ // Found the end of central directory. Sanity check the values.
+ if (eocd->cd_records * sizeof(CdRecord) > eocd->cd_size ||
+ zip_data->GetBytes() + eocd->cd_offset + eocd->cd_size > p)
+ return nullptr;
+
+ // This is a valid end of central directory record.
+ return eocd;
+ }
+ }
+ return nullptr;
+}
+
+bool GetFile(lldb::DataBufferSP zip_data, uint32_t local_file_header_offset,
+ lldb::offset_t &file_offset, lldb::offset_t &file_size) {
+ auto local_file_header = reinterpret_cast<const LocalFileHeader *>(
+ zip_data->GetBytes() + local_file_header_offset);
+ // The signature should match.
+ if (::memcmp(local_file_header->signature, LocalFileHeader::kSignature,
+ sizeof(LocalFileHeader::kSignature)) != 0)
+ return false;
+
+ auto file_data = reinterpret_cast<const uint8_t *>(local_file_header + 1) +
+ local_file_header->file_name_length +
+ local_file_header->extra_field_length;
+ // File should be uncompressed.
+ if (local_file_header->compressed_size !=
+ local_file_header->uncompressed_size)
+ return false;
+
+ // This file is valid. Return the file offset and size.
+ file_offset = file_data - zip_data->GetBytes();
+ file_size = local_file_header->uncompressed_size;
+ return true;
+}
+
+bool FindFile(lldb::DataBufferSP zip_data, const EocdRecord *eocd,
+ const llvm::StringRef file_path, lldb::offset_t &file_offset,
+ lldb::offset_t &file_size) {
+ // Find the file from the central directory records.
+ auto cd = reinterpret_cast<const CdRecord *>(zip_data->GetBytes() +
+ eocd->cd_offset);
+ size_t cd_records = eocd->cd_records;
+ for (size_t i = 0; i < cd_records; i++) {
+ // The signature should match.
+ if (::memcmp(cd->signature, CdRecord::kSignature,
+ sizeof(CdRecord::kSignature)) != 0)
+ return false;
+
+ // Sanity check the file name values.
+ auto file_name = reinterpret_cast<const char *>(cd + 1);
+ size_t file_name_length = cd->file_name_length;
+ if (file_name + file_name_length >= reinterpret_cast<const char *>(eocd) ||
+ file_name_length == 0)
+ return false;
+
+ // Compare the file name.
+ if (file_path == llvm::StringRef(file_name, file_name_length)) {
+ // Found the file.
+ return GetFile(zip_data, cd->local_file_header_offset, file_offset,
+ file_size);
+ } else {
+ // Skip to the next central directory record.
+ cd = reinterpret_cast<const CdRecord *>(
+ reinterpret_cast<const char *>(cd) + sizeof(CdRecord) +
+ cd->file_name_length + cd->extra_field_length + cd->comment_length);
+ // Sanity check the pointer.
+ if (reinterpret_cast<const char *>(cd) >=
+ reinterpret_cast<const char *>(eocd))
+ return false;
+ }
+ }
+
+ return false;
+}
+
+} // end anonymous namespace
+
+bool ZipFile::Find(lldb::DataBufferSP zip_data, const llvm::StringRef file_path,
+ lldb::offset_t &file_offset, lldb::offset_t &file_size) {
+ const EocdRecord *eocd = FindEocdRecord(zip_data);
+ if (!eocd)
+ return false;
+
+ return FindFile(zip_data, eocd, file_path, file_offset, file_size);
+}