omnizip 0.3.1
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +617 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Iso
|
|
6
|
+
# ISO 9660 Path Table
|
|
7
|
+
# Provides efficient directory hierarchy lookup
|
|
8
|
+
class PathTable
|
|
9
|
+
attr_reader :entries
|
|
10
|
+
|
|
11
|
+
# Path table entry
|
|
12
|
+
class Entry
|
|
13
|
+
attr_reader :name, :location, :parent_directory_number
|
|
14
|
+
|
|
15
|
+
# Initialize entry
|
|
16
|
+
#
|
|
17
|
+
# @param name [String] Directory name
|
|
18
|
+
# @param location [Integer] LBA location
|
|
19
|
+
# @param parent_directory_number [Integer] Parent directory index
|
|
20
|
+
def initialize(name, location, parent_directory_number)
|
|
21
|
+
@name = name
|
|
22
|
+
@location = location
|
|
23
|
+
@parent_directory_number = parent_directory_number
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Get full path by traversing parent entries
|
|
27
|
+
#
|
|
28
|
+
# @param entries [Array<Entry>] All path table entries
|
|
29
|
+
# @return [String] Full path
|
|
30
|
+
def full_path(entries)
|
|
31
|
+
return "/" if @parent_directory_number == 1 # Root
|
|
32
|
+
|
|
33
|
+
parts = [@name]
|
|
34
|
+
current_parent = @parent_directory_number
|
|
35
|
+
|
|
36
|
+
while current_parent > 1
|
|
37
|
+
parent = entries[current_parent - 1]
|
|
38
|
+
break unless parent
|
|
39
|
+
|
|
40
|
+
parts.unshift(parent.name)
|
|
41
|
+
current_parent = parent.parent_directory_number
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
"/#{parts.join('/')}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Initialize path table
|
|
49
|
+
def initialize
|
|
50
|
+
@entries = []
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Parse path table from binary data
|
|
54
|
+
#
|
|
55
|
+
# @param data [String] Binary path table data
|
|
56
|
+
# @param size [Integer] Size of path table in bytes
|
|
57
|
+
# @return [PathTable] Parsed path table
|
|
58
|
+
def self.parse(data, size)
|
|
59
|
+
new.tap { |pt| pt.parse(data, size) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Parse path table data
|
|
63
|
+
#
|
|
64
|
+
# @param data [String] Binary data
|
|
65
|
+
# @param size [Integer] Size in bytes
|
|
66
|
+
def parse(data, size)
|
|
67
|
+
offset = 0
|
|
68
|
+
|
|
69
|
+
while offset < size
|
|
70
|
+
# Byte 0: Length of directory identifier
|
|
71
|
+
name_length = data.getbyte(offset)
|
|
72
|
+
break if name_length.zero?
|
|
73
|
+
|
|
74
|
+
# Byte 1: Extended attribute record length
|
|
75
|
+
data.getbyte(offset + 1)
|
|
76
|
+
|
|
77
|
+
# Bytes 2-5: Location of extent (little-endian)
|
|
78
|
+
location = data[offset + 2, 4].unpack1("V")
|
|
79
|
+
|
|
80
|
+
# Bytes 6-7: Parent directory number
|
|
81
|
+
parent = data[offset + 6, 2].unpack1("v")
|
|
82
|
+
|
|
83
|
+
# Bytes 8+: Directory identifier
|
|
84
|
+
name = data[offset + 8, name_length].strip
|
|
85
|
+
|
|
86
|
+
# Handle root directory (zero-length name)
|
|
87
|
+
name = "/" if name.empty?
|
|
88
|
+
|
|
89
|
+
# Create entry
|
|
90
|
+
@entries << Entry.new(name, location, parent)
|
|
91
|
+
|
|
92
|
+
# Move to next entry
|
|
93
|
+
# Length is 8 + name_length, padded to even boundary
|
|
94
|
+
record_length = 8 + name_length
|
|
95
|
+
record_length += 1 if record_length.odd?
|
|
96
|
+
offset += record_length
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Find entry by directory name
|
|
101
|
+
#
|
|
102
|
+
# @param name [String] Directory name
|
|
103
|
+
# @return [Entry, nil] Found entry or nil
|
|
104
|
+
def find_by_name(name)
|
|
105
|
+
@entries.find { |e| e.name == name }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Find entry by location
|
|
109
|
+
#
|
|
110
|
+
# @param location [Integer] LBA location
|
|
111
|
+
# @return [Entry, nil] Found entry or nil
|
|
112
|
+
def find_by_location(location)
|
|
113
|
+
@entries.find { |e| e.location == location }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Get root directory entry
|
|
117
|
+
#
|
|
118
|
+
# @return [Entry] Root entry
|
|
119
|
+
def root
|
|
120
|
+
@entries.first
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "volume_descriptor"
|
|
4
|
+
require_relative "directory_record"
|
|
5
|
+
require_relative "path_table"
|
|
6
|
+
require "fileutils"
|
|
7
|
+
|
|
8
|
+
module Omnizip
|
|
9
|
+
module Formats
|
|
10
|
+
module Iso
|
|
11
|
+
# ISO 9660 image reader
|
|
12
|
+
# Provides read-only access to ISO filesystem
|
|
13
|
+
class Reader
|
|
14
|
+
attr_reader :file_path, :primary_volume_descriptor, :entries,
|
|
15
|
+
:path_table
|
|
16
|
+
|
|
17
|
+
# Initialize reader
|
|
18
|
+
#
|
|
19
|
+
# @param file_path [String] Path to ISO file
|
|
20
|
+
def initialize(file_path)
|
|
21
|
+
@file_path = file_path
|
|
22
|
+
@primary_volume_descriptor = nil
|
|
23
|
+
@entries = []
|
|
24
|
+
@path_table = nil
|
|
25
|
+
@io = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Open and parse ISO image
|
|
29
|
+
#
|
|
30
|
+
# @raise [RuntimeError] if file cannot be opened or is invalid
|
|
31
|
+
def open
|
|
32
|
+
@io = File.open(@file_path, "rb")
|
|
33
|
+
parse_volume_descriptors
|
|
34
|
+
parse_directory_structure
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Close ISO file
|
|
39
|
+
def close
|
|
40
|
+
@io&.close
|
|
41
|
+
@io = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Get volume identifier
|
|
45
|
+
#
|
|
46
|
+
# @return [String] Volume name
|
|
47
|
+
def volume_identifier
|
|
48
|
+
@primary_volume_descriptor&.volume_identifier || ""
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Get system identifier
|
|
52
|
+
#
|
|
53
|
+
# @return [String] System identifier
|
|
54
|
+
def system_identifier
|
|
55
|
+
@primary_volume_descriptor&.system_identifier || ""
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Get volume size in bytes
|
|
59
|
+
#
|
|
60
|
+
# @return [Integer] Total volume size
|
|
61
|
+
def volume_size
|
|
62
|
+
return 0 unless @primary_volume_descriptor
|
|
63
|
+
|
|
64
|
+
@primary_volume_descriptor.volume_space_size * Iso::SECTOR_SIZE
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Extract entry to file
|
|
68
|
+
#
|
|
69
|
+
# @param entry_path [String] Path in ISO
|
|
70
|
+
# @param output_path [String] Destination path
|
|
71
|
+
def extract_entry(entry_path, output_path)
|
|
72
|
+
entry = find_entry(entry_path)
|
|
73
|
+
raise "Entry not found: #{entry_path}" unless entry
|
|
74
|
+
|
|
75
|
+
if entry.directory?
|
|
76
|
+
FileUtils.mkdir_p(output_path)
|
|
77
|
+
else
|
|
78
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
|
79
|
+
data = read_file_data(entry)
|
|
80
|
+
File.binwrite(output_path, data)
|
|
81
|
+
|
|
82
|
+
# Set modification time if available
|
|
83
|
+
File.utime(entry.mtime, entry.mtime, output_path) if entry.mtime
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Extract all entries
|
|
88
|
+
#
|
|
89
|
+
# @param output_dir [String] Output directory
|
|
90
|
+
def extract_all(output_dir)
|
|
91
|
+
FileUtils.mkdir_p(output_dir)
|
|
92
|
+
|
|
93
|
+
@entries.each do |entry|
|
|
94
|
+
next if entry.current_directory? || entry.parent_directory?
|
|
95
|
+
|
|
96
|
+
output_path = File.join(output_dir, entry.name)
|
|
97
|
+
extract_entry(entry.name, output_path)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# List all entries
|
|
102
|
+
#
|
|
103
|
+
# @return [Array<DirectoryRecord>] All entries
|
|
104
|
+
def list_files
|
|
105
|
+
@entries.reject { |e| e.current_directory? || e.parent_directory? }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
# Parse volume descriptors
|
|
111
|
+
def parse_volume_descriptors
|
|
112
|
+
sector = Iso::VOLUME_DESCRIPTOR_START
|
|
113
|
+
|
|
114
|
+
loop do
|
|
115
|
+
@io.seek(sector * Iso::SECTOR_SIZE)
|
|
116
|
+
data = @io.read(Iso::SECTOR_SIZE)
|
|
117
|
+
raise "Failed to read volume descriptor" unless data
|
|
118
|
+
|
|
119
|
+
vd = VolumeDescriptor.parse(data)
|
|
120
|
+
|
|
121
|
+
case vd.type
|
|
122
|
+
when Iso::VD_PRIMARY
|
|
123
|
+
@primary_volume_descriptor = vd
|
|
124
|
+
when Iso::VD_TERMINATOR
|
|
125
|
+
break
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
sector += 1
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
return if @primary_volume_descriptor
|
|
132
|
+
|
|
133
|
+
raise "No primary volume descriptor found"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Parse directory structure
|
|
137
|
+
def parse_directory_structure
|
|
138
|
+
return unless @primary_volume_descriptor
|
|
139
|
+
|
|
140
|
+
root = @primary_volume_descriptor.root_directory_record
|
|
141
|
+
parse_directory(root, "")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Recursively parse directory
|
|
145
|
+
#
|
|
146
|
+
# @param dir_record [DirectoryRecord] Directory record
|
|
147
|
+
# @param path_prefix [String] Path prefix
|
|
148
|
+
def parse_directory(dir_record, path_prefix)
|
|
149
|
+
# Read directory data
|
|
150
|
+
@io.seek(dir_record.location * Iso::SECTOR_SIZE)
|
|
151
|
+
dir_data = @io.read(dir_record.data_length)
|
|
152
|
+
|
|
153
|
+
offset = 0
|
|
154
|
+
while offset < dir_data.bytesize
|
|
155
|
+
length = dir_data.getbyte(offset)
|
|
156
|
+
break if length.zero? # End of directory
|
|
157
|
+
|
|
158
|
+
record = DirectoryRecord.parse(dir_data, offset)
|
|
159
|
+
|
|
160
|
+
# Skip current and parent directory entries
|
|
161
|
+
unless record.current_directory? || record.parent_directory?
|
|
162
|
+
# Set full path
|
|
163
|
+
full_path = path_prefix.empty? ? record.name : "#{path_prefix}/#{record.name}"
|
|
164
|
+
record.instance_variable_set(:@full_path, full_path)
|
|
165
|
+
|
|
166
|
+
@entries << record
|
|
167
|
+
|
|
168
|
+
# Recursively parse subdirectories
|
|
169
|
+
parse_directory(record, full_path) if record.directory?
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
offset += length
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Find entry by path
|
|
177
|
+
#
|
|
178
|
+
# @param path [String] Entry path
|
|
179
|
+
# @return [DirectoryRecord, nil] Found entry
|
|
180
|
+
def find_entry(path)
|
|
181
|
+
# Normalize path
|
|
182
|
+
path = path.gsub(%r{^/+}, "").gsub(%r{/+$}, "")
|
|
183
|
+
@entries.find { |e| e.instance_variable_get(:@full_path) == path }
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Read file data
|
|
187
|
+
#
|
|
188
|
+
# @param entry [DirectoryRecord] File entry
|
|
189
|
+
# @return [String] File data
|
|
190
|
+
def read_file_data(entry)
|
|
191
|
+
@io.seek(entry.location * Iso::SECTOR_SIZE)
|
|
192
|
+
@io.read(entry.size)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Iso
|
|
6
|
+
# Rock Ridge Extensions for ISO 9660
|
|
7
|
+
#
|
|
8
|
+
# Implements System Use Sharing Protocol (SUSP) and Rock Ridge
|
|
9
|
+
# extensions to store Unix file attributes, permissions, symbolic links,
|
|
10
|
+
# and device nodes in ISO 9660 images.
|
|
11
|
+
#
|
|
12
|
+
# Rock Ridge allows ISO images to preserve full Unix filesystem semantics.
|
|
13
|
+
module RockRidge
|
|
14
|
+
# SUSP/Rock Ridge signature identifiers
|
|
15
|
+
module Signatures
|
|
16
|
+
SP = "SP" # System Use Sharing Protocol indicator
|
|
17
|
+
CE = "CE" # Continuation area
|
|
18
|
+
PX = "PX" # POSIX file attributes
|
|
19
|
+
PN = "PN" # POSIX device number
|
|
20
|
+
SL = "SL" # Symbolic link
|
|
21
|
+
NM = "NM" # Alternate name
|
|
22
|
+
CL = "CL" # Child link
|
|
23
|
+
PL = "PL" # Parent link
|
|
24
|
+
RE = "RE" # Relocated directory
|
|
25
|
+
TF = "TF" # Time stamps
|
|
26
|
+
SF = "SF" # Sparse file
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# System Use Entry
|
|
30
|
+
class SUEntry
|
|
31
|
+
attr_accessor :signature, :length, :version, :data
|
|
32
|
+
|
|
33
|
+
# Initialize entry
|
|
34
|
+
#
|
|
35
|
+
# @param signature [String] 2-byte signature
|
|
36
|
+
# @param version [Integer] Entry version
|
|
37
|
+
# @param data [String] Entry data
|
|
38
|
+
def initialize(signature, version: 1, data: "")
|
|
39
|
+
@signature = signature
|
|
40
|
+
@length = 4 + data.bytesize
|
|
41
|
+
@version = version
|
|
42
|
+
@data = data
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Convert to binary
|
|
46
|
+
#
|
|
47
|
+
# @return [String] Binary representation
|
|
48
|
+
def to_binary
|
|
49
|
+
result = +""
|
|
50
|
+
result << @signature # 2 bytes
|
|
51
|
+
result << [@length].pack("C") # 1 byte
|
|
52
|
+
result << [@version].pack("C") # 1 byte
|
|
53
|
+
result << @data
|
|
54
|
+
result
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Parse from binary
|
|
58
|
+
#
|
|
59
|
+
# @param data [String] Binary data
|
|
60
|
+
# @param offset [Integer] Starting offset
|
|
61
|
+
# @return [SUEntry] Parsed entry
|
|
62
|
+
def self.parse(data, offset = 0)
|
|
63
|
+
signature = data[offset, 2]
|
|
64
|
+
length = data.getbyte(offset + 2)
|
|
65
|
+
version = data.getbyte(offset + 3)
|
|
66
|
+
entry_data = data[offset + 4, length - 4]
|
|
67
|
+
|
|
68
|
+
new(signature, version: version, data: entry_data)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Add Rock Ridge extensions to directory record
|
|
73
|
+
#
|
|
74
|
+
# @param record_data [String] Directory record data
|
|
75
|
+
# @param file_stat [File::Stat] File statistics
|
|
76
|
+
# @param name [String] File name
|
|
77
|
+
# @param is_symlink [Boolean] Is this a symbolic link
|
|
78
|
+
# @return [String] Updated record with Rock Ridge fields
|
|
79
|
+
def self.add_extensions(record_data, file_stat, name, is_symlink: false)
|
|
80
|
+
system_use = +""
|
|
81
|
+
|
|
82
|
+
# Add SP entry (first entry in root directory only)
|
|
83
|
+
# This indicates Rock Ridge is being used
|
|
84
|
+
# For simplicity, we'll add to all entries
|
|
85
|
+
|
|
86
|
+
# Add PX (POSIX attributes)
|
|
87
|
+
system_use << build_px_entry(file_stat).to_binary
|
|
88
|
+
|
|
89
|
+
# Add TF (timestamps)
|
|
90
|
+
system_use << build_tf_entry(file_stat).to_binary
|
|
91
|
+
|
|
92
|
+
# Add NM (alternate name) if different from ISO name
|
|
93
|
+
unless name == File.basename(name).upcase
|
|
94
|
+
system_use << build_nm_entry(name).to_binary
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Add SL (symbolic link) if applicable
|
|
98
|
+
if is_symlink
|
|
99
|
+
link_target = File.readlink(file_stat)
|
|
100
|
+
system_use << build_sl_entry(link_target).to_binary
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Append system use to record
|
|
104
|
+
record_data + system_use
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Build PX (POSIX attributes) entry
|
|
108
|
+
#
|
|
109
|
+
# @param stat [File::Stat] File statistics
|
|
110
|
+
# @return [SUEntry] PX entry
|
|
111
|
+
def self.build_px_entry(stat)
|
|
112
|
+
data = +""
|
|
113
|
+
|
|
114
|
+
# File mode (both-endian)
|
|
115
|
+
data << [stat.mode].pack("V")
|
|
116
|
+
data << [stat.mode].pack("N")
|
|
117
|
+
|
|
118
|
+
# Number of links (both-endian)
|
|
119
|
+
data << [stat.nlink].pack("V")
|
|
120
|
+
data << [stat.nlink].pack("N")
|
|
121
|
+
|
|
122
|
+
# User ID (both-endian)
|
|
123
|
+
data << [stat.uid].pack("V")
|
|
124
|
+
data << [stat.uid].pack("N")
|
|
125
|
+
|
|
126
|
+
# Group ID (both-endian)
|
|
127
|
+
data << [stat.gid].pack("V")
|
|
128
|
+
data << [stat.gid].pack("N")
|
|
129
|
+
|
|
130
|
+
# Inode number (both-endian) - optional
|
|
131
|
+
data << [stat.ino].pack("V")
|
|
132
|
+
data << [stat.ino].pack("N")
|
|
133
|
+
|
|
134
|
+
SUEntry.new(Signatures::PX, data: data)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Build TF (timestamps) entry
|
|
138
|
+
#
|
|
139
|
+
# @param stat [File::Stat] File statistics
|
|
140
|
+
# @return [SUEntry] TF entry
|
|
141
|
+
def self.build_tf_entry(stat)
|
|
142
|
+
# Flags indicating which timestamps are present
|
|
143
|
+
# Bit 0: Creation time
|
|
144
|
+
# Bit 1: Modify time
|
|
145
|
+
# Bit 2: Access time
|
|
146
|
+
# Bit 3: Attributes time
|
|
147
|
+
# Bit 4: Backup time
|
|
148
|
+
# Bit 5: Expiration time
|
|
149
|
+
# Bit 6: Effective time
|
|
150
|
+
# Bit 7: Long format (17 bytes instead of 7)
|
|
151
|
+
|
|
152
|
+
flags = 0b00000110 # Modify and access times, short format
|
|
153
|
+
|
|
154
|
+
data = +""
|
|
155
|
+
data << [flags].pack("C")
|
|
156
|
+
|
|
157
|
+
# Modification time (7-byte format)
|
|
158
|
+
data << encode_time_7byte(stat.mtime)
|
|
159
|
+
|
|
160
|
+
# Access time (7-byte format)
|
|
161
|
+
data << encode_time_7byte(stat.atime)
|
|
162
|
+
|
|
163
|
+
SUEntry.new(Signatures::TF, data: data)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Build NM (alternate name) entry
|
|
167
|
+
#
|
|
168
|
+
# @param name [String] File name
|
|
169
|
+
# @return [SUEntry] NM entry
|
|
170
|
+
def self.build_nm_entry(name)
|
|
171
|
+
# Flags
|
|
172
|
+
# Bit 0: CONTINUE (name continues in next NM)
|
|
173
|
+
# Bit 1: CURRENT (name is ".")
|
|
174
|
+
# Bit 2: PARENT (name is "..")
|
|
175
|
+
flags = 0
|
|
176
|
+
|
|
177
|
+
data = +""
|
|
178
|
+
data << [flags].pack("C")
|
|
179
|
+
data << name
|
|
180
|
+
|
|
181
|
+
SUEntry.new(Signatures::NM, data: data)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Build SL (symbolic link) entry
|
|
185
|
+
#
|
|
186
|
+
# @param target [String] Link target
|
|
187
|
+
# @return [SUEntry] SL entry
|
|
188
|
+
def self.build_sl_entry(target)
|
|
189
|
+
# Flags
|
|
190
|
+
# Bit 0: CONTINUE (link continues in next SL)
|
|
191
|
+
flags = 0
|
|
192
|
+
|
|
193
|
+
data = +""
|
|
194
|
+
data << [flags].pack("C")
|
|
195
|
+
|
|
196
|
+
# Component flags
|
|
197
|
+
# Bit 0: CONTINUE (component continues)
|
|
198
|
+
# Bit 1: CURRENT (component is ".")
|
|
199
|
+
# Bit 2: PARENT (component is "..")
|
|
200
|
+
# Bit 3: ROOT (component is root)
|
|
201
|
+
comp_flags = 0
|
|
202
|
+
|
|
203
|
+
# Component length
|
|
204
|
+
data << [comp_flags].pack("C")
|
|
205
|
+
data << [target.bytesize].pack("C")
|
|
206
|
+
data << target
|
|
207
|
+
|
|
208
|
+
SUEntry.new(Signatures::SL, data: data)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Build PN (POSIX device number) entry
|
|
212
|
+
#
|
|
213
|
+
# @param stat [File::Stat] File statistics
|
|
214
|
+
# @return [SUEntry] PN entry
|
|
215
|
+
def self.build_pn_entry(stat)
|
|
216
|
+
data = +""
|
|
217
|
+
|
|
218
|
+
# Device number high (both-endian)
|
|
219
|
+
dev_high = (stat.rdev >> 32) & 0xFFFFFFFF
|
|
220
|
+
data << [dev_high].pack("V")
|
|
221
|
+
data << [dev_high].pack("N")
|
|
222
|
+
|
|
223
|
+
# Device number low (both-endian)
|
|
224
|
+
dev_low = stat.rdev & 0xFFFFFFFF
|
|
225
|
+
data << [dev_low].pack("V")
|
|
226
|
+
data << [dev_low].pack("N")
|
|
227
|
+
|
|
228
|
+
SUEntry.new(Signatures::PN, data: data)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Encode time in 7-byte format
|
|
232
|
+
#
|
|
233
|
+
# @param time [Time] Time to encode
|
|
234
|
+
# @return [String] 7-byte encoded time
|
|
235
|
+
def self.encode_time_7byte(time)
|
|
236
|
+
[
|
|
237
|
+
time.year - 1900,
|
|
238
|
+
time.month,
|
|
239
|
+
time.day,
|
|
240
|
+
time.hour,
|
|
241
|
+
time.min,
|
|
242
|
+
time.sec,
|
|
243
|
+
0, # GMT offset
|
|
244
|
+
].pack("C7")
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Parse Rock Ridge extensions from system use field
|
|
248
|
+
#
|
|
249
|
+
# @param system_use [String] System use field data
|
|
250
|
+
# @return [Hash] Parsed attributes
|
|
251
|
+
def self.parse_extensions(system_use)
|
|
252
|
+
attributes = {}
|
|
253
|
+
offset = 0
|
|
254
|
+
|
|
255
|
+
while offset < system_use.bytesize
|
|
256
|
+
entry = SUEntry.parse(system_use, offset)
|
|
257
|
+
|
|
258
|
+
case entry.signature
|
|
259
|
+
when Signatures::PX
|
|
260
|
+
attributes[:posix] = parse_px_entry(entry)
|
|
261
|
+
when Signatures::TF
|
|
262
|
+
attributes[:times] = parse_tf_entry(entry)
|
|
263
|
+
when Signatures::NM
|
|
264
|
+
attributes[:name] = parse_nm_entry(entry)
|
|
265
|
+
when Signatures::SL
|
|
266
|
+
attributes[:symlink] = parse_sl_entry(entry)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
offset += entry.length
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
attributes
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Parse PX entry
|
|
276
|
+
#
|
|
277
|
+
# @param entry [SUEntry] PX entry
|
|
278
|
+
# @return [Hash] Parsed POSIX attributes
|
|
279
|
+
def self.parse_px_entry(entry)
|
|
280
|
+
data = entry.data
|
|
281
|
+
{
|
|
282
|
+
mode: data[0, 4].unpack1("V"),
|
|
283
|
+
nlink: data[8, 4].unpack1("V"),
|
|
284
|
+
uid: data[16, 4].unpack1("V"),
|
|
285
|
+
gid: data[24, 4].unpack1("V"),
|
|
286
|
+
ino: data[32, 4].unpack1("V"),
|
|
287
|
+
}
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Parse TF entry
|
|
291
|
+
#
|
|
292
|
+
# @param entry [SUEntry] TF entry
|
|
293
|
+
# @return [Hash] Parsed timestamps
|
|
294
|
+
def self.parse_tf_entry(entry)
|
|
295
|
+
# Parse based on flags
|
|
296
|
+
flags = entry.data.getbyte(0)
|
|
297
|
+
times = {}
|
|
298
|
+
|
|
299
|
+
offset = 1
|
|
300
|
+
if flags & 0x02 # Modify time
|
|
301
|
+
times[:mtime] = parse_time_7byte(entry.data[offset, 7])
|
|
302
|
+
offset += 7
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
if flags & 0x04 # Access time
|
|
306
|
+
times[:atime] = parse_time_7byte(entry.data[offset, 7])
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
times
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Parse NM entry
|
|
313
|
+
#
|
|
314
|
+
# @param entry [SUEntry] NM entry
|
|
315
|
+
# @return [String] Alternate name
|
|
316
|
+
def self.parse_nm_entry(entry)
|
|
317
|
+
entry.data[1..]
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Parse SL entry
|
|
321
|
+
#
|
|
322
|
+
# @param entry [SUEntry] SL entry
|
|
323
|
+
# @return [String] Symbolic link target
|
|
324
|
+
def self.parse_sl_entry(entry)
|
|
325
|
+
# Skip flags byte, component flags, read length and target
|
|
326
|
+
comp_len = entry.data.getbyte(2)
|
|
327
|
+
entry.data[3, comp_len]
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Parse 7-byte time format
|
|
331
|
+
#
|
|
332
|
+
# @param data [String] 7-byte time data
|
|
333
|
+
# @return [Time] Parsed time
|
|
334
|
+
def self.parse_time_7byte(data)
|
|
335
|
+
year = 1900 + data.getbyte(0)
|
|
336
|
+
month = data.getbyte(1)
|
|
337
|
+
day = data.getbyte(2)
|
|
338
|
+
hour = data.getbyte(3)
|
|
339
|
+
minute = data.getbyte(4)
|
|
340
|
+
second = data.getbyte(5)
|
|
341
|
+
|
|
342
|
+
Time.new(year, month, day, hour, minute, second)
|
|
343
|
+
rescue ArgumentError
|
|
344
|
+
Time.now
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|