omnizip 0.3.41 → 0.3.42

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 919a322870094f270bd574530af4c463b6446586fdf869026b79ad017834bc1e
4
- data.tar.gz: 55507eb0b2da4109cb094e711cda2d39df10773c9774c4a01614edc0d8548d9a
3
+ metadata.gz: 060755f6c69bca46bcb999d0d36471045ae3d13e260cfa3c73601a8d440b1634
4
+ data.tar.gz: b524b77c579c824009821ca5d1bdb47327cfc55f3ebd1dab4ea8574d1ec3c631
5
5
  SHA512:
6
- metadata.gz: 73b03468e2bb51d227059f316ab4fce911ce19ee6e0c175559de7b1df58a69536d9284d2b5ee2a0a19366ef0c53d448d02e4b95f397042c69c569630aacea86f
7
- data.tar.gz: dad012852021f331f77a36bf86842274f612d215e32829965bf4bbaf5ad8b9b17539eb7e531a55e73c93d206c73a66760ccde6dfcf234b49e371f17f79451df8
6
+ metadata.gz: 237f14e0c479844a473d1830e956882a0d250f6c733522bcd9f3d36b7ba10b474b4564902791aecd52948ad330b82bb0bb7e61dd97db607bf3dcd85ab3dc847b
7
+ data.tar.gz: cfe4637c8ad1223ccb8b9735fc1494d61937b636f3d79dd7fbd2c1f59f6f35eb38686152ee56732f0cb29cfbf3c12f7ddfb6dae5bd6c146b8d833fca5315a00a
data/CHANGELOG.md CHANGED
@@ -7,6 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.41] - 2026-08-29
11
+
12
+ ### Changed
13
+ - One RAR parser. `Formats::Rar3::Reader` and `Formats::Rar5::Reader`
14
+ are adapters over the primary `Formats::Rar::Reader` — the only
15
+ parser verified against real WinRAR archives — replacing ~600
16
+ lines of duplicate parsing. Both legacy interfaces gain real-
17
+ archive fidelity for free (exact mtimes, backslash normalization,
18
+ directory detection, CRC fields). The legacy RAR5 writer — whose
19
+ output not even unrar could list (garbage names, 2038 dates) —
20
+ now bridges onto the spec-conformant primary writer; its archives
21
+ pass `unrar t`. The primary Header learned minimal archives (file
22
+ block first, no main header) for RAR4 and RAR5, restoring a legacy
23
+ capability in one place.
24
+ - Direct-seek extraction: the parser records each entry's
25
+ data_offset, so native extraction is one seek + read instead of
26
+ re-parsing the archive per entry. unrar-backed extraction spills
27
+ the archive once per archive path instead of extracting the whole
28
+ archive per entry (O(n^2) -> O(n)). Fixed a latent off-by-two in
29
+ the fallback block walk.
30
+ - Converter symmetry: zip -> 7z uses the native reader with the
31
+ same extract-to-temp/write-from-tree shape as 7z -> zip; internal
32
+ code no longer calls the rubyzip-compat `Omnizip::Zip::File` seam.
33
+ - `omnizip archive create` passes one flat options hash;
34
+ `Formats::Rar.create` applies the RAR4/RAR5-specific semantics
35
+ (the RAR4 writer's recovery guard no longer trips on explicit
36
+ false).
37
+
38
+ ### Fixed
39
+ - Native-decode CRC guard referenced `Compression::DecompressionError`
40
+ which does not exist (the classes live under
41
+ `Compression::Dispatcher`); the NameError was silently masked by
42
+ the fallback rescue.
43
+ - Unicode-flagged RAR4 names can carry non-UTF-8 bytes;
44
+ String#tr raised and aborted the block walk under non-UTF-8
45
+ locales (CI runners). Names are scrubbed before normalization.
46
+ - Corrupt size fields drove unbounded reads: a garbage pack_size
47
+ attempted a multi-gigabyte read (NoMemoryError on CI) or an
48
+ overflowing seek (EINVAL on Linux, silently clamped on macOS).
49
+ Packed data is now seeked past — never allocated to skip — with
50
+ sizes bounds-checked against the archive length and corrupt skips
51
+ clamped to EOF so entries still list.
52
+
10
53
  ## [0.3.40] - 2026-08-29
11
54
 
12
55
  ### Fixed
data/CONTEXT.md ADDED
@@ -0,0 +1,63 @@
1
+ # Domain Glossary
2
+
3
+ Names for the concepts that keep coming up in omnizip. Use these
4
+ words in code, docs, and reviews; when a term sharpens, update it
5
+ here.
6
+
7
+ ## Archive access
8
+
9
+ - **Archive facade** — `Omnizip::Archive`: the format-neutral entry
10
+ point. `create` yields a Builder (add_file/add_directory/add_data),
11
+ `open` yields a ReaderSession (entries/read/extract/extract_all).
12
+ - **ArchiveHandler** — per-format adapter registered in the handler
13
+ registry (create/extract_to/list/read_entry) plus its extension
14
+ route. The facade and the convenience layer dispatch through
15
+ handlers, never through format classes directly.
16
+ - **Extension route** — mapping from a file extension to a format
17
+ symbol. Writable routes (`ARCHIVE_FORMAT_EXTENSIONS`) may create
18
+ archives; read routes (`READ_ARCHIVE_FORMAT_EXTENSIONS`) only
19
+ extract/list/read. `.rar`, `.cpio`, `.iso` are read-only routes;
20
+ creating them raises truthfully.
21
+
22
+ ## Reading and decoding
23
+
24
+ - **Native decode** — decompression implemented in Ruby inside
25
+ omnizip. RAR native decode is **CRC-gated**: the stored header CRC
26
+ must match the decoded bytes or the result is discarded.
27
+ - **External fallback** — delegating to an external tool (the unrar
28
+ command) when native decode cannot be trusted. RAR extraction
29
+ spills the archive once per archive path (the **extraction spill
30
+ cache**) and copies entries out of it.
31
+
32
+ ## Writing and interop
33
+
34
+ - **Interop-verified** — output accepted by the official tool for
35
+ the format (unrar for RAR, 7zz for 7z/ISO, unzip for ZIP), checked
36
+ with `test` plus a byte-identical extraction round-trip.
37
+ - **Omnizip-internal codec** — a compression stream only omnizip can
38
+ decode (RAR methods above :store). Archives carrying them must be
39
+ labeled honestly; official tools fail loudly, never silently.
40
+ - **STORE** — the uncompressed method; the interoperability floor
41
+ for every archive writer in this library.
42
+
43
+ ## Seams
44
+
45
+ - **Compat seam** — `Omnizip::Zip` (aliased into the global `Zip`
46
+ namespace by `omnizip/rubyzip_compat`): a rubyzip-compatible
47
+ layer for library consumers migrating off rubyzip. Internal code
48
+ must use the native `Formats::Zip` tree instead; the Metadata
49
+ subsystem is its one deliberate in-house consumer (in-place
50
+ central-directory editing).
51
+ - **Bridge** — an adapter that carries a file-based implementation
52
+ across to a non-file interface, spilling through a temporary file
53
+ (the RAR3/RAR5 legacy readers, `Buffer::SevenZipBridge`).
54
+ - **Primary parser** — the one parser per format family verified
55
+ against real archives (e.g. `Formats::Rar::Reader`). Legacy
56
+ interfaces are adapters over primary parsers, never parallel
57
+ implementations.
58
+
59
+ ## In memory
60
+
61
+ - **Buffer** — `Omnizip::Buffer`: in-memory archive create/read for
62
+ ZIP (stream-based) and 7z (through the bridge). Format detection
63
+ compares binary magic literals.
@@ -3,14 +3,13 @@
3
3
  module Omnizip
4
4
  module Formats
5
5
  # ISO 9660 CD-ROM filesystem format support
6
- # Provides read-only access to ISO images
7
6
  #
8
- # ISO 9660 is the standard filesystem for CD-ROMs and DVD-ROMs.
9
- # This implementation supports:
10
- # - Primary Volume Descriptor parsing
11
- # - Directory structure traversal
12
- # - File extraction
13
- # - Rock Ridge extensions (basic)
7
+ # Reads and writes ISO 9660 images: Primary Volume Descriptor
8
+ # parsing, directory traversal, file extraction, and image
9
+ # creation whose directory records point at allocated extents
10
+ # (verified against 7-Zip). Rock Ridge and Joliet are NOT
11
+ # implemented the writer's options stay false rather than
12
+ # advertising extensions readers would misparse.
14
13
  module Iso
15
14
  # Nested classes - autoloaded
16
15
  autoload :Reader, "omnizip/formats/iso/reader"
@@ -258,6 +258,7 @@ module Omnizip
258
258
  raise "Command entry extraction failed: #{entry_name}"
259
259
  end
260
260
  extract_cache[cache_key] = cached_dir
261
+ install_exit_sweeper
261
262
  end
262
263
 
263
264
  source = File.join(cached_dir, entry_name)
@@ -265,8 +266,8 @@ module Omnizip
265
266
  end
266
267
 
267
268
  # Extracted-archive cache: archive path => spill directory.
268
- # Cleared by .clear_extract_cache! (tests); directories live
269
- # in the system temp dir until process exit.
269
+ # Cleared by .clear_extract_cache!; an at_exit sweeper
270
+ # reclaims everything at normal process termination.
270
271
  def extract_cache
271
272
  @extract_cache ||= {}
272
273
  end
@@ -277,6 +278,14 @@ module Omnizip
277
278
  extract_cache.clear
278
279
  end
279
280
 
281
+ # Reclaim spill directories when the process exits normally
282
+ def install_exit_sweeper
283
+ return if @exit_sweeper_installed
284
+
285
+ at_exit { clear_extract_cache! }
286
+ @exit_sweeper_installed = true
287
+ end
288
+
280
289
  # Build extract command
281
290
  def build_extract_command(archive_path, output_dir, password)
282
291
  # Array form: no shell involved, so paths and passwords
@@ -3,17 +3,18 @@
3
3
  module Omnizip
4
4
  module Formats
5
5
  # RAR archive format support
6
- # Provides read-only access to RAR archives (single and multi-volume)
7
6
  #
8
- # This module implements RAR archive format support:
9
- # - Format signature validation (RAR4 and RAR5)
10
- # - Archive structure parsing
11
- # - File listing
12
- # - File extraction (requires unrar gem or system command)
13
- # - Multi-volume archive support
14
- #
15
- # Note: RAR compression is proprietary, so this implementation
16
- # is read-only and requires external decompression tools.
7
+ # - Formats::Rar::Reader the primary parser, verified against
8
+ # real WinRAR archives: listing, directories, mtimes, and
9
+ # extraction (native STORE decode with stored-CRC verification;
10
+ # foreign compressed streams fall back to the unrar command
11
+ # after the CRC check rejects them)
12
+ # - Formats::Rar::Writer (RAR4) and Formats::Rar::Rar5::Writer —
13
+ # STORE output is unrar-verified; compressed methods produce
14
+ # Omnizip-internal streams (see the README interop table)
15
+ # - Formats::Rar3 / Formats::Rar5 legacy interfaces, thin
16
+ # adapters over the primary reader and the RAR5 writer
17
+ # - Multi-volume archives, encryption (RAR5), recovery records
17
18
  module Rar
18
19
  # Nested classes - autoloaded
19
20
  autoload :Constants, "omnizip/formats/rar/constants"
@@ -9,7 +9,7 @@ module Omnizip
9
9
  class Reader
10
10
  include Omnizip::Formats::Zip::Constants
11
11
 
12
- attr_reader :file_path, :entries
12
+ attr_reader :file_path
13
13
 
14
14
  def initialize(file_path)
15
15
  @file_path = file_path
@@ -36,6 +36,13 @@ module Omnizip
36
36
  self
37
37
  end
38
38
 
39
+ # All entries, parsing on first use — a Reader is usable
40
+ # immediately; #read remains for explicit eager loading
41
+ def entries
42
+ ensure_read
43
+ @entries
44
+ end
45
+
39
46
  # Extract all files to a directory
40
47
  def extract_all(output_dir, preserve_links: true,
41
48
  dereference_links: false)
@@ -163,6 +170,11 @@ dereference_links: false)
163
170
 
164
171
  private
165
172
 
173
+ # Parse the central directory once, on demand
174
+ def ensure_read
175
+ read if @entries.empty? && @central_directory.empty?
176
+ end
177
+
166
178
  # Read central directory entries
167
179
  def read_central_directory(io, eocd)
168
180
  io.seek(eocd.central_directory_offset, ::IO::SEEK_SET)
data/lib/omnizip/temp.rb CHANGED
@@ -129,9 +129,12 @@ module Omnizip
129
129
 
130
130
  case format
131
131
  when :zip
132
- Omnizip::Zip::File.create(path) do |zip|
133
- zip.add(name) { data }
134
- end
132
+ # Native writer — the rubyzip-compat Omnizip::Zip seam is
133
+ # for library consumers migrating from rubyzip, not for
134
+ # internal use
135
+ writer = Omnizip::Formats::Zip::Writer.new(path)
136
+ writer.add_data(name, data.to_s)
137
+ writer.write
135
138
  end
136
139
  end
137
140
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- VERSION = "0.3.41"
4
+ VERSION = "0.3.42"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnizip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.41
4
+ version: 0.3.42
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -115,6 +115,7 @@ files:
115
115
  - ".rubocop.yml"
116
116
  - ".rubocop_todo.yml"
117
117
  - CHANGELOG.md
118
+ - CONTEXT.md
118
119
  - COPYING
119
120
  - Gemfile
120
121
  - LICENSE