Fix multi-part RAR: use explicit ArchiveFormat instead of VolumedArchiveInStream

VolumedArchiveInStream only works for .7z.001 splits - it rejects RAR
filenames. For multi-part RAR (.partN.rar), use RandomAccessFileInStream
with explicit ArchiveFormat.RAR5/RAR format specification. Auto-detection
with null format can fail for multi-volume RAR archives.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe 2026-03-03 16:03:01 +01:00
parent 462fc0397e
commit 35e84e652e
10 changed files with 33 additions and 7 deletions

View File

@ -11,6 +11,7 @@ import net.sf.sevenzipjbinding.IInStream;
import net.sf.sevenzipjbinding.ISequentialOutStream; import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ICryptoGetTextPassword; import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
import net.sf.sevenzipjbinding.PropID; import net.sf.sevenzipjbinding.PropID;
import net.sf.sevenzipjbinding.ArchiveFormat;
import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException; import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream; import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
@ -328,13 +329,9 @@ public final class JBindExtractorMain {
String effectivePassword = password == null ? "" : password; String effectivePassword = password == null ? "" : password;
SevenZipVolumeCallback callback = new SevenZipVolumeCallback(archiveFile, effectivePassword); SevenZipVolumeCallback callback = new SevenZipVolumeCallback(archiveFile, effectivePassword);
// Multi-volume archives need VolumedArchiveInStream so 7z-JBinding can // VolumedArchiveInStream is ONLY for .7z.001 split archives.
// request additional volumes via the IArchiveOpenVolumeCallback. // It internally checks for the ".7z.001" suffix and rejects everything else.
boolean isMultiVolume = SEVEN_ZIP_SPLIT_RE.matcher(nameLower).matches() if (SEVEN_ZIP_SPLIT_RE.matcher(nameLower).matches()) {
|| RAR_MULTIPART_RE.matcher(nameLower).matches()
|| hasOldStyleRarSplits(archiveFile);
if (isMultiVolume) {
VolumedArchiveInStream volumed = new VolumedArchiveInStream(archiveFile.getName(), callback); VolumedArchiveInStream volumed = new VolumedArchiveInStream(archiveFile.getName(), callback);
IInArchive archive = SevenZip.openInArchive(null, volumed, callback); IInArchive archive = SevenZip.openInArchive(null, volumed, callback);
return new SevenZipArchiveContext(archive, null, volumed, callback); return new SevenZipArchiveContext(archive, null, volumed, callback);
@ -342,6 +339,35 @@ public final class JBindExtractorMain {
RandomAccessFile raf = new RandomAccessFile(archiveFile, "r"); RandomAccessFile raf = new RandomAccessFile(archiveFile, "r");
RandomAccessFileInStream stream = new RandomAccessFileInStream(raf); RandomAccessFileInStream stream = new RandomAccessFileInStream(raf);
// Multi-part RAR (.part1.rar, .part2.rar or old-style .rar/.r01/.r02):
// Auto-detection with null format can fail for multi-volume RAR because
// 7z-JBinding may not fully recognize the format from a single part.
// Specify the format explicitly (try RAR5 first, then RAR4).
if (RAR_MULTIPART_RE.matcher(nameLower).matches() || hasOldStyleRarSplits(archiveFile)) {
ArchiveFormat[] rarFormats = { ArchiveFormat.RAR5, ArchiveFormat.RAR };
Exception lastError = null;
for (ArchiveFormat fmt : rarFormats) {
try {
stream.seek(0L, 0);
IInArchive archive = SevenZip.openInArchive(fmt, stream, callback);
return new SevenZipArchiveContext(archive, stream, null, callback);
} catch (Exception e) {
lastError = e;
}
}
// Final attempt with auto-detection
try {
stream.seek(0L, 0);
IInArchive archive = SevenZip.openInArchive(null, stream, callback);
return new SevenZipArchiveContext(archive, stream, null, callback);
} catch (Exception e) {
// Close the RAF since we're about to throw
try { raf.close(); } catch (Throwable ignored) {}
throw lastError != null ? lastError : e;
}
}
IInArchive archive = SevenZip.openInArchive(null, stream, callback); IInArchive archive = SevenZip.openInArchive(null, stream, callback);
return new SevenZipArchiveContext(archive, stream, null, callback); return new SevenZipArchiveContext(archive, stream, null, callback);
} }