Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions patches/libarchive/select-registered-only.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
--- a/libarchive/archive_read_set_format.c
+++ b/libarchive/archive_read_set_format.c
@@ -40,9 +40,12 @@
const char *str;
struct archive_read *a = (struct archive_read *)_a;

- if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK))
- return r1;
-
+ /*
+ * BUN PATCH: select only among formats the caller already registered.
+ * Upstream first calls archive_read_support_format_by_code(), whose
+ * switch references every reader, so the whole reader set (7zip, rar,
+ * zip, ...) stays linked even though bun only ever registers tar.
+ */
r1 = r2 = (ARCHIVE_OK);
if (a->format)
r2 = (ARCHIVE_WARN);
@@ -108,7 +111,7 @@
if (!a->format->name || strcmp(a->format->name, str))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Internal error: Unable to set format");
+ "Format is not registered");
r1 = (ARCHIVE_FATAL);
}

--- a/libarchive/archive_read_append_filter.c
+++ b/libarchive/archive_read_append_filter.c
@@ -42,7 +42,12 @@
struct archive_read_filter *filter;
struct archive_read *a = (struct archive_read *)_a;

- r2 = (ARCHIVE_OK);
+ /*
+ * BUN PATCH: append only a filter the caller already registered.
+ * Upstream registers it here via archive_read_support_filter_<code>(),
+ * which keeps every filter linked; bun only ever appends gzip.
+ */
+ r1 = r2 = (ARCHIVE_OK);
switch (code)
{
case ARCHIVE_FILTER_NONE:
@@ -50,19 +55,15 @@
* NOTE: An initial "NONE" type filter is always set at the end of the
* filter chain.
*/
- r1 = (ARCHIVE_OK);
break;
case ARCHIVE_FILTER_GZIP:
strcpy(str, "gzip");
- r1 = archive_read_support_filter_gzip(_a);
break;
case ARCHIVE_FILTER_BZIP2:
strcpy(str, "bzip2");
- r1 = archive_read_support_filter_bzip2(_a);
break;
case ARCHIVE_FILTER_COMPRESS:
strcpy(str, "compress (.Z)");
- r1 = archive_read_support_filter_compress(_a);
break;
case ARCHIVE_FILTER_PROGRAM:
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
@@ -70,43 +71,33 @@
return (ARCHIVE_FATAL);
case ARCHIVE_FILTER_LZMA:
strcpy(str, "lzma");
- r1 = archive_read_support_filter_lzma(_a);
break;
case ARCHIVE_FILTER_XZ:
strcpy(str, "xz");
- r1 = archive_read_support_filter_xz(_a);
break;
case ARCHIVE_FILTER_UU:
strcpy(str, "uu");
- r1 = archive_read_support_filter_uu(_a);
break;
case ARCHIVE_FILTER_RPM:
strcpy(str, "rpm");
- r1 = archive_read_support_filter_rpm(_a);
break;
case ARCHIVE_FILTER_LZ4:
strcpy(str, "lz4");
- r1 = archive_read_support_filter_lz4(_a);
break;
case ARCHIVE_FILTER_ZSTD:
strcpy(str, "zstd");
- r1 = archive_read_support_filter_zstd(_a);
break;
case ARCHIVE_FILTER_LZIP:
strcpy(str, "lzip");
- r1 = archive_read_support_filter_lzip(_a);
break;
case ARCHIVE_FILTER_LZOP:
strcpy(str, "lzop");
- r1 = archive_read_support_filter_lzop(_a);
break;
case ARCHIVE_FILTER_LRZIP:
strcpy(str, "lrzip");
- r1 = archive_read_support_filter_lrzip(_a);
break;
case ARCHIVE_FILTER_GRZIP:
strcpy(str, "grzip");
- r1 = archive_read_support_filter_grzip(_a);
break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
@@ -127,7 +118,7 @@
if (!bidder->name || strcmp(bidder->name, str))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Internal error: Unable to append filter");
+ "Filter is not registered");
return (ARCHIVE_FATAL);
}

48 changes: 20 additions & 28 deletions scripts/build/deps/libarchive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,32 @@ import { depBuildDir } from "../source.ts";

const LIBARCHIVE_COMMIT = "ded82291ab41d5e355831b96b0e1ff49e24d8939";

// The unconditional list from libarchive/CMakeLists.txt + the two blake2
// reference impls (added when libb2 isn't linked, which it never is here).
// All formats/filters compile even though most are stubbed at runtime —
// bun's bindings call archive_read_support_{format,filter}_all, which
// reference every registration symbol.
// The unconditional list from libarchive/CMakeLists.txt, minus the read
// formats/filters bun never registers (it only registers tar/gnutar/gzip,
// and select-registered-only.patch stops set_format/append_filter from
// pulling the rest in). Kept even though unused: format_empty/format_raw
// (archive_match.c), ppmd7 (write_set_format_7zip), filter_program
// (append_filter_program_signature) — dead code that other kept files
// still reference by name.
// prettier-ignore
const SOURCES = [
"archive_acl", "archive_check_magic", "archive_cmdline", "archive_cryptor",
"archive_digest", "archive_entry", "archive_entry_copy_stat",
"archive_entry_link_resolver", "archive_entry_sparse", "archive_entry_stat",
"archive_entry_strmode", "archive_entry_xattr", "archive_hmac", "archive_match",
"archive_options", "archive_pack_dev", "archive_parse_date", "archive_pathmatch",
"archive_ppmd8", "archive_ppmd7", "archive_random", "archive_rb", "archive_read",
"archive_ppmd7", "archive_random", "archive_rb", "archive_read",
"archive_read_add_passphrase", "archive_read_append_filter",
"archive_read_data_into_fd", "archive_read_disk_entry_from_file",
"archive_read_disk_posix", "archive_read_disk_set_standard_lookup",
"archive_read_extract", "archive_read_extract2", "archive_read_open_fd",
"archive_read_open_file", "archive_read_open_filename", "archive_read_open_memory",
"archive_read_set_format", "archive_read_set_options",
"archive_read_support_filter_all", "archive_read_support_filter_by_code",
"archive_read_support_filter_bzip2", "archive_read_support_filter_compress",
"archive_read_support_filter_gzip", "archive_read_support_filter_grzip",
"archive_read_support_filter_lrzip", "archive_read_support_filter_lz4",
"archive_read_support_filter_lzop", "archive_read_support_filter_none",
"archive_read_support_filter_program", "archive_read_support_filter_rpm",
"archive_read_support_filter_uu", "archive_read_support_filter_xz",
"archive_read_support_filter_zstd", "archive_read_support_format_7zip",
"archive_read_support_format_all", "archive_read_support_format_ar",
"archive_read_support_format_by_code", "archive_read_support_format_cab",
"archive_read_support_format_cpio", "archive_read_support_format_empty",
"archive_read_support_format_iso9660", "archive_read_support_format_lha",
"archive_read_support_format_mtree", "archive_read_support_format_rar",
"archive_read_support_format_rar5", "archive_read_support_format_raw",
"archive_read_support_format_tar", "archive_read_support_format_warc",
"archive_read_support_format_xar", "archive_read_support_format_zip",
"archive_string", "archive_string_sprintf", "archive_time", "archive_util",
"archive_version_details", "archive_virtual", "archive_write",
"archive_write_disk_posix", "archive_write_disk_set_standard_lookup",
"archive_read_support_filter_gzip", "archive_read_support_filter_program",
"archive_read_support_format_empty", "archive_read_support_format_raw",
"archive_read_support_format_tar", "archive_string", "archive_string_sprintf",
"archive_time", "archive_util", "archive_version_details", "archive_virtual",
"archive_write", "archive_write_disk_posix", "archive_write_disk_set_standard_lookup",
"archive_write_open_fd", "archive_write_open_file", "archive_write_open_filename",
"archive_write_open_memory", "archive_write_add_filter",
"archive_write_add_filter_b64encode", "archive_write_add_filter_by_name",
Expand All @@ -78,9 +66,8 @@ const SOURCES = [
"archive_write_set_format_shar", "archive_write_set_format_ustar",
"archive_write_set_format_v7tar", "archive_write_set_format_warc",
"archive_write_set_format_xar", "archive_write_set_format_zip",
"archive_write_set_options", "archive_write_set_passphrase",
"filter_fork_posix", "xxhash",
"archive_blake2sp_ref", "archive_blake2s_ref",
"archive_write_set_options", "archive_write_set_passphrase", "filter_fork_posix",
"xxhash",
];

const SOURCES_WIN = [
Expand All @@ -103,6 +90,11 @@ export const libarchive: Dependency = {

patches: [
"patches/libarchive/archive_write_add_filter_gzip.c.patch",
// archive_read_set_format/append_filter register by code through a
// switch over every reader/filter, which keeps them all linked. Bun
// registers tar/gnutar/gzip itself, so make those calls only select
// an already-registered one.
"patches/libarchive/select-registered-only.patch",
// Propagate ARCHIVE_RETRY from the client read callback up through
// the gzip filter and tar reader so the worker-thread extract loop
// in `bun install` can yield and resume as HTTP chunks arrive. See
Expand Down
17 changes: 9 additions & 8 deletions src/install/TarballStream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,19 +616,20 @@ impl TarballStream {
let archive = lib::ReadArchive::new();
// Bypass bidding entirely: the stream is always gzip → tar, and
// bidding would try to read-ahead before any bytes have arrived.
// With patches/libarchive/select-registered-only.patch,
// archive_read_append_filter / archive_read_set_format only select a
// filter/format that was already registered, so register gzip and tar
// first. Tar must also be registered before read_set_options (so the
// option has a slot) and set_format must come after it: libarchive's
// archive_set_format_option() clobbers `a->format` while dispatching,
// and losing the selected format means archive_read_open1() falls
// back to bidding, which fails when the first HTTP chunk is short.
// ARCHIVE_FILTER_GZIP = 1, ARCHIVE_FORMAT_TAR = 0x30000.
let _ = archive.read_support_filter_gzip();
// SAFETY: archive is a valid non-null handle from read_new(); FFI call has no other preconditions.
if unsafe { lib::archive_read_append_filter(archive.as_mut_ptr(), 1) } != 0 {
return Err(crate::Error::Fail);
}
// Register tar before read_set_options so the option has a format slot
// to apply to. archive_read_set_format would register it too, but
// libarchive's archive_set_format_option() overwrites `a->format` with
// each slot while dispatching and then writes NULL, so calling
// read_set_options after archive_read_set_format throws the selected
// format away and archive_read_open1() falls back to bidding. Bidding
// reads ahead 512 decompressed bytes and fails with "Unrecognized
// archive format" when the first HTTP chunk is too small for that.
let _ = archive.read_support_format_tar();
let _ = archive.read_set_options(c"read_concatenated_archives");
// SAFETY: archive is a valid non-null handle from read_new(); FFI call has no other preconditions.
Expand Down
Loading