cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
181 lines
6.7 KiB
C++
181 lines
6.7 KiB
C++
//===-- 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);
|
|
}
|