omnizip 0.3.57 → 0.3.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/docs/guides/creating-archives.adoc +17 -23
- data/lib/omnizip/commands/decompress_command.rb +4 -1
- data/lib/omnizip/formats/rar/rar5/writer.rb +5 -0
- data/lib/omnizip/formats/zip/writer.rb +40 -0
- data/lib/omnizip/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04bb0d1c81ef7f4571014bb6679ad0d4750d4c078729daea82c88f643e246818
|
|
4
|
+
data.tar.gz: 7523e6a77a43db5eb02ec7c16ef4adf0751150129f329af82eb11297f93a68f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: da1c027f6e44246e6421760d3197501db38957aeb1541c0db6e3553ab73b5e03a221a362b0165d4196bf1185f0022b341529321cdce9c2a339c3cf2f31b4f733
|
|
7
|
+
data.tar.gz: 7732ed6d8268d006195f7648980e2f8619ea89d45767932afd587f508592f627697a9cc436f6f3e1a01b41a8a579034f654c1bc33cc3331fced28af5bd68b8ff
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.57] - 2026-09-02
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- TAR archives no longer corrupt silently on long paths: the writer
|
|
14
|
+
now splits entry names over 99 bytes across the ustar prefix +
|
|
15
|
+
name fields at a "/" boundary, the reader joins them back for
|
|
16
|
+
consumers, and paths no ustar fields can represent raise
|
|
17
|
+
`FormatError` instead of truncating. Verified against system tar.
|
|
18
|
+
- `:auto` profile sampling walked directories in the OS's
|
|
19
|
+
iteration order, resolving different files (and compression
|
|
20
|
+
settings) per platform; the walk is sorted and deterministic now.
|
|
21
|
+
- The getting-started docs' 7z examples used a Reader API that
|
|
22
|
+
never shipped (`each_file`, `close`, `Reader.info`) — every
|
|
23
|
+
snippet raised NoMethodError verbatim. Rewritten against the real
|
|
24
|
+
surface and executed to verify.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- The last 28 bare `raise "string"` sites (RAR tree, cpio entries,
|
|
28
|
+
parity internals, zip compat streams, two commands) gained their
|
|
29
|
+
matching error classes — `lib/` now contains zero bare raises.
|
|
30
|
+
- The dead `--preserve-ntfs-streams` CLI flag is removed; nothing
|
|
31
|
+
read it and no code path implements NTFS stream preservation.
|
|
32
|
+
|
|
10
33
|
## [0.3.56] - 2026-09-02
|
|
11
34
|
|
|
12
35
|
### Fixed
|
|
@@ -76,7 +76,7 @@ writer.write
|
|
|
76
76
|
|
|
77
77
|
IMPORTANT: RAR5 CRC32 checksums only work with STORE compression. When LZMA is used, CRC32 is automatically disabled. Use BLAKE2sp (always present) for compressed file integrity.
|
|
78
78
|
|
|
79
|
-
=== RAR5 Multi-Volume Archives
|
|
79
|
+
=== RAR5 Multi-Volume Archives
|
|
80
80
|
|
|
81
81
|
Split large archives across multiple volumes:
|
|
82
82
|
|
|
@@ -110,36 +110,30 @@ volumes = writer.write
|
|
|
110
110
|
# Minimum volume size: 64 KB
|
|
111
111
|
----
|
|
112
112
|
|
|
113
|
-
=== RAR5 Solid
|
|
113
|
+
=== RAR5 Solid Archives
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
`solid: true` requests a shared solid block, but the RAR-compatible
|
|
116
|
+
LZSS encoder is not implemented yet — the writer warns and writes
|
|
117
|
+
independent STORE entries instead, so the archive stays extractable
|
|
118
|
+
by official unrar:
|
|
116
119
|
|
|
117
120
|
[source,ruby]
|
|
118
121
|
----
|
|
119
122
|
require 'omnizip/formats/rar/rar5/writer'
|
|
120
123
|
|
|
121
|
-
# Enable solid compression
|
|
122
124
|
writer = Omnizip::Formats::Rar::Rar5::Writer.new('project.rar',
|
|
123
|
-
|
|
124
|
-
level: 5,
|
|
125
|
-
solid: true # Shared dictionary across files
|
|
125
|
+
solid: true # warns: falls back to independent STORE entries
|
|
126
126
|
)
|
|
127
127
|
writer.add_directory('source_code/')
|
|
128
128
|
writer.write
|
|
129
|
+
----
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
# - Text documents
|
|
134
|
-
# - Similar structured data
|
|
135
|
-
|
|
136
|
-
# Trade-offs:
|
|
137
|
-
# - Cannot extract individual files without decompressing entire block
|
|
138
|
-
# - Corruption may affect multiple files
|
|
139
|
-
# - Slightly longer extraction time for single files
|
|
131
|
+
Solid compression (shared dictionary across files, better ratios
|
|
132
|
+
for similar content) arrives when the RAR-compatible LZSS encoder
|
|
133
|
+
lands.
|
|
140
134
|
----
|
|
141
135
|
|
|
142
|
-
=== RAR5 AES-256 Encryption
|
|
136
|
+
=== RAR5 AES-256 Encryption
|
|
143
137
|
|
|
144
138
|
Password-protect archives with industry-standard encryption:
|
|
145
139
|
|
|
@@ -177,7 +171,7 @@ writer.write
|
|
|
177
171
|
# - Password verification before decryption
|
|
178
172
|
----
|
|
179
173
|
|
|
180
|
-
=== RAR5 PAR2 Recovery Records
|
|
174
|
+
=== RAR5 PAR2 Recovery Records
|
|
181
175
|
|
|
182
176
|
Add error correction for archive recovery:
|
|
183
177
|
|
|
@@ -216,7 +210,7 @@ files = writer.write
|
|
|
216
210
|
# - Works with multi-volume, solid, and encrypted archives
|
|
217
211
|
----
|
|
218
212
|
|
|
219
|
-
=== RAR5 Combined Features
|
|
213
|
+
=== RAR5 Combined Features
|
|
220
214
|
|
|
221
215
|
Use all features together for comprehensive protection:
|
|
222
216
|
|
|
@@ -262,13 +256,13 @@ puts " Total size: #{files.sum { |f| File.size(f) } / 1024 / 1024}MB"
|
|
|
262
256
|
|
|
263
257
|
=== RAR5 Best Practices
|
|
264
258
|
|
|
265
|
-
**For Maximum
|
|
259
|
+
**For Maximum Compatibility:**
|
|
266
260
|
[source,ruby]
|
|
267
261
|
----
|
|
268
262
|
writer = Omnizip::Formats::Rar::Rar5::Writer.new('archive.rar',
|
|
269
263
|
compression: :lzma,
|
|
270
|
-
level: 5, #
|
|
271
|
-
|
|
264
|
+
level: 5, # Honored by the 7z-style writers; RAR5 entries
|
|
265
|
+
# are STORE until the LZSS encoder lands
|
|
272
266
|
include_mtime: true
|
|
273
267
|
)
|
|
274
268
|
----
|
|
@@ -80,7 +80,10 @@ module Omnizip
|
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
# Archive-vs-stream routing derives from the registry's
|
|
84
|
+
# extension routes so new formats route here automatically
|
|
85
|
+
ARCHIVE_EXTENSIONS = (Convenience::ARCHIVE_FORMAT_EXTENSIONS.keys +
|
|
86
|
+
Convenience::READ_ARCHIVE_FORMAT_EXTENSIONS.keys).freeze
|
|
84
87
|
|
|
85
88
|
private
|
|
86
89
|
|
|
@@ -199,6 +199,11 @@ module Omnizip
|
|
|
199
199
|
if @options[:solid] && Compression::Lzss.available?
|
|
200
200
|
write_solid_block(f, @files)
|
|
201
201
|
else
|
|
202
|
+
if @options[:solid]
|
|
203
|
+
warn "omnizip: solid: true requested but the " \
|
|
204
|
+
"RAR-compatible LZSS encoder is not available " \
|
|
205
|
+
"— writing independent STORE entries instead"
|
|
206
|
+
end
|
|
202
207
|
@files.each do |file|
|
|
203
208
|
write_file_entry(f, file)
|
|
204
209
|
end
|
|
@@ -152,6 +152,8 @@ module Omnizip
|
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
def write_precompressed_to_io(io, compression_method: COMPRESSION_DEFLATE)
|
|
155
|
+
check_classic_entry_count!
|
|
156
|
+
entries.each { |entry| check_classic_size_limit!(entry) }
|
|
155
157
|
local_header_offsets = []
|
|
156
158
|
|
|
157
159
|
entries.each do |entry|
|
|
@@ -189,6 +191,7 @@ module Omnizip
|
|
|
189
191
|
|
|
190
192
|
# Write to an IO object
|
|
191
193
|
def write_to_io(io, compression_method: COMPRESSION_DEFLATE, level: 6)
|
|
194
|
+
check_classic_entry_count!
|
|
192
195
|
local_header_offsets = []
|
|
193
196
|
|
|
194
197
|
# Write local file headers and data. An entry-level
|
|
@@ -220,6 +223,8 @@ module Omnizip
|
|
|
220
223
|
entry[:crc32] = calculate_crc32(entry[:uncompressed_data])
|
|
221
224
|
end
|
|
222
225
|
|
|
226
|
+
check_classic_size_limit!(entry)
|
|
227
|
+
|
|
223
228
|
# Update local header with compressed sizes
|
|
224
229
|
local_header.compressed_size = entry[:compressed_size]
|
|
225
230
|
local_header.uncompressed_size = entry[:uncompressed_size]
|
|
@@ -234,6 +239,7 @@ module Omnizip
|
|
|
234
239
|
|
|
235
240
|
# Record start of central directory
|
|
236
241
|
central_directory_offset = io.pos
|
|
242
|
+
check_classic_offset_limit!(central_directory_offset)
|
|
237
243
|
|
|
238
244
|
# Write central directory headers
|
|
239
245
|
entries.each_with_index do |entry, index|
|
|
@@ -257,8 +263,42 @@ module Omnizip
|
|
|
257
263
|
io.write(eocd.to_binary)
|
|
258
264
|
end
|
|
259
265
|
|
|
266
|
+
# Classic (non-ZIP64) ZIP headers pack sizes and offsets as
|
|
267
|
+
# 32-bit values and entry counts as 16-bit; exceeding them
|
|
268
|
+
# raises instead of letting Array#pack fail with a bare
|
|
269
|
+
# RangeError. ZIP64 writing is not supported.
|
|
270
|
+
MAX_CLASSIC_SIZE = 0xFFFFFFFF
|
|
271
|
+
MAX_CLASSIC_ENTRIES = 0xFFFF
|
|
272
|
+
private_constant :MAX_CLASSIC_SIZE, :MAX_CLASSIC_ENTRIES
|
|
273
|
+
|
|
260
274
|
private
|
|
261
275
|
|
|
276
|
+
def check_classic_entry_count!
|
|
277
|
+
return if entries.size <= MAX_CLASSIC_ENTRIES
|
|
278
|
+
|
|
279
|
+
raise Omnizip::UnsupportedFormatError,
|
|
280
|
+
"#{entries.size} entries exceed the classic ZIP " \
|
|
281
|
+
"limit of #{MAX_CLASSIC_ENTRIES} (ZIP64 writing is " \
|
|
282
|
+
"not supported)"
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def check_classic_size_limit!(entry)
|
|
286
|
+
return if entry[:compressed_size].to_i <= MAX_CLASSIC_SIZE &&
|
|
287
|
+
entry[:uncompressed_size].to_i <= MAX_CLASSIC_SIZE
|
|
288
|
+
|
|
289
|
+
raise Omnizip::UnsupportedFormatError,
|
|
290
|
+
"entry #{entry[:filename].inspect} exceeds the 4 GiB " \
|
|
291
|
+
"classic ZIP size limit (ZIP64 writing is not supported)"
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def check_classic_offset_limit!(offset)
|
|
295
|
+
return if offset <= MAX_CLASSIC_SIZE
|
|
296
|
+
|
|
297
|
+
raise Omnizip::UnsupportedFormatError,
|
|
298
|
+
"archive layout exceeds the 4 GiB classic ZIP " \
|
|
299
|
+
"offset limit (ZIP64 writing is not supported)"
|
|
300
|
+
end
|
|
301
|
+
|
|
262
302
|
# Create an entry hash
|
|
263
303
|
def create_entry(
|
|
264
304
|
filename:,
|
data/lib/omnizip/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omnizip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.58
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|