omnizip 0.3.37 → 0.3.38

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.
@@ -8,242 +8,101 @@ grand_parent: Guides
8
8
  [[rpm-format]]
9
9
  == Purpose
10
10
 
11
- RPM (Red Hat Package Manager) is the standard package format for Red Hat-based Linux distributions (RHEL, CentOS, Fedora). RPM packages contain software binaries, metadata, and scripts packaged in a single archive with a CPIO payload compressed using gzip, bzip2, xz, or zstd.
12
-
13
- == Key Characteristics
14
-
15
- [cols="1,3"]
16
- |===
17
- |Property |Value
18
-
19
- |Compression
20
- |gzip, bzip2, xz, or zstd
21
-
22
- |Encryption
23
- |GPG signature verification
24
-
25
- |Archive Support
26
- |Single package with CPIO payload
27
-
28
- |Large Files
29
- |Limited by CPIO and compression
30
-
31
- |Best For
32
- |Linux software distribution, RPM-based systems
33
- |===
11
+ RPM is the package format of Red Hat-based Linux distributions. An
12
+ RPM file is a lead, signature, metadata header and a payload — a
13
+ CPIO archive compressed with gzip/bzip2/xz/zstd.
34
14
 
35
15
  == Basic Usage
36
16
 
37
- === Read RPM Package Metadata
17
+ === Open a Package
38
18
 
39
19
  [source,ruby]
40
20
  ----
41
- # Open and read RPM metadata
42
- rpm = Omnizip::Rpm.open('package.rpm')
43
-
44
- puts "Name: #{rpm.name}"
45
- puts "Version: #{rpm.version}"
46
- puts "Release: #{rpm.release}"
47
- puts "Architecture: #{rpm.arch}"
48
- puts "Summary: #{rpm.summary}"
49
- puts "Description: #{rpm.description}"
50
-
51
- # List package contents
52
- rpm.each_entry do |entry|
53
- puts "#{entry.path} (#{entry.size} bytes)"
21
+ Omnizip::Formats::Rpm.open('package.rpm') do |rpm|
22
+ puts rpm.files.size
54
23
  end
55
24
 
25
+ rpm = Omnizip::Formats::Rpm.open('package.rpm')
56
26
  rpm.close
57
27
  ----
58
28
 
59
- === Extract RPM Package
29
+ === Package Metadata
60
30
 
61
31
  [source,ruby]
62
32
  ----
63
- # Extract all files from RPM
64
- Omnizip::Rpm.extract('package.rpm', '/output/directory/')
65
-
66
- # Extract specific files
67
- Omnizip::Rpm.open('package.rpm') do |rpm|
68
- rpm.extract_files(['usr/bin/app', 'usr/share/man/man1/app.1.gz'], '/output/')
33
+ info = Omnizip::Formats::Rpm.info('package.rpm')
34
+ puts info[:name] # => "app"
35
+ puts info[:version]
36
+ puts info[:release]
37
+ puts info[:arch]
38
+ puts info[:summary]
39
+ puts info[:license]
40
+ puts info[:vendor]
41
+ puts info[:build_time]
42
+ puts info[:file_count]
43
+
44
+ # Same details on the reader
45
+ Omnizip::Formats::Rpm.open('package.rpm') do |rpm|
46
+ puts rpm.name
47
+ puts rpm.version
48
+ puts rpm.license
69
49
  end
70
50
  ----
71
51
 
72
- === Check RPM Signature
52
+ === List and Extract Files
73
53
 
74
54
  [source,ruby]
75
55
  ----
76
- # Verify RPM signature (requires GPG keys)
77
- rpm = Omnizip::Rpm.open('package.rpm')
78
- if rpm.signed?
79
- puts "Package is GPG signed"
80
- puts "Key ID: #{rpm.signature_key_id}"
56
+ # File paths inside the payload
57
+ Omnizip::Formats::Rpm.list('package.rpm')
58
+
59
+ # Extract the payload (CPIO contents)
60
+ Omnizip::Formats::Rpm.extract('package.rpm', '/output/directory/')
61
+
62
+ # Or per-reader
63
+ Omnizip::Formats::Rpm.open('package.rpm') do |rpm|
64
+ rpm.extract('/output/directory/')
81
65
  end
82
66
  ----
83
67
 
84
68
  == RPM Structure
85
69
 
86
- RPM packages consist of:
87
-
88
70
  . **Lead** - 96 bytes identifying the package
89
- . **Signature** - Header with cryptographic signatures
90
- . **Header** - Metadata (name, version, dependencies, scripts)
91
- . **Payload** - CPIO archive compressed with gzip/bzip2/xz/zstd
71
+ . **Signature** - cryptographic signatures
72
+ . **Header** - metadata (name, version, dependencies)
73
+ . **Payload** - CPIO archive
92
74
 
93
75
  [source,ruby]
94
76
  ----
95
- # Internal RPM structure
96
- rpm = Omnizip::Rpm.open('package.rpm')
97
-
98
- # Access lead
77
+ rpm = Omnizip::Formats::Rpm.open('package.rpm')
99
78
  puts rpm.lead.inspect
100
-
101
- # Access signature
102
79
  puts rpm.signature.inspect
103
-
104
- # Access header (metadata)
105
80
  puts rpm.header.inspect
106
-
107
- # Access payload directly (compressed CPIO)
108
- puts rpm.payload.inspect
109
81
  ----
110
82
 
111
- == Payload Extraction
112
-
113
- The RPM payload is a CPIO archive that can be extracted:
83
+ == Payload
114
84
 
115
85
  [source,ruby]
116
86
  ----
117
- # Extract only the payload (files)
118
- Omnizip::Rpm.open('package.rpm') do |rpm|
119
- rpm.extract_payload('/output/directory/')
120
- end
87
+ Omnizip::Formats::Rpm.open('package.rpm') do |rpm|
88
+ puts "Compression: #{rpm.payload_compressor}"
121
89
 
122
- # Access payload entries directly
123
- Omnizip::Rpm.open('package.rpm') do |rpm|
124
- rpm.each_payload_entry do |entry|
125
- puts "Entry: #{entry.path}"
126
- puts "Content: #{entry.read[0, 100]}..."
127
- end
90
+ # Compressed CPIO bytes; decompress with the matching format
91
+ payload = rpm.payload
128
92
  end
129
93
  ----
130
94
 
131
- == Compression Types
132
-
133
- RPM supports four payload compression formats:
134
-
135
- [cols="2,1,1"]
136
- |===
137
- |Format |Extension |Compression Ratio
138
-
139
- |gzip
140
- |.gz
141
- |Good
142
-
143
- |bzip2
144
- |.bz2
145
- |Better
146
-
147
- |xz
148
- |.xz
149
- |Best
150
-
151
- |zstd
152
- |.zst
153
- |Best (modern)
154
- |===
155
-
156
- [source,ruby]
157
- ----
158
- # Check compression type
159
- rpm = Omnizip::Rpm.open('package.rpm')
160
- puts "Compression: #{rpm.compression_type}"
161
-
162
- # Handle different compression types
163
- case rpm.compression_type
164
- when :gzip
165
- puts "Using gzip decompression"
166
- when :bzip2
167
- puts "Using bzip2 decompression"
168
- when :xz
169
- puts "Using xz decompression"
170
- when :zstd
171
- puts "Using zstd decompression"
172
- end
173
- ----
174
-
175
- == Scriptlets
176
-
177
- RPM packages can contain pre/post install scripts:
178
-
179
- [source,ruby]
180
- ----
181
- # Access scriptlets
182
- rpm = Omnizip::Rpm.open('package.rpm')
183
-
184
- puts "Pre-install script: #{rpm.pre_install_script}"
185
- puts "Post-install script: #{rpm.post_install_script}"
186
- puts "Pre-uninstall script: #{rpm.pre_uninstall_script}"
187
- puts "Post-uninstall script: #{rpm.post_uninstall_script}"
188
- ----
189
-
190
95
  == Dependencies
191
96
 
192
- RPM packages declare dependencies:
193
-
194
- [source,ruby]
195
- ----
196
- # Access dependencies
197
- rpm = Omnizip::Rpm.open('package.rpm')
198
-
199
- # Required packages
200
- puts "Requires:"
201
- rpm.requires.each do |dep|
202
- puts " #{dep.name} #{dep.version}"
203
- end
204
-
205
- # Provided capabilities
206
- puts "Provides:"
207
- rpm.provides.each do |cap|
208
- puts " #{cap.name}"
209
- end
210
-
211
- # Required capabilities
212
- puts "Requires:"
213
- rpm.requires.each do |req|
214
- puts " #{req.name} #{req.version}"
215
- end
216
-
217
- # Conflicts
218
- puts "Conflicts:"
219
- rpm.conflicts.each do |conf|
220
- puts " #{conf.name} #{conf.version}"
221
- end
222
- ----
223
-
224
- == File Information
225
-
226
- RPM tracks detailed file information:
227
-
228
97
  [source,ruby]
229
98
  ----
230
- # Access file entries
231
- Omnizip::Rpm.open('package.rpm') do |rpm|
232
- rpm.each_file do |file|
233
- puts "Path: #{file.path}"
234
- puts "Size: #{file.size}"
235
- puts "Mode: #{file.mode.to_s(8)}"
236
- puts "Owner: #{file.owner}"
237
- puts "Group: #{file.group}"
238
- puts "MD5: #{file.md5}" if file.md5
239
- puts "Is config: #{file.config?}"
240
- puts "Is doc: #{file.doc?}"
241
- end
99
+ Omnizip::Formats::Rpm.open('package.rpm') do |rpm|
100
+ rpm.requires.each { |name, op, version| puts "#{name} #{op} #{version}" }
101
+ rpm.provides.each { |name, op, version| puts "#{name} #{op} #{version}" }
102
+ rpm.conflicts.each { |name, op, version| puts "#{name} #{op} #{version}" }
242
103
  end
243
104
  ----
244
105
 
245
106
  == See Also
246
107
 
247
- * link:tar-format.html[TAR Format] - Related archive format
248
- * link:xz-format.html[XZ Format] - XZ compression used in some RPMs
249
- * link:cpio-format.html[CPIO Format] - Underlying payload format
108
+ * link:gzip-format.html[GZIP Format] - Common payload compression
@@ -99,15 +99,16 @@ writer.close
99
99
  [source,ruby]
100
100
  ----
101
101
  # 7z archive
102
- writer = Omnizip::Formats::SevenZip::Writer.new('archive.7z')
103
- writer.add_file('document.txt')
104
- writer.add_directory('photos/')
105
- writer.close
102
+ Omnizip::Formats::SevenZip.create('archive.7z') do |writer|
103
+ writer.add_file('document.txt')
104
+ writer.add_directory('photos/')
105
+ end
106
106
 
107
107
  # ZIP archive
108
108
  writer = Omnizip::Formats::Zip::Writer.new('archive.zip')
109
109
  writer.add_file('document.txt')
110
- writer.close
110
+ writer.add_directory('photos/')
111
+ writer.write
111
112
  ----
112
113
 
113
114
  ==== Extracting Archives
@@ -115,26 +116,24 @@ writer.close
115
116
  [source,ruby]
116
117
  ----
117
118
  # 7z archive
118
- reader = Omnizip::Formats::SevenZip::Reader.new('archive.7z')
119
- reader.extract_all('output/')
120
- reader.close
119
+ Omnizip::Formats::SevenZip.open('archive.7z') do |reader|
120
+ reader.extract_all('output/')
121
+ end
121
122
 
122
- # ZIP archive
123
- reader = Omnizip::Formats::Zip::Reader.new('archive.zip')
123
+ # ZIP archive (call .read to parse before extracting)
124
+ reader = Omnizip::Formats::Zip::Reader.new('archive.zip').read
124
125
  reader.extract_all('output/')
125
- reader.close
126
126
  ----
127
127
 
128
128
  ==== Listing Contents
129
129
 
130
130
  [source,ruby]
131
131
  ----
132
- reader = Omnizip::Formats::SevenZip::Reader.new('archive.7z')
133
- reader.open
134
- reader.each_file do |entry|
135
- puts "#{entry.name}: #{entry.size} bytes"
132
+ Omnizip::Formats::SevenZip.open('archive.7z') do |reader|
133
+ reader.list_files.each do |entry|
134
+ puts "#{entry.name}: #{entry.size} bytes"
135
+ end
136
136
  end
137
- reader.close
138
137
  ----
139
138
 
140
139
  === Error Handling
@@ -145,7 +144,7 @@ begin
145
144
  Omnizip.compress_file('input.txt', 'output.lzma')
146
145
  rescue Omnizip::CompressionError => e
147
146
  puts "Compression failed: #{e.message}"
148
- rescue Omnizip::AlgorithmNotFound => e
147
+ rescue Omnizip::AlgorithmNotFoundError => e
149
148
  puts "Algorithm not found: #{e.message}"
150
149
  rescue IOError => e
151
150
  puts "I/O error: #{e.message}"
@@ -188,11 +188,8 @@ omnizip archive create archive.zip files/ \
188
188
 
189
189
  [source,ruby]
190
190
  ----
191
- # Try RAR4 reader
191
+ # One reader covers both RAR4 and RAR5 (version auto-detected)
192
192
  reader = Omnizip::Formats::Rar::Reader.new('archive.rar')
193
-
194
- # Or RAR5 reader
195
- reader = Omnizip::Formats::Rar::Rar5::Reader.new('archive.rar')
196
193
  ----
197
194
 
198
195
  ### Getting Help
@@ -71,6 +71,8 @@ module Omnizip
71
71
  tar: -> { Omnizip::ArchiveHandlers::TarHandler },
72
72
  seven_zip: -> { Omnizip::ArchiveHandlers::SevenZipHandler },
73
73
  rar: -> { Omnizip::ArchiveHandlers::RarHandler },
74
+ cpio: -> { Omnizip::ArchiveHandlers::CpioHandler },
75
+ iso: -> { Omnizip::ArchiveHandlers::IsoHandler },
74
76
  }.freeze
75
77
  private_constant :LAZY_LOAD_TRIGGERS
76
78
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
4
+
5
+ module Omnizip
6
+ module ArchiveHandlers
7
+ # Read-only adapter for the CPIO format (initramfs images, RPM
8
+ # payloads). Extraction and listing are supported; CPIO writing
9
+ # exists at the format level but is not exposed through the
10
+ # convenience archive API.
11
+ class CpioHandler
12
+ def create(_path, **_options)
13
+ raise Omnizip::UnsupportedFormatError,
14
+ "cpio archives cannot be created through the convenience " \
15
+ "API; use Omnizip::Formats::Cpio.create directly"
16
+ end
17
+
18
+ def extract_to(path, output_dir, **_)
19
+ Omnizip::Formats::Cpio.extract(path, output_dir)
20
+ end
21
+
22
+ def list(path, details: false, **_)
23
+ entries = Omnizip::Formats::Cpio.list(path)
24
+ if details
25
+ entries.map do |e|
26
+ { name: e.name, size: e.data.bytesize,
27
+ directory: e.name.end_with?("/"), mtime: nil }
28
+ end
29
+ else
30
+ entries.map(&:name)
31
+ end
32
+ end
33
+
34
+ def read_entry(path, entry_name, **_)
35
+ entry = Omnizip::Formats::Cpio.list(path)
36
+ .find { |e| e.name == entry_name }
37
+ unless entry
38
+ raise Errno::ENOENT, "Entry not found: #{entry_name}"
39
+ end
40
+
41
+ entry.data
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ Omnizip::ArchiveHandler.register(:cpio, Omnizip::ArchiveHandlers::CpioHandler.new)
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
4
+
5
+ module Omnizip
6
+ module ArchiveHandlers
7
+ # Read-only adapter for the ISO 9660 format (disk images).
8
+ # Extraction and listing are supported; ISO writing exists at the
9
+ # format level but is not exposed through the convenience archive
10
+ # API.
11
+ class IsoHandler
12
+ def create(_path, **_options)
13
+ raise Omnizip::UnsupportedFormatError,
14
+ "iso images cannot be created through the convenience " \
15
+ "API; use Omnizip::Formats::Iso.create directly"
16
+ end
17
+
18
+ def extract_to(path, output_dir, **_)
19
+ Omnizip::Formats::Iso.extract(path, output_dir)
20
+ end
21
+
22
+ def list(path, details: false, **_)
23
+ entries = Omnizip::Formats::Iso.list(path)
24
+ if details
25
+ entries.map do |e|
26
+ { name: e.full_path, size: e.size, directory: e.directory?,
27
+ mtime: e.recording_date }
28
+ end
29
+ else
30
+ entries.map(&:full_path)
31
+ end
32
+ end
33
+
34
+ def read_entry(path, entry_name, **_)
35
+ entry = Omnizip::Formats::Iso.list(path)
36
+ .find { |e| e.full_path == entry_name }
37
+ unless entry
38
+ raise Errno::ENOENT, "Entry not found: #{entry_name}"
39
+ end
40
+ raise Errno::EISDIR, "Entry is a directory: #{entry_name}" if entry.directory?
41
+
42
+ Dir.mktmpdir("omnizip-iso-entry") do |dir|
43
+ dest = File.join(dir, "entry")
44
+ Omnizip::Formats::Iso.open(path) do |iso|
45
+ iso.extract_entry(entry_name, dest)
46
+ end
47
+ File.binread(dest)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ Omnizip::ArchiveHandler.register(:iso, Omnizip::ArchiveHandlers::IsoHandler.new)
@@ -9,5 +9,7 @@ module Omnizip
9
9
  autoload :TarHandler, "omnizip/archive_handlers/tar_handler"
10
10
  autoload :SevenZipHandler, "omnizip/archive_handlers/seven_zip_handler"
11
11
  autoload :RarHandler, "omnizip/archive_handlers/rar_handler"
12
+ autoload :CpioHandler, "omnizip/archive_handlers/cpio_handler"
13
+ autoload :IsoHandler, "omnizip/archive_handlers/iso_handler"
12
14
  end
13
15
  end
data/lib/omnizip/cli.rb CHANGED
@@ -25,6 +25,28 @@ module Omnizip
25
25
  # under Omnizip::Cli would re-trigger this file's autoload
26
26
  # mid-load.
27
27
  module Shared
28
+ # Thor 1.x does not treat --help/-h on subcommands as a request
29
+ # for help: the flag reaches the command as a positional argument
30
+ # and arity checking fails. Classes extending this module route
31
+ # those flags to the standard help output instead.
32
+ module HelpDispatch
33
+ def dispatch(meth, given_args, given_opts, config)
34
+ wants_help = [Array(given_args), Array(given_opts)].any? do |list|
35
+ list.include?("--help") || list.include?("-h")
36
+ end
37
+ if wants_help
38
+ name = meth || Array(given_args).grep_v(/\A-/).first
39
+ # Subcommand groups intercept their own --help (e.g.
40
+ # "archive create --help" must show the create usage).
41
+ return super if subcommands.include?(name)
42
+
43
+ return start(["help", name].compact, config)
44
+ end
45
+
46
+ super
47
+ end
48
+ end
49
+
28
50
  # Print a formatted error message and exit nonzero.
29
51
  #
30
52
  # @param error [StandardError] the error to display
@@ -51,6 +73,7 @@ module Omnizip
51
73
  # Profile commands subcommand group
52
74
  class ProfileCommands < Thor
53
75
  include Shared
76
+ extend Shared::HelpDispatch
54
77
 
55
78
  class << self
56
79
  def exit_on_failure?
@@ -99,6 +122,7 @@ module Omnizip
99
122
  # Archive commands subcommand group
100
123
  class ArchiveCommands < Thor
101
124
  include Shared
125
+ extend Shared::HelpDispatch
102
126
 
103
127
  class << self
104
128
  def exit_on_failure?
@@ -328,6 +352,7 @@ module Omnizip
328
352
  # files using various compression algorithms.
329
353
  class Cli < Thor
330
354
  include Shared
355
+ extend Shared::HelpDispatch
331
356
 
332
357
  class << self
333
358
  def exit_on_failure?
@@ -230,16 +230,18 @@ module Omnizip
230
230
  ".7z" => :seven_zip,
231
231
  }.freeze
232
232
 
233
- # Extensions naming real formats this gem can READ but not write
234
- # through the convenience API. Writing them a ZIP under a foreign
235
- # name was silent corruption; failing truthfully is the only
236
- # honest behavior.
233
+ # Extensions naming formats with read-only convenience routing.
234
+ # Writing them a ZIP under a foreign name was silent corruption;
235
+ # creation raises truthfully instead (the format-level writers
236
+ # exist for RAR/CPIO/ISO but are not part of the archive API).
237
237
  READ_ONLY_FORMAT_EXTENSIONS = [".rar", ".iso", ".cpio"].freeze
238
238
 
239
239
  # Extensions whose format has a READ-ONLY handler: extraction and
240
240
  # listing route to it, while creation keeps raising.
241
241
  READ_ARCHIVE_FORMAT_EXTENSIONS = {
242
242
  ".rar" => :rar,
243
+ ".cpio" => :cpio,
244
+ ".iso" => :iso,
243
245
  }.freeze
244
246
 
245
247
  # Extension -> single-file decompressor (stream interface).
@@ -43,6 +43,12 @@ module Omnizip
43
43
  # Allocate sectors for directories and files
44
44
  allocate_sectors(tree)
45
45
 
46
+ # Tree nodes are separate hashes from @files entries; copy
47
+ # the allocated file locations onto the tree so directory
48
+ # records reference the real extents (otherwise every
49
+ # record points at sector 0).
50
+ assign_file_locations(tree)
51
+
46
52
  # Build path table
47
53
  path_table = build_path_table(tree)
48
54
 
@@ -143,6 +149,24 @@ module Omnizip
143
149
  allocate_file_sectors
144
150
  end
145
151
 
152
+ # Copy allocated file locations onto the matching tree nodes
153
+ #
154
+ # @param tree [Hash] Directory tree root
155
+ def assign_file_locations(tree)
156
+ walk = lambda do |node|
157
+ (node[:children] || []).each do |child|
158
+ if child[:directory]
159
+ walk.call(child)
160
+ else
161
+ match = @files.find { |f| f[:iso_path] == child[:iso_path] }
162
+ child[:location] = match[:location] if match
163
+ child[:size] = match[:size] if match
164
+ end
165
+ end
166
+ end
167
+ walk.call(tree)
168
+ end
169
+
146
170
  # Allocate sectors for a directory
147
171
  #
148
172
  # @param dir_node [Hash] Directory node
@@ -90,8 +90,8 @@ module Omnizip
90
90
  @entries.each do |entry|
91
91
  next if entry.current_directory? || entry.parent_directory?
92
92
 
93
- output_path = File.join(output_dir, entry.name)
94
- extract_entry(entry.name, output_path)
93
+ output_path = File.join(output_dir, entry.full_path)
94
+ extract_entry(entry.full_path, output_path)
95
95
  end
96
96
  end
97
97
 
@@ -178,8 +178,13 @@ module Omnizip
178
178
  preparer: "OMNIZIP #{Omnizip::VERSION}",
179
179
  application: "OMNIZIP",
180
180
  level: 2,
181
- rock_ridge: true,
182
- joliet: true,
181
+ # Rock Ridge System Use fields and Joliet UCS-2 directory
182
+ # trees are not implemented; advertising either extension
183
+ # makes readers misparse the image (e.g. 7-Zip reads the
184
+ # ASCII names as UCS-2 via the cloned SVD), so both stay
185
+ # off until real implementations land.
186
+ rock_ridge: false,
187
+ joliet: false,
183
188
  }
184
189
  end
185
190
 
@@ -193,6 +198,7 @@ module Omnizip
193
198
  source: File.expand_path(dir_path),
194
199
  iso_path: iso_path,
195
200
  stat: File.stat(dir_path),
201
+ directory: true,
196
202
  }
197
203
 
198
204
  # Add all contents
@@ -219,6 +225,7 @@ module Omnizip
219
225
  source: File.expand_path(dir_path),
220
226
  iso_path: iso_path,
221
227
  stat: File.stat(dir_path),
228
+ directory: true,
222
229
  }
223
230
 
224
231
  Dir.foreach(dir_path) do |entry|
@@ -283,13 +290,22 @@ module Omnizip
283
290
  # @param builder [VolumeBuilder] Volume builder
284
291
  # @param dir_structure [Hash] Directory structure
285
292
  def write_volume_descriptors(io, builder, dir_structure)
293
+ # Fixed layout: 16 PVD, 17 terminator, 18 path table (L),
294
+ # 19 path table (BE), directory data from sector 22.
295
+ root = dir_structure[:root].merge(
296
+ total_sectors: dir_structure[:total_sectors],
297
+ path_table_size: dir_structure[:path_table_size],
298
+ path_table_location: 18,
299
+ path_table_location_be: 19,
300
+ )
301
+
286
302
  # Write primary volume descriptor
287
- pvd = builder.build_primary(dir_structure[:root])
303
+ pvd = builder.build_primary(root)
288
304
  io.write(pvd)
289
305
 
290
306
  # Write Joliet supplementary descriptor if enabled
291
307
  if @joliet
292
- svd = builder.build_joliet(dir_structure[:root])
308
+ svd = builder.build_joliet(root)
293
309
  io.write(svd)
294
310
  end
295
311
 
@@ -69,7 +69,9 @@ module Omnizip
69
69
  # @param path [String] Path to ISO file
70
70
  # @return [Array<DirectoryRecord>] Directory entries
71
71
  def self.list(path)
72
- open(path, &:entries)
72
+ entries = nil
73
+ open(path) { |iso| entries = iso.entries }
74
+ entries
73
75
  end
74
76
 
75
77
  # Extract ISO contents