diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 132541b4..1d45057d 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -41,7 +41,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index fa324ed3..bbee991e 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -28,7 +28,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: recursive
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
index 8e1df007..d59adfd5 100644
--- a/.github/workflows/mingw.yml
+++ b/.github/workflows/mingw.yml
@@ -54,7 +54,7 @@ jobs:
upx
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: recursive
diff --git a/.github/workflows/setup.yml b/.github/workflows/setup.yml
index 0ff81dc5..c0fc073a 100644
--- a/.github/workflows/setup.yml
+++ b/.github/workflows/setup.yml
@@ -19,7 +19,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v3
diff --git a/.github/workflows/vs2026.yml b/.github/workflows/vs2026.yml
index 2c368afb..371f4d5a 100644
--- a/.github/workflows/vs2026.yml
+++ b/.github/workflows/vs2026.yml
@@ -40,7 +40,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: recursive
diff --git a/.vs/bled.vcxproj b/.vs/bled.vcxproj
index 7d57bbd6..de587bfb 100644
--- a/.vs/bled.vcxproj
+++ b/.vs/bled.vcxproj
@@ -28,6 +28,7 @@
+
@@ -161,7 +162,7 @@
Level3
ProgramDatabase
Disabled
- _OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;_CRTDBG_MAP_ALLOC;%(PreprocessorDefinitions)
+ BLED_EXPECT_DISK_IMAGE;_OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;_CRTDBG_MAP_ALLOC;%(PreprocessorDefinitions)
MultiThreadedDebug
..\src
CompileAsC
@@ -180,7 +181,7 @@
Level3
ProgramDatabase
Disabled
- _OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;_CRTDBG_MAP_ALLOC;%(PreprocessorDefinitions)
+ BLED_EXPECT_DISK_IMAGE;_OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;_CRTDBG_MAP_ALLOC;%(PreprocessorDefinitions)
MultiThreadedDebug
..\src
/utf-8 %(AdditionalOptions)
@@ -199,7 +200,7 @@
ProgramDatabase
EnableFastChecks
Disabled
- _OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;_CRTDBG_MAP_ALLOC;%(PreprocessorDefinitions)
+ BLED_EXPECT_DISK_IMAGE;_OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;_CRTDBG_MAP_ALLOC;%(PreprocessorDefinitions)
MultiThreadedDebug
..\src
CompileAsC
@@ -216,7 +217,7 @@
Level3
- _OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;%(PreprocessorDefinitions)
+ BLED_EXPECT_DISK_IMAGE;_OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;%(PreprocessorDefinitions)
MultiThreaded
..\src
CompileAsC
@@ -237,7 +238,7 @@
Level3
true
- _OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;%(PreprocessorDefinitions)
+ BLED_EXPECT_DISK_IMAGE;_OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;%(PreprocessorDefinitions)
MultiThreaded
..\src
/utf-8 %(AdditionalOptions)
@@ -256,7 +257,7 @@
Level3
- _OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;%(PreprocessorDefinitions)
+ BLED_EXPECT_DISK_IMAGE;_OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;%(PreprocessorDefinitions)
MultiThreaded
..\src
CompileAsC
diff --git a/.vs/bled.vcxproj.filters b/.vs/bled.vcxproj.filters
index abbc6ed8..d963ee37 100644
--- a/.vs/bled.vcxproj.filters
+++ b/.vs/bled.vcxproj.filters
@@ -117,6 +117,9 @@
Source Files
+
+ Source Files
+
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 3b37f441..caa86595 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,10 +1,12 @@
-o Version 4.15 (2026.06.??)
- Improve the guards for using the "silent" option
+o Version 4.15 (2026.06.30)
+ Add RISC-V 64 support to UEFI:NTFS
+ Improve the guards for using the "silent" Windows installation option
Improve the ability to cancel during write retries
+ Improve progress reporting for compressed image extraction
Fix unrestricted XML entity expansion and integer overflow in ezxml parser (courtesy of Eric Sadowski)
Fix "silent" Windows installation failing at 75% in most cases
Fix a crash during boot when using UEFI:NTFS on Snapdragon X based ARM64 platforms
- Fix the first WUE option always being checked by default
+ Fix first WUE option always being checked by default
Fix an infinite loop when using Windows ISOs that contain multiple WIMs
Fix "Enable runtime UEFI media validation" checkbox not always being properly enabled
Other WUE improvements/fixes for OneDrive removal and username validation (with thanks to @christian8641)
diff --git a/res/appstore/listing/listing.csv b/res/appstore/listing/listing.csv
index 2880ae7f..dbc1c806 100644
--- a/res/appstore/listing/listing.csv
+++ b/res/appstore/listing/listing.csv
@@ -65,7 +65,7 @@
• Pakeitimų žurnalas: https://github.com/pbatard/rufus/blob/master/ChangeLog.txt","Rufus adalah utiliti yang membantu memformat dan mencipta pemacu kilat USB boot, seperti pemacu pen/kekunci USB, kayu memori, dan lain-lain.
• Laman rasmi: https://rufus.ie
• Kod Sumber: https://github.com/pbatard/rufus
-• ChangeLog: https://github.com/pbatard/rufus/blob/master/ChangeLog.txt","Rufus er et verktøy som hjelper til med å formatere og lage oppstartbare USB-stasjoner, for eksempel USB-minnepinner, USB-harddisker, og lignende.
+• Log Perubahan: https://github.com/pbatard/rufus/blob/master/ChangeLog.txt","Rufus er et verktøy som hjelper til med å formatere og lage oppstartbare USB-stasjoner, for eksempel USB-minnepinner, USB-harddisker, og lignende.
• Offisiell webside: https://rufus.ie
• Kildekode: https://github.com/pbatard/rufus
• Endringslogg: https://github.com/pbatard/rufus/blob/master/ChangeLog.txt","Rufus ابزاری است که به فرمت و ایجاد درایوهای فلش USB قابل بوت، مانند کلیدهای USB/pendrives، مموری استیک ها و غیره به شما کمک می کند.
@@ -114,19 +114,17 @@
• Trang web chính thức: https://rufus.ie
• Mã nguồn: https://github.com/pbatard/rufus
• Nhật ký thay đổi: https://github.com/pbatard/rufus/blob/master/ChangeLog.txt"
-"ReleaseNotes","3","Text","• Windows User Experience improvements:
-• - Add a ""Quality of Life"" option, to disable Teams, Outlook, Copilot and other Microsoft forced nuisances
-• - Add a Silent installation option, that automatically, and WITHOUT PROMPT, installs Windows on the first detected disk
-• - Add an option to copy 'SkuSiPolicy.p7b' to the ESP on installation (please refer to KB5042562 for more info)
-• - Add tooltips for all the dialog options
-• Add limited support for El-Torito UEFI image extraction (Mostly for Dell BIOS update ISOs)
-• Improve error report when the user tries to use an image that resides on the target drive
-• Improve the UEFI:NTFS partition label to make the install media more explicit during Windows Setup disk partitioning
-• Improve support for Bazzite and other Fedora derivatives that don't follow EFI conventions
-• Improve detection and exclusion of the new Bitdefender hidden VHDs
-• Improve reporting of GRUB and Isolinux MBRs
-• Fix potential errors during creation of Windows To Go media, due to the use of new versions of bcdboot
-• Fix errors with local accounts that start or end with whitespaces",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"ReleaseNotes","3","Text","• Add RISC-V 64 support to UEFI:NTFS
+• Improve the guards for using the ""silent"" Windows installation option
+• Improve the ability to cancel during write retries
+• Improve progress reporting for compressed image extraction
+• Fix unrestricted XML entity expansion and integer overflow in ezxml parser (courtesy of Eric Sadowski)
+• Fix ""silent"" Windows installation failing at 75% in most cases
+• Fix a crash during boot when using UEFI:NTFS on Snapdragon X based ARM64 platforms
+• Fix first WUE option always being checked by default
+• Fix an infinite loop when using Windows ISOs that contain multiple WIMs
+• Fix ""Enable runtime UEFI media validation"" checkbox not always being properly enabled
+• Other WUE improvements/fixes for OneDrive removal and username validation (with thanks to @christian8641)",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
"Title","4","Text","Rufus",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
"ShortTitle","5","Text","",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
"SortTitle","6","Text","",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
diff --git a/res/uefi/readme.txt b/res/uefi/readme.txt
index 7841927f..894f11c6 100644
--- a/res/uefi/readme.txt
+++ b/res/uefi/readme.txt
@@ -7,10 +7,10 @@ This image, which can be mounted as a FAT file system or opened in 7-zip,
contains the following data:
o Secure Boot signed NTFS UEFI drivers, derived from ntfs-3g [1].
- These drivers are the exact same as the read-only binaries from release 1.8,
+ These drivers are the exact same as the read-only binaries from release 1.9,
except for the addition of Microsoft's Secure Boot signature.
-o Non Secure Boot signed exFAT (and ARM NTFS) UEFI drivers from EfiFs [2].
+o Non Secure Boot signed exFAT (and ARM/RISC NTFS) UEFI drivers from EfiFs [2].
These drivers are the exact same as the binaries from EfiFs release 1.12 but,
because they are licensed under GPLv3, cannot be Secure Boot signed.
diff --git a/res/uefi/uefi-ntfs.img b/res/uefi/uefi-ntfs.img
index 7c05fa33..ac50eed9 100644
Binary files a/res/uefi/uefi-ntfs.img and b/res/uefi/uefi-ntfs.img differ
diff --git a/src/bled/Makefile.am b/src/bled/Makefile.am
index 23b00401..3827cc51 100644
--- a/src/bled/Makefile.am
+++ b/src/bled/Makefile.am
@@ -1,10 +1,10 @@
noinst_LIBRARIES = libbled.a
-libbled_a_SOURCES = bled.c crc32.c data_align.c data_extract_all.c data_skip.c decompress_bunzip2.c \
+libbled_a_SOURCES = bled.c bled_size.c crc32.c data_align.c data_extract_all.c data_skip.c decompress_bunzip2.c \
decompress_gunzip.c decompress_uncompress.c decompress_unlzma.c decompress_unxz.c decompress_unzip.c \
decompress_unzstd.c decompress_vtsi.c filter_accept_all.c filter_accept_list.c filter_accept_reject_list.c \
find_list_entry.c fse_decompress.c header_list.c header_skip.c header_verbose_list.c huf_decompress.c \
init_handle.c open_transformer.c seek_by_jump.c seek_by_read.c xz_dec_bcj.c xz_dec_lzma2.c xz_dec_stream.c \
xxhash.c zstd_common.c zstd_decompress.c zstd_decompress_block.c zstd_ddict.c zstd_entropy_common.c \
zstd_error_private.c
-libbled_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -Wno-undef -Wno-strict-aliasing
+libbled_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -DBLED_EXPECT_DISK_IMAGE -Wno-undef -Wno-strict-aliasing
diff --git a/src/bled/Makefile.in b/src/bled/Makefile.in
index 7619dc50..f0a4c484 100644
--- a/src/bled/Makefile.in
+++ b/src/bled/Makefile.in
@@ -94,7 +94,8 @@ am__v_AR_1 =
libbled_a_AR = $(AR) $(ARFLAGS)
libbled_a_LIBADD =
am_libbled_a_OBJECTS = libbled_a-bled.$(OBJEXT) \
- libbled_a-crc32.$(OBJEXT) libbled_a-data_align.$(OBJEXT) \
+ libbled_a-bled_size.$(OBJEXT) libbled_a-crc32.$(OBJEXT) \
+ libbled_a-data_align.$(OBJEXT) \
libbled_a-data_extract_all.$(OBJEXT) \
libbled_a-data_skip.$(OBJEXT) \
libbled_a-decompress_bunzip2.$(OBJEXT) \
@@ -276,7 +277,7 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
noinst_LIBRARIES = libbled.a
-libbled_a_SOURCES = bled.c crc32.c data_align.c data_extract_all.c data_skip.c decompress_bunzip2.c \
+libbled_a_SOURCES = bled.c bled_size.c crc32.c data_align.c data_extract_all.c data_skip.c decompress_bunzip2.c \
decompress_gunzip.c decompress_uncompress.c decompress_unlzma.c decompress_unxz.c decompress_unzip.c \
decompress_unzstd.c decompress_vtsi.c filter_accept_all.c filter_accept_list.c filter_accept_reject_list.c \
find_list_entry.c fse_decompress.c header_list.c header_skip.c header_verbose_list.c huf_decompress.c \
@@ -284,7 +285,7 @@ libbled_a_SOURCES = bled.c crc32.c data_align.c data_extract_all.c data_skip.c d
xxhash.c zstd_common.c zstd_decompress.c zstd_decompress_block.c zstd_ddict.c zstd_entropy_common.c \
zstd_error_private.c
-libbled_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -Wno-undef -Wno-strict-aliasing
+libbled_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -DBLED_EXPECT_DISK_IMAGE -Wno-undef -Wno-strict-aliasing
all: all-am
.SUFFIXES:
@@ -346,6 +347,12 @@ libbled_a-bled.o: bled.c
libbled_a-bled.obj: bled.c
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libbled_a_CFLAGS) $(CFLAGS) -c -o libbled_a-bled.obj `if test -f 'bled.c'; then $(CYGPATH_W) 'bled.c'; else $(CYGPATH_W) '$(srcdir)/bled.c'; fi`
+libbled_a-bled_size.o: bled_size.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libbled_a_CFLAGS) $(CFLAGS) -c -o libbled_a-bled_size.o `test -f 'bled_size.c' || echo '$(srcdir)/'`bled_size.c
+
+libbled_a-bled_size.obj: bled_size.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libbled_a_CFLAGS) $(CFLAGS) -c -o libbled_a-bled_size.obj `if test -f 'bled_size.c'; then $(CYGPATH_W) 'bled_size.c'; else $(CYGPATH_W) '$(srcdir)/bled_size.c'; fi`
+
libbled_a-crc32.o: crc32.c
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libbled_a_CFLAGS) $(CFLAGS) -c -o libbled_a-crc32.o `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c
diff --git a/src/bled/bb_archive.h b/src/bled/bb_archive.h
index 36fca55c..2e8a6ff6 100644
--- a/src/bled/bb_archive.h
+++ b/src/bled/bb_archive.h
@@ -235,23 +235,28 @@ typedef struct transformer_state_t {
int dst_fd;
const char *dst_dir; /* if non-NULL, extract to dir */
char *dst_name;
- uint64_t dst_size;
+ int64_t src_size; /* size of the source archive */
+ int64_t dst_size; /* size of the uncompressed data, if available */
size_t mem_output_size_max; /* if non-zero, decompress to RAM instead of fd */
size_t mem_output_size;
char *mem_output_buf;
+ uint64_t bytes_total; /* used in unzip code only, for directory extraction */
uint64_t bytes_out;
- uint64_t bytes_in; /* used in unzip code only: needs to know packed size */
+ uint64_t bytes_in; /* used in unzip code only: needs to know packed size */
uint32_t crc32;
- time_t mtime; /* gunzip code may set this on exit */
+ time_t mtime; /* gunzip code may set this on exit */
- union { /* if we read magic, it's saved here */
+ union { /* if we read magic, it's saved here */
uint8_t b[8];
uint16_t b16[4];
uint32_t b32[2];
} magic;
} transformer_state_t;
+typedef int64_t(*unpacker_t)(transformer_state_t* xstate);
+typedef int64_t(*get_uncompressed_size_t)(int fd);
+int64_t get_uncompressed_size(int fd, int type);
void init_transformer_state(transformer_state_t *xstate) FAST_FUNC;
ssize_t transformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize) FAST_FUNC;
ssize_t xtransformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize) FAST_FUNC;
@@ -276,7 +281,7 @@ static inline int transformer_switch_file(transformer_state_t* xstate)
last_slash = i;
}
if (bled_switch != NULL)
- bled_switch(dst, xstate->dst_size);
+ bled_switch(dst, xstate->bytes_total);
dst[last_slash] = 0;
bb_make_directory(dst, 0, 0);
dst[last_slash] = '/';
diff --git a/src/bled/bled.c b/src/bled/bled.c
index f73508e1..c421cab5 100644
--- a/src/bled/bled.c
+++ b/src/bled/bled.c
@@ -1,7 +1,7 @@
/*
* Bled (Base Library for Easy Decompression)
*
- * Copyright © 2014-2024 Pete Batard
+ * Copyright © 2014-2026 Pete Batard
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
@@ -15,27 +15,26 @@
#include "bb_archive.h"
#include "bled.h"
-typedef long long int(*unpacker_t)(transformer_state_t *xstate);
-
/* Globals */
smallint bb_got_signal;
-uint64_t bb_total_rb;
+uint64_t bb_total_rb, bb_total_wb;
printf_t bled_printf = NULL;
read_t bled_read = NULL;
write_t bled_write = NULL;
progress_t bled_progress = NULL;
switch_t bled_switch = NULL;
unsigned long* bled_cancel_request;
-static bool bled_initialized = 0;
+bool bled_initialized = 0;
jmp_buf bb_error_jmp;
char* bb_virtual_buf = NULL;
size_t bb_virtual_len = 0, bb_virtual_pos = 0;
int bb_virtual_fd = -1;
+bool bb_progress_on_write = 0;
// ZSTD has a minimal buffer size of (1 << ZSTD_BLOCKSIZELOG_MAX) + ZSTD_blockHeaderSize = 128 KB + 3
// So we set our bufsize to 256 KB
uint32_t BB_BUFSIZE = 0x40000;
-static long long int unpack_none(transformer_state_t *xstate)
+static int64_t unpack_none(transformer_state_t *xstate)
{
bb_error_msg("This compression type is not supported");
return -1;
@@ -66,6 +65,7 @@ int64_t bled_uncompress(const char* src, const char* dst, int type)
}
bb_total_rb = 0;
+ bb_total_wb = 0;
init_transformer_state(&xstate);
xstate.src_fd = -1;
xstate.dst_fd = -1;
@@ -87,6 +87,14 @@ int64_t bled_uncompress(const char* src, const char* dst, int type)
goto err;
}
+ if (bled_progress != NULL) {
+ xstate.src_size = lseek(xstate.src_fd, 0, SEEK_END);
+ xstate.dst_size = get_uncompressed_size(xstate.src_fd, type);
+
+ bb_progress_on_write = (xstate.dst_size > 0);
+ bled_progress(bb_progress_on_write ? -xstate.dst_size : -xstate.src_size);
+ }
+
if (setjmp(bb_error_jmp))
goto err;
@@ -112,6 +120,7 @@ int64_t bled_uncompress_with_handles(HANDLE hSrc, HANDLE hDst, int type)
}
bb_total_rb = 0;
+ bb_total_wb = 0;
init_transformer_state(&xstate);
xstate.src_fd = -1;
xstate.dst_fd = -1;
@@ -133,6 +142,13 @@ int64_t bled_uncompress_with_handles(HANDLE hSrc, HANDLE hDst, int type)
return -1;
}
+ if (bled_progress != NULL) {
+ xstate.src_size = lseek(xstate.src_fd, 0, SEEK_END);
+ xstate.dst_size = get_uncompressed_size(xstate.src_fd, type);
+ bb_progress_on_write = (xstate.dst_size > 0);
+ bled_progress(bb_progress_on_write ? -xstate.dst_size : -xstate.src_size);
+ }
+
if (setjmp(bb_error_jmp))
return -1;
@@ -156,6 +172,7 @@ int64_t bled_uncompress_to_buffer(const char* src, char* buf, size_t size, int t
}
bb_total_rb = 0;
+ bb_total_wb = 0;
init_transformer_state(&xstate);
xstate.src_fd = -1;
xstate.dst_fd = -1;
@@ -179,6 +196,13 @@ int64_t bled_uncompress_to_buffer(const char* src, char* buf, size_t size, int t
goto err;
}
+ if (bled_progress != NULL) {
+ xstate.src_size = lseek(xstate.src_fd, 0, SEEK_END);
+ xstate.dst_size = get_uncompressed_size(xstate.src_fd, type);
+ bb_progress_on_write = (xstate.dst_size > 0);
+ bled_progress(bb_progress_on_write ? -xstate.dst_size : -xstate.src_size);
+ }
+
if (setjmp(bb_error_jmp))
goto err;
@@ -203,6 +227,7 @@ int64_t bled_uncompress_to_dir(const char* src, const char* dir, int type)
}
bb_total_rb = 0;
+ bb_total_wb = 0;
init_transformer_state(&xstate);
xstate.src_fd = -1;
xstate.dst_fd = -1;
@@ -221,6 +246,13 @@ int64_t bled_uncompress_to_dir(const char* src, const char* dir, int type)
goto err;
}
+ if (bled_progress != NULL) {
+ xstate.src_size = lseek(xstate.src_fd, 0, SEEK_END);
+ xstate.dst_size = 0;
+ bb_progress_on_write = 0;
+ bled_progress(-xstate.src_size);
+ }
+
if (setjmp(bb_error_jmp))
goto err;
@@ -268,6 +300,34 @@ int64_t bled_uncompress_from_buffer_to_buffer(const char* src, const size_t src_
return ret;
}
+/* Get the decompressed size of file 'src', compressed using 'type' */
+int64_t bled_get_uncompressed_size(const char* src, int type)
+{
+ int fd = -1;
+ int64_t ret = -1;
+
+ if (!bled_initialized) {
+ bb_error_msg("The library has not been initialized");
+ return -1;
+ }
+
+ if ((type < 0) || (type >= BLED_COMPRESSION_MAX)) {
+ bb_error_msg("Unsupported compression format");
+ goto err;
+ }
+
+ fd = _openU(src, _O_RDONLY | _O_BINARY, 0);
+ if (fd < 0)
+ return -1;
+
+ ret = get_uncompressed_size(fd, type);
+
+err:
+ if (fd > 0)
+ _close(fd);
+ return ret;
+}
+
/* Initialize the library.
* When the parameters are not NULL or zero you can:
* - specify the buffer size to use (must be larger than 256KB and a power of two)
diff --git a/src/bled/bled.h b/src/bled/bled.h
index 7a354cad..3524e916 100644
--- a/src/bled/bled.h
+++ b/src/bled/bled.h
@@ -1,7 +1,7 @@
/*
* Bled (Base Library for Easy Decompression)
*
- * Copyright © 2014-2024 Pete Batard
+ * Copyright © 2014-2026 Pete Batard
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
@@ -16,7 +16,7 @@
#endif
typedef void (*printf_t) (const char* format, ...);
-typedef void (*progress_t) (const uint64_t read_bytes);
+typedef void (*progress_t) (const int64_t read_bytes);
typedef int (*read_t)(int fd, void* buf, unsigned int count);
typedef int (*write_t)(int fd, const void* buf, unsigned int count);
typedef void (*switch_t)(const char* filename, const uint64_t size);
@@ -35,6 +35,9 @@ typedef enum {
BLED_COMPRESSION_MAX
} bled_compression_type;
+/* (Where possible) get the uncompressed size of file 'src', compressed using 'type' */
+int64_t bled_get_uncompressed_size(const char* src, int type);
+
/* Uncompress file 'src', compressed using 'type', to file 'dst' */
int64_t bled_uncompress(const char* src, const char* dst, int type);
diff --git a/src/bled/bled_size.c b/src/bled/bled_size.c
new file mode 100644
index 00000000..4c9dda1a
--- /dev/null
+++ b/src/bled/bled_size.c
@@ -0,0 +1,782 @@
+/*
+ * Bled (Base Library for Easy Decompression) - Size related dependencies
+ *
+ * Mostly generated by AI - No copyright assigned.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#ifdef _CRTDBG_MAP_ALLOC
+#include
+#include
+#endif
+
+#include "libbb.h"
+#include "bb_archive.h"
+#include "bled.h"
+
+/* -------------------------------------------------------------------------
+ * Internal helpers
+ * ---------------------------------------------------------------------- */
+
+#define DECOMP_SIZE_ERROR ((int64_t)-1)
+#define DECOMP_SIZE_UNKNOWN ((int64_t)-2)
+#define DECOMP_IO_ERROR ((int64_t)-3)
+
+ /** Read exactly n bytes from fd into buf; returns 0 on success, -1 on error. */
+static int read_exact(int fd, void* buf, size_t n)
+{
+ uint8_t* p = (uint8_t*)buf;
+ while (n > 0) {
+ int got = _read(fd, p, (unsigned)n);
+ if (got <= 0)
+ return -1;
+ p += got;
+ n -= (size_t)got;
+ }
+ return 0;
+}
+
+int64_t get_uncompress_size_none(int fd)
+{
+ return DECOMP_SIZE_UNKNOWN;
+}
+
+/* -------------------------------------------------------------------------
+ * PKZIP
+ *
+ * Local File Header layout (all fields little-endian):
+ *
+ * Offset Size Field
+ * 0 4 Signature (0x04034b50)
+ * 4 2 Version needed
+ * 6 2 General purpose bit flag
+ * 8 2 Compression method
+ * 10 2 Last mod file time
+ * 12 2 Last mod file date
+ * 14 4 CRC-32
+ * 18 4 Compressed size (0xFFFFFFFF → see ZIP64 Extra Field)
+ * 22 4 Uncompressed size (0xFFFFFFFF → see ZIP64 Extra Field)
+ * 26 2 File name length
+ * 28 2 Extra field length
+ * 30 ? File name
+ * 30+n ? Extra field
+ *
+ * ZIP64 Extra Field tag: 0x0001
+ * Within the ZIP64 extra field block the fields appear only when the
+ * corresponding 32-bit field in the Local Header is 0xFFFFFFFF:
+ * - Original size (8 bytes LE) ← first, if uncompressed == 0xFFFFFFFF
+ * - Compressed size (8 bytes LE) ← second, if compressed == 0xFFFFFFFF
+ * ---------------------------------------------------------------------- */
+
+#define ZIP_LFH_SIG UINT32_C(0x04034b50)
+#define ZIP_LFH_HDR_SIZE 30
+#define ZIP64_EXTRA_TAG 0x0001
+#define ZIP32_NEEDS_ZIP64 UINT32_C(0xFFFFFFFF)
+
+static int64_t get_uncompress_size_zip(int fd)
+{
+ uint8_t hdr[ZIP_LFH_HDR_SIZE];
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return DECOMP_SIZE_ERROR;
+ if (read_exact(fd, hdr, ZIP_LFH_HDR_SIZE))
+ return DECOMP_SIZE_ERROR;
+
+ /* Verify Local File Header signature. */
+ if (get_le32(hdr + 0) != ZIP_LFH_SIG)
+ return DECOMP_SIZE_ERROR;
+
+ uint32_t uncomp32 = get_le32(hdr + 22);
+ uint32_t comp32 = get_le32(hdr + 18);
+ uint16_t fname_len = get_le16(hdr + 26);
+ uint16_t extra_len = get_le16(hdr + 28);
+
+ /* Fast path: 32-bit field is valid. */
+ if (uncomp32 != ZIP32_NEEDS_ZIP64)
+ return (int64_t)uncomp32;
+
+ /* Need ZIP64 Extra Field – skip over the file name first. */
+ if (lseek(fd, (int64_t)fname_len, SEEK_CUR) < 0)
+ return DECOMP_SIZE_ERROR;
+
+ /* Walk the extra field TLV blocks looking for tag 0x0001. */
+ uint16_t remaining = extra_len;
+ while (remaining >= 4) {
+ uint8_t tag_buf[4];
+ if (read_exact(fd, tag_buf, 4))
+ return DECOMP_SIZE_ERROR;
+ remaining = (uint16_t)(remaining - 4);
+
+ uint16_t tag = get_le16(tag_buf + 0);
+ uint16_t dlen = get_le16(tag_buf + 2);
+
+ if (tag == ZIP64_EXTRA_TAG) {
+ /*
+ * Fields are present only when the corresponding 32-bit value is
+ * 0xFFFFFFFF. Uncompressed size comes first; we need 8 bytes.
+ */
+ if (uncomp32 == ZIP32_NEEDS_ZIP64) {
+ if (dlen < 8 || remaining < 8)
+ return DECOMP_SIZE_ERROR;
+ uint8_t sz[8];
+ if (read_exact(fd, sz, 8))
+ return DECOMP_SIZE_ERROR;
+ return (int64_t)get_le64(sz);
+ }
+ }
+
+ /* Skip this block. */
+ if (dlen > remaining) return DECOMP_SIZE_ERROR;
+ if (lseek(fd, (int64_t)dlen, SEEK_CUR) < 0)
+ return DECOMP_SIZE_ERROR;
+ remaining = (uint16_t)(remaining - dlen);
+ (void)comp32; /* suppress unused-variable warning */
+ }
+
+ return DECOMP_SIZE_ERROR; /* ZIP64 tag not found */
+}
+
+/* -------------------------------------------------------------------------
+ * GZIP
+ *
+ * The ISIZE field is the last 4 bytes of the file (little-endian uint32).
+ * It holds the decompressed size modulo 2^32.
+ *
+ * Header magic: 0x1F 0x8B
+ * ---------------------------------------------------------------------- */
+
+#define GZIP_MAGIC_0 0x1F
+#define GZIP_MAGIC_1 0x8B
+
+static int64_t get_uncompress_size_gz(int fd)
+{
+ /* Verify magic bytes at offset 0. */
+ uint8_t magic[2];
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return DECOMP_SIZE_ERROR;
+ if (read_exact(fd, magic, 2))
+ return DECOMP_SIZE_ERROR;
+ if (magic[0] != GZIP_MAGIC_0 || magic[1] != GZIP_MAGIC_1)
+ return DECOMP_SIZE_ERROR;
+
+ /* Seek to ISIZE: last 4 bytes of the file. */
+ if (lseek(fd, -4, SEEK_END) < 0)
+ return DECOMP_SIZE_ERROR;
+
+ uint8_t isize[4];
+ if (read_exact(fd, isize, 4))
+ return DECOMP_SIZE_ERROR;
+
+ /*
+ * ISIZE is uint32 mod 2^32. We return it as int64_t; callers that need
+ * to handle files >= 4 GiB must apply their own correction.
+ */
+ return (int64_t)(uint64_t)get_le32(isize);
+}
+
+/* -------------------------------------------------------------------------
+ * LZMA (.lzma "alone" format)
+ *
+ * Header layout:
+ * Offset Size Field
+ * 0 1 Properties byte (lc, lp, pb encoded)
+ * 1 4 Dictionary size (uint32 LE)
+ * 5 8 Uncompressed size (uint64 LE;
+ * 0xFFFFFFFFFFFFFFFF → unknown, uses EOS marker)
+ * ---------------------------------------------------------------------- */
+
+#define LZMA_HEADER_SIZE 13 /* 1 + 4 + 8 */
+#define LZMA_SIZE_UNKNOWN UINT64_C(0xFFFFFFFFFFFFFFFF)
+
+static int64_t get_uncompress_size_lzma(int fd)
+{
+ uint8_t hdr[LZMA_HEADER_SIZE];
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return DECOMP_SIZE_ERROR;
+ if (read_exact(fd, hdr, LZMA_HEADER_SIZE))
+ return DECOMP_SIZE_ERROR;
+
+ /*
+ * Basic sanity check on the properties byte:
+ * lc = props % 9, lp = (props / 9) % 5, pb = props / 45
+ * pb must be <= 4, so the byte must be < 225 (9 * 5 * 5).
+ */
+ if (hdr[0] >= 225)
+ return DECOMP_SIZE_ERROR;
+
+ uint64_t size = get_le64(hdr + 5);
+ if (size == LZMA_SIZE_UNKNOWN)
+ return DECOMP_SIZE_UNKNOWN;
+
+ return (int64_t)size;
+}
+
+/* -------------------------------------------------------------------------
+ * XZ (.xz)
+ *
+ * An XZ file is one or more Streams, each structured as:
+ *
+ * Stream Header (12 bytes)
+ * Block(s) (variable)
+ * Index (variable)
+ * Stream Footer (12 bytes)
+ *
+ * Stream Footer layout (little-endian unless noted):
+ * Offset Size Field
+ * 0 4 CRC32 of bytes 4..7
+ * 4 4 Backward Size (uint32 LE; index_size = (value+1)*4)
+ * 8 2 Stream Flags (must match Stream Header flags)
+ * 10 2 Magic (0x59 0x5A)
+ *
+ * Index layout (immediately before Stream Footer):
+ * [0] 1 Indicator byte (must be 0x00)
+ * [1] VLI Number of Records
+ * per record:
+ * VLI Unpadded Size of the compressed Block
+ * VLI Uncompressed Size of the Block ← what we want
+ * padding to 4-byte boundary
+ * 4 CRC32 of the Index
+ *
+ * VLI encoding: up to 9 bytes; each byte contributes 7 bits (little-endian
+ * bit order); the MSB is a continuation flag (1 = more bytes follow).
+ * Maximum value: 2^63 - 1 (high bit of the 9th byte must be 0).
+ * ---------------------------------------------------------------------- */
+
+#define XZ_STREAM_FOOTER_SIZE 12
+#define XZ_MAGIC_0 0xFDu
+#define XZ_MAGIC_1 0x37u
+#define XZ_MAGIC_2 0x7Au
+#define XZ_MAGIC_3 0x58u
+#define XZ_MAGIC_4 0x5Au
+#define XZ_MAGIC_5 0x00u
+#define XZ_FOOTER_MAGIC_0 0x59u
+#define XZ_FOOTER_MAGIC_1 0x5Au
+
+ /**
+ * Decode one XZ/LZMA2 VLI from buf[*pos], advancing *pos.
+ * Returns the value, or UINT64_MAX on error (overlong / overflow).
+ */
+static uint64_t xz_read_vli(const uint8_t* buf, size_t buf_len, size_t* pos)
+{
+ uint64_t val = 0;
+ int shift = 0;
+
+ for (;;) {
+ if (*pos >= buf_len) return UINT64_MAX; /* truncated */
+ if (shift >= 63) return UINT64_MAX; /* overlong */
+
+ uint8_t b = buf[(*pos)++];
+ val |= (uint64_t)(b & 0x7Fu) << shift;
+ shift += 7;
+
+ if (!(b & 0x80u))
+ return val;
+ }
+}
+
+static int64_t get_uncompress_size_xz(int fd)
+{
+ /* ---- Verify Stream Header magic (6 bytes at offset 0) ---- */
+ uint8_t hdr_magic[6];
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return DECOMP_SIZE_ERROR;
+ if (read_exact(fd, hdr_magic, 6))
+ return DECOMP_SIZE_ERROR;
+ if (hdr_magic[0] != XZ_MAGIC_0 || hdr_magic[1] != XZ_MAGIC_1 ||
+ hdr_magic[2] != XZ_MAGIC_2 || hdr_magic[3] != XZ_MAGIC_3 ||
+ hdr_magic[4] != XZ_MAGIC_4 || hdr_magic[5] != XZ_MAGIC_5)
+ return DECOMP_SIZE_ERROR;
+
+ /* ---- Read Stream Footer ---- */
+ if (lseek(fd, -(int64_t)XZ_STREAM_FOOTER_SIZE, SEEK_END) < 0)
+ return DECOMP_SIZE_ERROR;
+
+ uint8_t footer[XZ_STREAM_FOOTER_SIZE];
+ if (read_exact(fd, footer, XZ_STREAM_FOOTER_SIZE))
+ return DECOMP_SIZE_ERROR;
+
+ /* Check footer magic bytes at offset 10-11 */
+ if (footer[10] != XZ_FOOTER_MAGIC_0 || footer[11] != XZ_FOOTER_MAGIC_1)
+ return DECOMP_SIZE_ERROR;
+
+ /* Backward Size: real Index size = (backward_size + 1) * 4 */
+ uint32_t backward_size = get_le32(footer + 4);
+ size_t index_size = ((size_t)backward_size + 1u) * 4u;
+
+ /* ---- Read Index block ---- */
+ int64_t index_offset =
+ -(int64_t)XZ_STREAM_FOOTER_SIZE - (int64_t)index_size;
+ if (lseek(fd, index_offset, SEEK_END) < 0)
+ return DECOMP_SIZE_ERROR;
+
+ uint8_t* index_buf = (uint8_t*)
+ _alloca(index_size);
+ if (read_exact(fd, index_buf, index_size))
+ return DECOMP_SIZE_ERROR;
+
+ /* ---- Parse Index ---- */
+ size_t pos = 0;
+
+ /* Indicator byte must be 0x00 */
+ if (index_buf[pos++] != 0x00u)
+ return DECOMP_SIZE_ERROR;
+
+ /* Number of Records */
+ uint64_t num_records = xz_read_vli(index_buf, index_size, &pos);
+ if (num_records == UINT64_MAX || num_records == 0)
+ return DECOMP_SIZE_ERROR;
+
+ /* Sum uncompressed sizes across all records */
+ uint64_t total = 0;
+ for (uint64_t r = 0; r < num_records; r++) {
+ /* Unpadded Size (compressed) — skip it */
+ uint64_t unpadded = xz_read_vli(index_buf, index_size, &pos);
+ if (unpadded == UINT64_MAX || unpadded == 0)
+ return DECOMP_SIZE_ERROR;
+
+ /* Uncompressed Size — accumulate */
+ uint64_t uncomp = xz_read_vli(index_buf, index_size, &pos);
+ if (uncomp == UINT64_MAX)
+ return DECOMP_SIZE_ERROR;
+
+ total += uncomp;
+ }
+
+ return (int64_t)total;
+}
+
+/* -------------------------------------------------------------------------
+ * 7-ZIP (.7z)
+ *
+ * Signature Header (32 bytes at offset 0):
+ * Offset Size Field
+ * 0 6 Signature (0x37 0x7A 0xBC 0xAF 0x27 0x1C)
+ * 6 1 Major version
+ * 7 1 Minor version
+ * 8 4 CRC32 of bytes 12..27
+ * 12 8 NextHeader Offset (uint64 LE, relative to offset 32)
+ * 20 8 NextHeader Size (uint64 LE)
+ * 28 4 NextHeader CRC32
+ *
+ * The End Header lives at file offset 32 + NextHeaderOffset.
+ * Its property-block stream (for a plain, non-encoded-header archive) is:
+ *
+ * kHeader (0x01)
+ * kMainStreamsInfo (0x04)
+ * kPackInfo (0x06)
+ * 7z-num: PackPos
+ * 7z-num: NumPackStreams
+ * kSize (0x09)
+ * NumPackStreams × 7z-num packed sizes
+ * kEnd (0x00)
+ * kUnPackInfo (0x07)
+ * kFolder (0x0b)
+ * 7z-num: NumFolders (must be 1 for single-file)
+ * byte: External (0 = inline folder data)
+ * [inline] 7z-num: NumCoders
+ * per coder:
+ * byte: flags (bits[3:0]=CodecIdSize, bit4=IsComplex, bit5=HasAttrs)
+ * CodecIdSize bytes: Codec ID
+ * if IsComplex: 7z-num NumInStreams, 7z-num NumOutStreams
+ * if HasAttrs: 7z-num AttrSize, AttrSize bytes Attributes
+ * [if NumCoders>1] BindPairs, PackedStreams (skipped here)
+ * kCodersUnPackSize (0x0c)
+ * 7z-num: uncompressed size ← what we want
+ * [optional kCRC (0x0a)]
+ * kEnd (0x00)
+ * [optional kSubStreamsInfo (0x08)]
+ * kEnd (0x00)
+ * [kFilesInfo (0x05) ...]
+ * kEnd (0x00)
+ *
+ * 7z variable-length number encoding:
+ * The first byte's leading 1-bits count how many extra bytes follow.
+ * extra=0 → value in bits[6:0] of first byte (7 bits)
+ * extra=1 → bits[5:0] of first byte + 8 bits of second (14 bits)
+ * ...
+ * extra=8 → 0xFF marker + full uint64 in next 8 bytes
+ * ---------------------------------------------------------------------- */
+
+static const uint8_t SZ_SIG[6] = { 0x37,0x7A,0xBC,0xAF,0x27,0x1C };
+
+#define SZ_kEnd 0x00u
+#define SZ_kHeader 0x01u
+#define SZ_kMainStreamsInfo 0x04u
+#define SZ_kPackInfo 0x06u
+#define SZ_kUnPackInfo 0x07u
+#define SZ_kSize 0x09u
+#define SZ_kFolder 0x0Bu
+#define SZ_kCodersUnPackSize 0x0Cu
+#define SZ_kEncodedHeader 0x17u
+
+/**
+ * Decode one 7z variable-length number from buf[*pos], advancing *pos.
+ *
+ * Algorithm from the 7-zip SDK (CPP/7zip/Archive/7z/7zIn.cpp ReadNumber):
+ * mask starts at 0x80 and shifts right each iteration.
+ * While (firstByte & mask) != 0: read one more byte into low bits of value.
+ * When the stop bit is found: add (firstByte & (mask-1)) into the
+ * appropriately shifted position.
+ *
+ * Returns UINT64_MAX on error (buffer overrun).
+ */
+static uint64_t sz_read_num(const uint8_t* buf, size_t buf_len, size_t* pos)
+{
+ if (*pos >= buf_len) return UINT64_MAX;
+
+ uint8_t first = buf[(*pos)++];
+ uint8_t mask = 0x80u;
+ uint64_t value = 0;
+
+ for (int i = 0; i < 8; i++) {
+ if ((first & mask) == 0) {
+ /* Stop bit found: the remaining bits of first contribute here */
+ uint64_t high_part = first & (uint64_t)(mask - 1u);
+ value += high_part << (i * 8);
+ return value;
+ }
+ /* Consume one more byte into bits [i*8 .. i*8+7] of value */
+ if (*pos >= buf_len) return UINT64_MAX;
+ value |= (uint64_t)buf[(*pos)++] << (i * 8);
+ mask = (uint8_t)(mask >> 1);
+ }
+ /* first == 0xFF: all 8 extra bytes consumed, value is complete */
+ return value;
+}
+
+static int64_t get_uncompress_size_7zip(int fd)
+{
+ /* ---- Read & verify Signature Header (32 bytes) ---- */
+ uint8_t sig_hdr[32];
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return DECOMP_SIZE_ERROR;
+ if (read_exact(fd, sig_hdr, 32))
+ return DECOMP_SIZE_ERROR;
+ if (memcmp(sig_hdr, SZ_SIG, 6) != 0)
+ return DECOMP_SIZE_ERROR;
+
+ uint64_t nh_offset = get_le64(sig_hdr + 12);
+ uint64_t nh_size = get_le64(sig_hdr + 20);
+
+ if (nh_size == 0 || nh_size > (uint64_t)64 * 1024 * 1024)
+ return DECOMP_SIZE_ERROR; /* sanity cap: 64 MiB */
+
+ /* ---- Read End Header ---- */
+ int64_t eh_abs = (int64_t)(32u + nh_offset);
+ if (lseek(fd, eh_abs, SEEK_SET) < 0) return DECOMP_SIZE_ERROR;
+
+ uint8_t* eh = (uint8_t*)
+ _alloca((size_t)nh_size);
+ if (read_exact(fd, eh, (size_t)nh_size)) return DECOMP_SIZE_ERROR;
+
+ size_t pos = 0;
+ size_t len = (size_t)nh_size;
+
+ /*
+ * Helpers that operate on (eh, len, pos) in the enclosing scope.
+ * We avoid macros that embed do-while inside comma-expressions (not
+ * valid in C99); instead use explicit checks before each read.
+ */
+#define SZ_NEED(n) \
+ do { if (pos + (size_t)(n) > len) return DECOMP_SIZE_ERROR; } while (0)
+#define SZ_BYTE(var) \
+ do { SZ_NEED(1); (var) = eh[pos++]; } while (0)
+#define SZ_NUM(var) \
+ do { (var) = sz_read_num(eh, len, &pos); \
+ if ((var) == UINT64_MAX) return DECOMP_SIZE_ERROR; } while (0)
+#define SZ_SKIP(n) \
+ do { SZ_NEED(n); pos += (size_t)(n); } while (0)
+#define SZ_EXPECT(tag) \
+ do { uint8_t _b; SZ_BYTE(_b); \
+ if (_b != (tag)) return DECOMP_SIZE_ERROR; } while (0)
+
+ uint8_t first_tag;
+ uint64_t num_pack, num_folders, num_coders, unpack_size;
+
+ /* Expect kHeader */
+ SZ_EXPECT(SZ_kHeader);
+
+ /* kEncodedHeader (0x17) means the real header is itself compressed;
+ * that requires decompressing a sub-stream, which we do not support. */
+ SZ_NEED(1);
+ if (eh[pos] == SZ_kEncodedHeader)
+ return DECOMP_SIZE_ERROR;
+
+ SZ_EXPECT(SZ_kMainStreamsInfo);
+ SZ_EXPECT(SZ_kPackInfo);
+
+ /* PackPos (discard) then NumPackStreams */
+ { uint64_t dummy; SZ_NUM(dummy); (void)dummy; }
+ SZ_NUM(num_pack);
+
+ /* kSize: skip NumPackStreams packed-size VLIs */
+ SZ_EXPECT(SZ_kSize);
+ for (uint64_t i = 0; i < num_pack; i++) {
+ uint64_t dummy; SZ_NUM(dummy); (void)dummy;
+ }
+ SZ_EXPECT(SZ_kEnd); /* closes kPackInfo */
+
+ SZ_EXPECT(SZ_kUnPackInfo);
+ SZ_EXPECT(SZ_kFolder);
+
+ SZ_NUM(num_folders);
+ if (num_folders != 1)
+ return DECOMP_SIZE_ERROR; /* only single-folder archives supported */
+
+ {
+ uint8_t external; SZ_BYTE(external);
+ if (external != 0)
+ return DECOMP_SIZE_ERROR;
+ } /* external not supported */
+
+/* Parse the inline folder record to skip past coder descriptors */
+ SZ_NUM(num_coders);
+ if (num_coders == 0) return DECOMP_SIZE_ERROR;
+
+ for (uint64_t c = 0; c < num_coders; c++) {
+ uint8_t flags;
+ SZ_BYTE(flags);
+ uint8_t id_size = flags & 0x0Fu;
+ int is_complex = (flags >> 4) & 1;
+ int has_attrs = (flags >> 5) & 1;
+
+ SZ_SKIP(id_size); /* Codec ID bytes */
+
+ if (is_complex) {
+ uint64_t dummy;
+ SZ_NUM(dummy); (void)dummy; /* NumInStreams */
+ SZ_NUM(dummy); (void)dummy; /* NumOutStreams */
+ }
+ if (has_attrs) {
+ uint64_t attr_size; SZ_NUM(attr_size);
+ SZ_SKIP(attr_size);
+ }
+ }
+
+ /* Multi-coder folders have BindPairs and possibly PackedStreams indices */
+ if (num_coders > 1) {
+ for (uint64_t i = 0; i < num_coders - 1; i++) {
+ uint64_t dummy;
+ SZ_NUM(dummy); (void)dummy; /* InIndex */
+ SZ_NUM(dummy); (void)dummy; /* OutIndex */
+ }
+ if (num_pack > num_coders - 1) {
+ uint64_t n = num_pack - (num_coders - 1);
+ for (uint64_t i = 0; i < n; i++) {
+ uint64_t dummy; SZ_NUM(dummy); (void)dummy;
+ }
+ }
+ }
+
+ /* kCodersUnPackSize: one VLI per output stream (single folder → one value) */
+ SZ_NEED(1);
+ first_tag = eh[pos++];
+ if (first_tag != SZ_kCodersUnPackSize) return DECOMP_SIZE_ERROR;
+
+ SZ_NUM(unpack_size);
+ return (int64_t)unpack_size;
+
+#undef SZ_NEED
+#undef SZ_BYTE
+#undef SZ_NUM
+#undef SZ_SKIP
+#undef SZ_EXPECT
+}
+
+/* -------------------------------------------------------------------------
+ * Ventoy Sparse Image (.vsti)
+ * ---------------------------------------------------------------------- */
+
+static int64_t get_uncompress_size_vtsi(int fd)
+{
+ if (lseek(fd, -512 + 8 + 4, SEEK_END) < 0)
+ return DECOMP_SIZE_ERROR;
+
+ uint8_t isize[8];
+ if (read_exact(fd, isize, 8))
+ return DECOMP_SIZE_ERROR;
+
+ return (int64_t)get_le64(isize);
+}
+
+/* -------------------------------------------------------------------------
+ * Zstandard (.zst)
+ *
+ * Frame layout:
+ * Offset Size Field
+ * 0 4 Magic (0xFD2FB528, LE)
+ * 4 1 Frame Header Descriptor (FHD)
+ * 5 0/1 Window_Descriptor (absent when Single_Segment_Flag set)
+ * ? 0/1/2/4/8 Content_Size
+ *
+ * FHD bits:
+ * [7:6] Frame_Content_Size_Flag (FCS_Flag)
+ * [5] Single_Segment_Flag (SS_Flag)
+ * [4] reserved (must be 0)
+ * [3] Content_Checksum_Flag
+ * [2] reserved (must be 0)
+ * [1:0] Dictionary_ID_Flag
+ *
+ * Content_Size size from FCS_Flag:
+ * 0 → 0 bytes if SS_Flag==0 (unknown), or 1 byte if SS_Flag==1
+ * 1 → 2 bytes (value + 256, to avoid overlap with 1-byte encoding)
+ * 2 → 4 bytes
+ * 3 → 8 bytes
+ * ---------------------------------------------------------------------- */
+
+#define ZSTD_MAGIC UINT32_C(0xFD2FB528)
+#define ZSTD_MAGIC_SIZE 4
+
+static int64_t get_uncompress_size_zstd(int fd)
+{
+ uint8_t buf[4 + 1 + 1]; /* magic + FHD + optional Window_Descriptor */
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return DECOMP_SIZE_ERROR;
+ if (read_exact(fd, buf, 6))
+ return DECOMP_SIZE_ERROR;
+
+ if (get_le32(buf) != ZSTD_MAGIC)
+ return DECOMP_SIZE_ERROR;
+
+ uint8_t fhd = buf[4];
+ int fcs_flag = (fhd >> 6) & 0x03;
+ int ss_flag = (fhd >> 5) & 0x01;
+
+ /* Window_Descriptor is present only when Single_Segment_Flag == 0.
+ * buf[5] already consumed it; if SS_Flag==1 that byte is Content_Size[0].
+ * We'll re-seek to the correct offset to avoid confusion. */
+
+ /* Determine Content_Size field size. */
+ int cs_bytes;
+ if (fcs_flag == 0) {
+ if (ss_flag == 0)
+ return DECOMP_SIZE_UNKNOWN; /* field absent */
+ cs_bytes = 1;
+ } else if (fcs_flag == 1) {
+ cs_bytes = 2;
+ } else if (fcs_flag == 2) {
+ cs_bytes = 4;
+ } else {
+ cs_bytes = 8;
+ }
+
+ /*
+ * Content_Size starts at:
+ * offset 5 if Single_Segment_Flag == 1 (no Window_Descriptor)
+ * offset 6 if Single_Segment_Flag == 0 (Window_Descriptor present)
+ */
+ int64_t cs_offset = (ss_flag ? 5 : 6);
+
+ if (lseek(fd, cs_offset, SEEK_SET) < 0)
+ return DECOMP_SIZE_ERROR;
+
+ uint8_t cs_buf[8] = { 0 };
+ if (read_exact(fd, cs_buf, (size_t)cs_bytes))
+ return DECOMP_SIZE_ERROR;
+
+ uint64_t size;
+ switch (cs_bytes) {
+ case 1:
+ size = cs_buf[0];
+ break;
+ case 2:
+ /*
+ * 2-byte encoding: actual value = field_value + 256
+ * (reserves 0-255 for the 1-byte SS_Flag path)
+ */
+ size = (uint64_t)get_le16(cs_buf) + 256U;
+ break;
+ case 4:
+ size = get_le32(cs_buf);
+ break;
+ default: /* 8 */
+ size = get_le64(cs_buf);
+ break;
+ }
+
+ return (int64_t)size;
+}
+
+#if defined(BLED_EXPECT_DISK_IMAGE)
+extern progress_t bled_progress;
+extern unpacker_t unpacker[BLED_COMPRESSION_MAX];
+int64_t get_uncompressed_size_from_disk_image(int fd, int type)
+{
+ progress_t old_bled_progress = bled_progress;
+ int64_t size = DECOMP_SIZE_UNKNOWN;
+ uint8_t buf[8192];
+ transformer_state_t xstate;
+
+ bled_progress = NULL; /* Can't have progress on this operation */
+ init_transformer_state(&xstate);
+ xstate.src_fd = fd;
+ xstate.dst_fd = -1;
+ xstate.mem_output_buf = buf;
+ xstate.mem_output_size = 0;
+ xstate.mem_output_size_max = sizeof(buf);
+
+ if (setjmp(bb_error_jmp))
+ goto out;
+
+ lseek(fd, 0, SEEK_SET);
+ size = unpacker[type](&xstate);
+ if (size != sizeof(buf) || buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
+ size = DECOMP_SIZE_UNKNOWN;
+ goto out;
+ }
+ if (buf[0x1be] == 0x00 && buf[0x1c2] == 0xee) {
+ /*
+ * Protective EFI MBR => look for the primary GPT, which'll also give us the sector size
+ * The address of the backup GPT is assumed to be the last LBA for the disk
+ */
+ if (memcmp("EFI PART", &buf[0x200], 8) == 0)
+ size = (get_le64(&buf[0x220]) + 1) * 512;
+ else if (memcmp("EFI PART", &buf[0x1000], 8) == 0)
+ size = (get_le64(&buf[0x1020]) + 1) * 4096;
+ else
+ bb_error_msg("Could not locate primary GPT");
+ } else if (buf[0x1be] == 0x80) {
+ /* Regular bootable MBR => compute the max LBA (using primary partitions only) */
+ uint32_t max_lba = 1;
+ for (int i = 0; i < 4; i++)
+ max_lba = MAX(max_lba, get_le32(&buf[0x1be + 0x10 * i + 8]) + get_le32(&buf[0x1be + 0x10 * i + 0xc]));
+ size = (max_lba + 1) * 512; /* assume 512-byte sectors for anything MBR based */
+ if (size < 4096)
+ size = DECOMP_SIZE_UNKNOWN;
+ }
+
+out:
+ bled_progress = old_bled_progress;
+ return size;
+}
+#endif
+
+static get_uncompressed_size_t _get_uncompressed_size[BLED_COMPRESSION_MAX] = {
+ get_uncompress_size_none,
+ get_uncompress_size_zip,
+ get_uncompress_size_none, // .Z has no decompressed size info
+ get_uncompress_size_gz,
+ get_uncompress_size_lzma,
+ get_uncompress_size_none, // .bz2 has no decompressed size info
+ get_uncompress_size_xz,
+ get_uncompress_size_7zip,
+ get_uncompress_size_vtsi,
+ get_uncompress_size_zstd,
+};
+
+/* Get the decompressed size of file 'fd', compressed using 'type' */
+int64_t get_uncompressed_size(int fd, int type)
+{
+ int64_t size = _get_uncompressed_size[type](fd);
+#if defined(BLED_EXPECT_DISK_IMAGE)
+ if (size < 0)
+ size = get_uncompressed_size_from_disk_image(fd, type);
+#endif
+
+ /* Make sure to reset our file pointer */
+ lseek(fd, 0, SEEK_SET);
+ return size;
+}
diff --git a/src/bled/decompress_bunzip2.c b/src/bled/decompress_bunzip2.c
index 7cb08f93..9a08eb5c 100644
--- a/src/bled/decompress_bunzip2.c
+++ b/src/bled/decompress_bunzip2.c
@@ -768,7 +768,7 @@ unpack_bz2_stream(transformer_state_t *xstate)
if (check_signature16(xstate, BZIP2_MAGIC))
return -1;
- outbuf = xmalloc(IOBUF_SIZE);
+ outbuf = aligned_xmalloc(IOBUF_SIZE);
if (outbuf == NULL)
return -1;
len = 0;
@@ -834,7 +834,7 @@ unpack_bz2_stream(transformer_state_t *xstate)
release_mem:
dealloc_bunzip(bd);
- free(outbuf);
+ aligned_free(outbuf);
return i ? i : IF_DESKTOP(total_written) + 0;
}
diff --git a/src/bled/decompress_gunzip.c b/src/bled/decompress_gunzip.c
index beb7fb39..c99b0dc1 100644
--- a/src/bled/decompress_gunzip.c
+++ b/src/bled/decompress_gunzip.c
@@ -1012,7 +1012,7 @@ inflate_unzip_internal(STATE_PARAM transformer_state_t *xstate)
ssize_t nwrote;
/* Allocate all global buffers (for DYN_ALLOC option) */
- gunzip_window = xzalloc(GUNZIP_WSIZE);
+ gunzip_window = aligned_xzalloc(GUNZIP_WSIZE);
gunzip_outbuf_count = 0;
gunzip_bytes_out = 0;
gunzip_src_fd = xstate->src_fd;
@@ -1068,7 +1068,7 @@ inflate_unzip_internal(STATE_PARAM transformer_state_t *xstate)
}
ret:
/* Cleanup */
- free(gunzip_window);
+ aligned_free(gunzip_window);
free(gunzip_crc_table);
return n;
}
diff --git a/src/bled/decompress_uncompress.c b/src/bled/decompress_uncompress.c
index a995a0db..28178cf7 100644
--- a/src/bled/decompress_uncompress.c
+++ b/src/bled/decompress_uncompress.c
@@ -104,7 +104,7 @@ unpack_Z_stream(transformer_state_t *xstate)
return -1;
inbuf = xzalloc(IBUFSIZ + 64);
- outbuf = xzalloc(OBUFSIZ + 2048);
+ outbuf = aligned_xzalloc(OBUFSIZ + 2048);
htab = xzalloc(HSIZE); /* wasn't zeroed out before, maybe can xmalloc? */
codetab = xzalloc(HSIZE * sizeof(codetab[0]));
@@ -311,7 +311,7 @@ unpack_Z_stream(transformer_state_t *xstate)
retval = IF_DESKTOP(total_written) + 0;
err:
free(inbuf);
- free(outbuf);
+ aligned_free(outbuf);
free(htab);
free(codetab);
return retval;
diff --git a/src/bled/decompress_unlzma.c b/src/bled/decompress_unlzma.c
index 856193cb..d79cb463 100644
--- a/src/bled/decompress_unlzma.c
+++ b/src/bled/decompress_unlzma.c
@@ -263,8 +263,8 @@ unpack_lzma_stream(transformer_state_t *xstate)
if (header.dict_size == 0)
header.dict_size++;
- buffer_size = (uint32_t)MIN(header.dst_size, header.dict_size);
- buffer = xmalloc(buffer_size);
+ buffer_size = (uint32_t)MAX(SECTOR_ALIGNMENT, MIN(header.dst_size, header.dict_size));
+ buffer = aligned_xmalloc(buffer_size);
{
int num_probs;
@@ -528,7 +528,7 @@ unpack_lzma_stream(transformer_state_t *xstate)
}
rc_free(rc);
free(p);
- free(buffer);
+ aligned_free(buffer);
return total_written;
}
}
diff --git a/src/bled/decompress_unxz.c b/src/bled/decompress_unxz.c
index 731a77e4..45ff892c 100644
--- a/src/bled/decompress_unxz.c
+++ b/src/bled/decompress_unxz.c
@@ -49,7 +49,7 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_xz_stream(transformer_state_t *xstate
bb_error_msg_and_err("memory allocation error");
in = xmalloc(XZ_BUFSIZE);
- out = xmalloc(XZ_BUFSIZE);
+ out = aligned_xmalloc(XZ_BUFSIZE);
b.in = in;
b.in_pos = 0;
@@ -129,7 +129,7 @@ out:
err:
xz_dec_end(s);
free(in);
- free(out);
+ aligned_free(out);
if (ret == XZ_OK)
return n;
else if (ret == XZ_BUF_FULL)
diff --git a/src/bled/decompress_unzip.c b/src/bled/decompress_unzip.c
index ef8745a2..e0818128 100644
--- a/src/bled/decompress_unzip.c
+++ b/src/bled/decompress_unzip.c
@@ -399,7 +399,7 @@ static void unzip_set_xstate(transformer_state_t* xstate, zip_header_t* zip)
zip64_t* zip64;
/* Set the default sizes for non ZIP64 content */
- xstate->dst_size = zip->fmt.ucmpsize;
+ xstate->bytes_total = zip->fmt.ucmpsize;
xstate->bytes_in = zip->fmt.cmpsize;
/* Set the filename */
@@ -440,7 +440,7 @@ static void unzip_set_xstate(transformer_state_t* xstate, zip_header_t* zip)
zip64->fmt.cmpsize,
zip64->fmt.ucmpsize
);
- xstate->dst_size = zip64->fmt.ucmpsize;
+ xstate->bytes_total = zip64->fmt.ucmpsize;
xstate->bytes_in = zip64->fmt.cmpsize;
}
}
@@ -457,10 +457,10 @@ unzip_extract(zip_header_t* zip, transformer_state_t* xstate)
if (zip->fmt.method == 0) {
/* Method 0 - stored (not compressed) */
- if (xstate->dst_size) {
- bb_copyfd_exact_size(xstate->src_fd, xstate->dst_fd, xstate->dst_size);
+ if (xstate->bytes_total) {
+ bb_copyfd_exact_size(xstate->src_fd, xstate->dst_fd, xstate->bytes_total);
}
- return xstate->dst_size;
+ return xstate->bytes_total;
}
if (zip->fmt.method == 8) {
@@ -507,7 +507,7 @@ unzip_extract(zip_header_t* zip, transformer_state_t* xstate)
}
/* Validate decompression - size */
- if (n != -ENOSPC && xstate->dst_size != xstate->bytes_out) {
+ if (n != -ENOSPC && xstate->bytes_total != xstate->bytes_out) {
/* Don't die. Who knows, maybe len calculation
* was botched somewhere. After all, crc matched! */
bb_simple_error_msg("bad length");
diff --git a/src/bled/decompress_unzstd.c b/src/bled/decompress_unzstd.c
index 894ad5cb..4c6cdee7 100644
--- a/src/bled/decompress_unzstd.c
+++ b/src/bled/decompress_unzstd.c
@@ -27,8 +27,8 @@ unpack_zstd_stream_inner(transformer_state_t *xstate,
ZSTD_DStream *dctx, void *out_buff)
{
const U32 zstd_magic = ZSTD_MAGIC;
- const size_t in_allocsize = roundupsize(ZSTD_DStreamInSize(), 1024),
- out_allocsize = roundupsize(ZSTD_DStreamOutSize(), 1024);
+ const size_t in_allocsize = roundupsize(ZSTD_DStreamInSize(), SECTOR_ALIGNMENT),
+ out_allocsize = roundupsize(ZSTD_DStreamOutSize(), SECTOR_ALIGNMENT);
IF_DESKTOP(long long int total = 0;)
size_t last_result = ZSTD_error_maxCode + 1;
@@ -119,8 +119,8 @@ unpack_zstd_stream_inner(transformer_state_t *xstate,
IF_DESKTOP(long long) int FAST_FUNC
unpack_zstd_stream(transformer_state_t *xstate)
{
- const size_t in_allocsize = roundupsize(ZSTD_DStreamInSize(), 1024),
- out_allocsize = roundupsize(ZSTD_DStreamOutSize(), 1024);
+ const size_t in_allocsize = roundupsize(ZSTD_DStreamInSize(), SECTOR_ALIGNMENT),
+ out_allocsize = roundupsize(ZSTD_DStreamOutSize(), SECTOR_ALIGNMENT);
IF_DESKTOP(long long) int result;
void *out_buff;
@@ -132,10 +132,10 @@ unpack_zstd_stream(transformer_state_t *xstate)
bb_error_msg_and_die("memory exhausted");
}
- out_buff = xmalloc(in_allocsize + out_allocsize);
+ out_buff = aligned_xmalloc(in_allocsize + out_allocsize);
result = unpack_zstd_stream_inner(xstate, dctx, out_buff);
- free(out_buff);
+ aligned_free(out_buff);
ZSTD_freeDStream(dctx);
return result;
}
diff --git a/src/bled/decompress_vtsi.c b/src/bled/decompress_vtsi.c
index 70a2c269..612fca4a 100644
--- a/src/bled/decompress_vtsi.c
+++ b/src/bled/decompress_vtsi.c
@@ -62,7 +62,7 @@ typedef struct {
extern int __static_assert__[sizeof(VTSI_FOOTER) == 512 ? 1 : -1];
-#define MAX_READ_BUF (8 * 1024 * 1024)
+#define MAX_READ_BUF BB_BUFSIZE
static int check_vtsi_footer(VTSI_FOOTER* footer)
{
@@ -140,13 +140,13 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_vtsi_stream(transformer_state_t* xsta
goto err;
if (xstate->mem_output_size_max == 512)
- max_buflen = 1024;
+ max_buflen = SECTOR_ALIGNMENT;
- segment = xmalloc(footer.segment_num * sizeof(VTSI_SEGMENT) + max_buflen);
- if (!segment)
+ buf = aligned_xmalloc(max_buflen + footer.segment_num * sizeof(VTSI_SEGMENT));
+ if (!buf)
bb_error_msg_and_err("Failed to alloc segment buffer %u", footer.segment_num);
- buf = (uint8_t*)segment + footer.segment_num * sizeof(VTSI_SEGMENT);
+ segment = (VTSI_SEGMENT*)&buf[max_buflen];
lseek(src_fd, footer.segment_offset, SEEK_SET);
safe_read(src_fd, segment, footer.segment_num * sizeof(VTSI_SEGMENT));
@@ -182,8 +182,7 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_vtsi_stream(transformer_state_t* xsta
n = tot;
err:
- if (segment)
- free(segment);
+ aligned_free(buf);
return n;
}
diff --git a/src/bled/libbb.h b/src/bled/libbb.h
index eb799206..4bd4b6f7 100644
--- a/src/bled/libbb.h
+++ b/src/bled/libbb.h
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -40,6 +41,7 @@
#include
#define ONE_TB 1099511627776ULL
+#define SECTOR_ALIGNMENT 4096
#define ENABLE_DESKTOP 1
#if ENABLE_DESKTOP
@@ -124,6 +126,7 @@ extern jmp_buf bb_error_jmp;
extern char* bb_virtual_buf;
extern size_t bb_virtual_len, bb_virtual_pos;
extern int bb_virtual_fd;
+extern bool bb_progress_on_write;
uint32_t* crc32_filltable(uint32_t *crc_table, int endian);
uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len, uint32_t *crc32table_le);
@@ -152,7 +155,7 @@ struct timeval64 {
};
extern void (*bled_printf) (const char* format, ...);
-extern void (*bled_progress) (const uint64_t processed_bytes);
+extern void (*bled_progress) (const int64_t processed_bytes);
extern void (*bled_switch) (const char* filename, const uint64_t filesize);
extern int (*bled_read)(int fd, void* buf, unsigned int count);
extern int (*bled_write)(int fd, const void* buf, unsigned int count);
@@ -191,8 +194,8 @@ static inline int fnmatch(const char *pattern, const char *string, int flags) {
static inline pid_t wait(int* status) { *status = 4; return -1; }
#define wait_any_nohang wait
-/* This enables the display of a progress based on the number of bytes read */
-extern uint64_t bb_total_rb;
+/* This enables the display of a progress based on the number of bytes read/written */
+extern uint64_t bb_total_rb, bb_total_wb;
static inline int full_read(int fd, void *buf, unsigned int count) {
int rb;
@@ -223,23 +226,28 @@ static inline int full_read(int fd, void *buf, unsigned int count) {
} else {
rb = (bled_read != NULL) ? bled_read(fd, buf, count) : _read(fd, buf, count);
}
- if (rb > 0) {
+ if (bled_progress != NULL && !bb_progress_on_write && rb > 0) {
bb_total_rb += rb;
- if (bled_progress != NULL)
- bled_progress(bb_total_rb);
+ bled_progress(bb_total_rb);
}
return rb;
}
static inline int full_write(int fd, const void* buffer, unsigned int count)
{
+ int wb;
/* None of our r/w buffers should be larger than BB_BUFSIZE */
if (count > BB_BUFSIZE) {
errno = E2BIG;
return -1;
}
- return (bled_write != NULL) ? bled_write(fd, buffer, count) : _write(fd, buffer, count);
+ wb = (bled_write != NULL) ? bled_write(fd, buffer, count) : _write(fd, buffer, count);
+ if (bled_progress != NULL && bb_progress_on_write && wb > 0) {
+ bb_total_wb += wb;
+ bled_progress(bb_total_wb);
+ }
+ return wb;
}
static inline void bb_copyfd_exact_size(int fd1, int fd2, off_t size)
@@ -254,7 +262,7 @@ static inline void bb_copyfd_exact_size(int fd1, int fd2, off_t size)
if (size > ONE_TB)
bb_error_msg_and_die("too large");
- buf = malloc(BB_BUFSIZE);
+ buf = _mm_malloc(BB_BUFSIZE, SECTOR_ALIGNMENT);
if (buf == NULL)
bb_error_msg_and_die("out of memory");
@@ -262,7 +270,7 @@ static inline void bb_copyfd_exact_size(int fd1, int fd2, off_t size)
int r, w;
r = full_read(fd1, buf, (unsigned int)MIN(size - rb, BB_BUFSIZE));
if (r < 0) {
- free(buf);
+ _mm_free(buf);
bb_error_msg_and_die("read error");
}
if (r == 0) {
@@ -271,7 +279,7 @@ static inline void bb_copyfd_exact_size(int fd1, int fd2, off_t size)
}
w = full_write(fd2, buf, r);
if (w < 0) {
- free(buf);
+ _mm_free(buf);
bb_error_msg_and_die("write error");
}
if (w != r) {
@@ -280,7 +288,7 @@ static inline void bb_copyfd_exact_size(int fd1, int fd2, off_t size)
}
rb += r;
}
- free(buf);
+ _mm_free(buf);
}
static inline struct tm *localtime_r(const time_t *timep, struct tm *result) {
@@ -294,6 +302,9 @@ static inline struct tm *localtime_r(const time_t *timep, struct tm *result) {
#define xmalloc malloc
#define xzalloc(x) calloc(x, 1)
#define malloc_or_warn malloc
+#define aligned_xmalloc(x) _mm_malloc(x, SECTOR_ALIGNMENT);
+static inline void* aligned_xzalloc(size_t x) { void* r = aligned_xmalloc(x); if (r) memset(r, 0, x); return r; }
+#define aligned_free _mm_free
#define mkdir(x, y) _mkdirU(x)
struct fd_pair { int rd; int wr; };
void xpipe(int filedes[2]) FAST_FUNC;
diff --git a/src/bled/xz_dec_stream.c b/src/bled/xz_dec_stream.c
index b0184228..15c1a7be 100644
--- a/src/bled/xz_dec_stream.c
+++ b/src/bled/xz_dec_stream.c
@@ -368,7 +368,7 @@ static enum xz_ret XZ_FUNC crc32_validate(struct xz_dec *s, struct xz_buf *b)
*/
static bool XZ_FUNC check_skip(struct xz_dec *s, struct xz_buf *b)
{
- while (s->check_type < 16 && s->pos < check_sizes[s->check_type]) {
+ while (s->check_type >= 0 && s->check_type < 16 && s->pos < check_sizes[s->check_type]) {
if (b->in_pos == b->in_size)
return false;
diff --git a/src/db.h b/src/db.h
index c3197e9f..c4982b45 100644
--- a/src/db.h
+++ b/src/db.h
@@ -84,6 +84,7 @@ static uint8_t sha256db[] = {
0x82, 0x11, 0xfa, 0xe8, 0xaf, 0xf0, 0x23, 0x3f, 0x05, 0xa8, 0xb7, 0x8c, 0x58, 0x15, 0x25, 0xe2, 0x81, 0xac, 0x98, 0x23, 0x54, 0xa8, 0xc4, 0x3b, 0xb4, 0x96, 0x5e, 0x61, 0xdc, 0x98, 0xb4, 0x62, // syslinux-6.03/pre8/ldlinux.bss
0x83, 0x57, 0xaa, 0xd3, 0x6a, 0xec, 0x68, 0x21, 0xcb, 0xf2, 0x17, 0x4d, 0xb5, 0xd2, 0x09, 0xef, 0x2c, 0xd2, 0x62, 0x88, 0x12, 0x39, 0xeb, 0xc3, 0xf4, 0xc1, 0xcf, 0x55, 0xab, 0x10, 0xee, 0x55, // grub-2.12~rc1/core.img
0x83, 0x9b, 0xd0, 0x8a, 0xcb, 0x68, 0x47, 0xd6, 0x55, 0x07, 0xf1, 0x4e, 0x7a, 0x55, 0x6e, 0x91, 0xe6, 0x12, 0x9c, 0x47, 0x86, 0x3f, 0x7d, 0x61, 0xe2, 0xce, 0x6d, 0xb7, 0x8d, 0xf3, 0xd2, 0x3f, // syslinux-6.03/pre9/ldlinux.bss
+ 0x84, 0x73, 0x50, 0x4a, 0x84, 0x9c, 0x89, 0x83, 0x29, 0xcb, 0x90, 0x92, 0xf2, 0x5e, 0x26, 0x9d, 0x4e, 0x19, 0x3b, 0xc7, 0x72, 0xf8, 0x48, 0x9f, 0xa2, 0xc5, 0xd5, 0xa5, 0xd2, 0xd8, 0x87, 0x74, // grub-2.12-nonstandard-gdie/core.img
0x87, 0xaa, 0x91, 0xf8, 0x7f, 0xba, 0x5f, 0x31, 0x79, 0x43, 0x08, 0xda, 0xa4, 0xa4, 0x8d, 0xad, 0x6c, 0xf6, 0xfa, 0x34, 0x26, 0x4d, 0x66, 0xb8, 0x84, 0xb8, 0xb9, 0xdc, 0x96, 0x42, 0xed, 0x86, // syslinux-5.02/ldlinux.sys
0x88, 0x14, 0xe5, 0x76, 0xab, 0xc1, 0xaa, 0x44, 0xdd, 0xe9, 0x43, 0xb0, 0xca, 0xae, 0xe8, 0x33, 0xa5, 0x81, 0x01, 0x42, 0x61, 0x4a, 0xde, 0xeb, 0x4c, 0xc7, 0x25, 0xe7, 0x8a, 0x50, 0x45, 0xb7, // syslinux-6.03/ldlinux.bss
0x8b, 0x93, 0x7e, 0x5e, 0x8b, 0xae, 0x5a, 0xf8, 0xc8, 0x95, 0x63, 0xc0, 0x0e, 0x9c, 0xaf, 0xc6, 0xcd, 0x7c, 0x2c, 0x80, 0x8a, 0xda, 0x7b, 0xf4, 0xad, 0x51, 0x08, 0xda, 0x3e, 0x51, 0xcd, 0x70, // grub-2.00-22/core.img
diff --git a/src/drive.c b/src/drive.c
index 0b9a27eb..5c660348 100644
--- a/src/drive.c
+++ b/src/drive.c
@@ -1765,11 +1765,15 @@ const char* GetFsName(HANDLE hPhysical, LARGE_INTEGER StartingOffset)
if (buf == NULL)
goto out;
- // 1. Try to detect ISO9660/FAT/exFAT/NTFS/ReFS through the 512 bytes superblock at offset 0
+ // 1. Try to detect ISO9660/FAT/exFAT/NTFS/ReFS/SquashFS through the 512 bytes superblock at offset 0
if (!SetFilePointerEx(hPhysical, StartingOffset, NULL, FILE_BEGIN))
goto out;
if (!ReadFile(hPhysical, buf, sector_size, &size, NULL) || size != sector_size)
goto out;
+ if (memcmp("hsqs", buf, 4) == 0) {
+ ret = "SquashFS";
+ goto out;
+ }
if (strncmp("CD001", &buf[0x01], 5) == 0) {
ret = "ISO9660";
goto out;
diff --git a/src/format.c b/src/format.c
index 329cf3b3..c13f253c 100644
--- a/src/format.c
+++ b/src/format.c
@@ -1072,10 +1072,18 @@ BOOL WritePBR(HANDLE hLogicalVolume)
return FALSE;
}
-static void update_progress(const uint64_t processed_bytes)
+static void update_progress(const int64_t processed_bytes)
{
- UpdateProgressWithInfo(OP_FORMAT, MSG_261, processed_bytes, img_report.image_size);
- uprint_progress(processed_bytes, img_report.image_size);
+ static uint64_t total_bytes;
+
+ if (processed_bytes < 0) {
+ total_bytes = -processed_bytes;
+ UpdateProgressWithInfo(OP_FORMAT, MSG_261, 0, total_bytes);
+ uprint_progress(0, total_bytes);
+ } else {
+ UpdateProgressWithInfo(OP_FORMAT, MSG_261, processed_bytes, total_bytes);
+ uprint_progress(processed_bytes, total_bytes);
+ }
}
// Some compressed images use streams that aren't multiple of the sector
@@ -1288,7 +1296,6 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
if_assert_fails((uintptr_t)sec_buf% SelectedDrive.SectorSize == 0)
goto out;
sec_buf_pos = 0;
- update_progress(0);
bled_init(256 * KB, uprintf, NULL, sector_write, update_progress, NULL, &ErrorStatus);
bled_ret = bled_uncompress_with_handles(hSourceImage, hPhysicalDrive, img_report.compression_type);
bled_exit();
diff --git a/src/hash.c b/src/hash.c
index 20a354bc..bd8272b9 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -1532,7 +1532,6 @@ BOOL HashFile(const unsigned type, const char* path, uint8_t* hash)
HASH_CONTEXT hash_ctx = { {0} };
HANDLE h = INVALID_HANDLE_VALUE;
DWORD rs = 0;
- uint64_t rb;
uint8_t buf[4096];
if ((type >= HASH_MAX) || (path == NULL) || (hash == NULL))
@@ -1546,7 +1545,7 @@ BOOL HashFile(const unsigned type, const char* path, uint8_t* hash)
}
hash_init[type](&hash_ctx);
- for (rb = 0; ; rb += rs) {
+ while(1) {
CHECK_FOR_USER_CANCEL;
if (!ReadFile(h, buf, sizeof(buf), &rs, NULL)) {
ErrorStatus = RUFUS_ERROR(ERROR_READ_FAULT);
diff --git a/src/rufus.c b/src/rufus.c
index 09e34c45..83803ce3 100755
--- a/src/rufus.c
+++ b/src/rufus.c
@@ -1391,6 +1391,8 @@ DWORD WINAPI ImageScanThread(LPVOID param)
(img_report.compression_type != BLED_COMPRESSION_NONE && img_report.compression_type < BLED_COMPRESSION_MAX) ?
"compressed " : "", img_report.is_vhd ? "VHD" : "disk");
selection_default = BT_IMAGE;
+ if (img_report.projected_size > 0)
+ uprintf(" Size: %s (Projected)", SizeToHumanReadable(img_report.projected_size, FALSE, FALSE));
}
if (img_report.is_iso) {
@@ -1525,7 +1527,8 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
Notification(MB_OK | MB_ICONERROR, lmprintf(MSG_358), lmprintf(MSG_359));
goto out;
}
- if ((size_check) && (img_report.projected_size > (uint64_t)SelectedDrive.DiskSize)) {
+ /* Add 4 KB extra margin for VHD footers and so on */
+ if ((size_check) && (img_report.projected_size > ((uint64_t)SelectedDrive.DiskSize + 4 * KB))) {
// This ISO image is too big for the selected target
Notification(MB_OK | MB_ICONERROR, lmprintf(MSG_088), lmprintf(MSG_089));
goto out;
diff --git a/src/rufus.rc b/src/rufus.rc
index ecac3ef6..abdf1754 100644
--- a/src/rufus.rc
+++ b/src/rufus.rc
@@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
-CAPTION "Rufus 4.15.2391"
+CAPTION "Rufus 4.15.2396"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@@ -409,8 +409,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,15,2391,0
- PRODUCTVERSION 4,15,2391,0
+ FILEVERSION 4,15,2396,0
+ PRODUCTVERSION 4,15,2396,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -428,13 +428,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus"
- VALUE "FileVersion", "4.15.2391"
+ VALUE "FileVersion", "4.15.2396"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", " 2011-2026 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.15.exe"
VALUE "ProductName", "Rufus"
- VALUE "ProductVersion", "4.15.2391"
+ VALUE "ProductVersion", "4.15.2396"
END
END
BLOCK "VarFileInfo"
diff --git a/src/stdio.c b/src/stdio.c
index c7861c65..cc13d283 100644
--- a/src/stdio.c
+++ b/src/stdio.c
@@ -967,9 +967,15 @@ static void print_extracted_file(const char* file_path, uint64_t file_length)
PrintStatus(0, MSG_000, str); // MSG_000 is "%s"
}
-static void update_progress(const uint64_t processed_bytes)
+static void update_progress(const int64_t processed_bytes)
{
- UpdateProgressWithInfo(OP_EXTRACT_ZIP, MSG_348, processed_bytes, archive_size);
+ static uint64_t total_bytes = 0;
+
+ if (processed_bytes < 0) {
+ total_bytes = -processed_bytes;
+ UpdateProgressWithInfo(OP_EXTRACT_ZIP, MSG_348, 0, total_bytes);
+ } else
+ UpdateProgressWithInfo(OP_EXTRACT_ZIP, MSG_348, processed_bytes, total_bytes);
}
// Extract content from a zip archive onto the designated directory or drive
diff --git a/src/stdlg.c b/src/stdlg.c
index 51c132d2..74dc7efe 100644
--- a/src/stdlg.c
+++ b/src/stdlg.c
@@ -838,17 +838,17 @@ static int GetComboBoxMinWidth(HWND hCtrl, StrArray* array)
return max_width + arrow_width + padding;
}
-static VOID ShowSilentOption(HWND hDlg, int s, BOOL show)
+static VOID ShowSilentOption(HWND hDlg, int index, BOOL show)
{
int i, dh;
RECT rc1, rc2;
- ShowWindow(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + selection_data[s].options->edition_index - 1), show ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + selection_data[index].options->edition_index - 1), show ? SW_SHOW : SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDC_SELECTION_EDITION), show ? SW_SHOW : SW_HIDE);
GetWindowRect(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1), &rc1);
GetWindowRect(GetDlgItem(hDlg, IDC_SELECTION_CHOICE2), &rc2);
dh = show ? (rc2.top - rc1.top) : (rc1.top - rc2.top);
- for (i = selection_data[s].options->edition_index; i < (int)selection_data[s].options->choices.Index; i++)
+ for (i = selection_data[index].options->edition_index; i < (int)selection_data[index].options->choices.Index; i++)
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), 0, dh, 0, 0, 1.0f);
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDOK), 0, dh, 0, 0, 1.0f);
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDCANCEL), 0, dh, 0, 0, 1.0f);
@@ -1055,7 +1055,6 @@ static INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam
break;
case WM_COMMAND:
BOOL enable = TRUE;
- BOOL silent_install_checked = FALSE;
WORD command = LOWORD(wParam);
if (command >= IDC_SELECTION_CHOICE1 && command < IDC_SELECTION_CHOICEMAX) {
if (selection_data[s].options->flags & SELECTION_NEEDS_ALL_TO_PROCEED) {
@@ -1417,12 +1416,11 @@ void DestroyTooltip(HWND hControl)
void DestroyAllTooltips(void)
{
- int i, j;
+ int i;
- for (i = 0, j = 0; i < MAX_TOOLTIPS; i++) {
+ for (i = 0; i < MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == NULL)
continue;
- j++;
DestroyWindow(ttlist[i].hTip);
safe_free(ttlist[i].wstring);
ttlist[i].original_proc = NULL;
diff --git a/src/vhd.c b/src/vhd.c
index d0faa1f5..7444ee17 100644
--- a/src/vhd.c
+++ b/src/vhd.c
@@ -99,6 +99,9 @@ static int8_t IsCompressedBootableImage(const char* path)
ErrorStatus = 0;
if (img_report.compression_type < BLED_COMPRESSION_MAX) {
bled_init(0, uprintf, NULL, NULL, NULL, NULL, &ErrorStatus);
+ dc = bled_get_uncompressed_size(path, file_assoc[i].type);
+ if (dc > 0)
+ img_report.projected_size = dc;
dc = bled_uncompress_to_buffer(path, (char*)buf, MBR_SIZE, file_assoc[i].type);
bled_exit();
} else if (img_report.compression_type == BLED_COMPRESSION_MAX) {