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.
Files changed (511) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rubocop_todo.yml +754 -0
  5. data/COPYING +502 -0
  6. data/Gemfile +17 -0
  7. data/LICENSE +12 -0
  8. data/README.adoc +1045 -0
  9. data/Rakefile +12 -0
  10. data/benchmark/README.md +260 -0
  11. data/benchmark/benchmark_suite.rb +125 -0
  12. data/benchmark/compression_bench.rb +181 -0
  13. data/benchmark/filter_bench.rb +180 -0
  14. data/benchmark/models/benchmark_result.rb +59 -0
  15. data/benchmark/models/comparison_result.rb +69 -0
  16. data/benchmark/profile_suite.rb +167 -0
  17. data/benchmark/reporter.rb +150 -0
  18. data/benchmark/run_benchmarks.rb +66 -0
  19. data/benchmark/test_data.rb +137 -0
  20. data/config/formats/rar3_spec.yml +91 -0
  21. data/config/formats/rar5_spec.yml +102 -0
  22. data/docs/.github/workflows/docs.yml +142 -0
  23. data/docs/.gitignore +21 -0
  24. data/docs/.lychee.toml +67 -0
  25. data/docs/Gemfile +13 -0
  26. data/docs/RAR_WRITE_SUPPORT.md +26 -0
  27. data/docs/README.md +101 -0
  28. data/docs/_config.yml +112 -0
  29. data/docs/assets/logo.svg +1 -0
  30. data/docs/assets/omnizip-logo.pdf +1540 -11
  31. data/docs/comparison/feature-matrix.adoc +694 -0
  32. data/docs/comparison/index.adoc +113 -0
  33. data/docs/comparison/vs-7zip.adoc +309 -0
  34. data/docs/comparison/vs-peazip.adoc +77 -0
  35. data/docs/comparison/vs-rubyzip.adoc +342 -0
  36. data/docs/comparison/vs-winrar.adoc +100 -0
  37. data/docs/compatibility.adoc +579 -0
  38. data/docs/concepts/index.adoc +129 -0
  39. data/docs/developer/architecture.adoc +256 -0
  40. data/docs/developer/contributing.adoc +158 -0
  41. data/docs/developer/index.adoc +25 -0
  42. data/docs/developer/testing.adoc +212 -0
  43. data/docs/getting-started/basic-usage.adoc +271 -0
  44. data/docs/getting-started/index.adoc +42 -0
  45. data/docs/getting-started/installation.adoc +138 -0
  46. data/docs/getting-started/quick-start.adoc +185 -0
  47. data/docs/getting-started/your-first-archive.adoc +218 -0
  48. data/docs/guides/advanced-features/encryption.adoc +300 -0
  49. data/docs/guides/advanced-features/index.adoc +49 -0
  50. data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
  51. data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
  52. data/docs/guides/advanced-features/streaming.adoc +212 -0
  53. data/docs/guides/archive-formats/gzip-format.adoc +107 -0
  54. data/docs/guides/archive-formats/index.adoc +130 -0
  55. data/docs/guides/archive-formats/rar-format.adoc +104 -0
  56. data/docs/guides/archive-formats/rar5.adoc +521 -0
  57. data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
  58. data/docs/guides/archive-formats/tar-format.adoc +106 -0
  59. data/docs/guides/archive-formats/xz-format.adoc +118 -0
  60. data/docs/guides/archive-formats/zip-format.adoc +35 -0
  61. data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
  62. data/docs/guides/compression-algorithms/deflate.adoc +319 -0
  63. data/docs/guides/compression-algorithms/index.adoc +190 -0
  64. data/docs/guides/compression-algorithms/lzma.adoc +398 -0
  65. data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
  66. data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
  67. data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
  68. data/docs/guides/creating-archives.adoc +354 -0
  69. data/docs/guides/extracting-archives.adoc +53 -0
  70. data/docs/guides/format-conversion.adoc +64 -0
  71. data/docs/guides/index.adoc +49 -0
  72. data/docs/guides/migration-rubyzip.adoc +217 -0
  73. data/docs/guides/parity-archives.adoc +605 -0
  74. data/docs/guides/performance-tuning.adoc +88 -0
  75. data/docs/index.adoc +218 -0
  76. data/docs/lychee.toml +67 -0
  77. data/docs/reference/api/overview.adoc +188 -0
  78. data/docs/reference/cli/compress-command.adoc +114 -0
  79. data/docs/reference/cli/overview.adoc +140 -0
  80. data/docs/reference/index.adoc +26 -0
  81. data/docs/resources/faq.adoc +185 -0
  82. data/docs/resources/quick-reference.adoc +222 -0
  83. data/docs/troubleshooting/index.adoc +208 -0
  84. data/examples/api_comparison.rb +205 -0
  85. data/examples/deflate64_example.rb +96 -0
  86. data/examples/par2_demo.rb +121 -0
  87. data/examples/quick_start_native.rb +150 -0
  88. data/examples/quick_start_rubyzip.rb +115 -0
  89. data/examples/rubyzip_compatibility_demo.rb +194 -0
  90. data/exe/omnizip +27 -0
  91. data/lib/omnizip/algorithm.rb +130 -0
  92. data/lib/omnizip/algorithm_registry.rb +86 -0
  93. data/lib/omnizip/algorithms/.keep +0 -0
  94. data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
  95. data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
  96. data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
  97. data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
  98. data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
  99. data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
  100. data/lib/omnizip/algorithms/bzip2.rb +130 -0
  101. data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
  102. data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
  103. data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
  104. data/lib/omnizip/algorithms/deflate.rb +128 -0
  105. data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
  106. data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
  107. data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
  108. data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
  109. data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
  110. data/lib/omnizip/algorithms/deflate64.rb +109 -0
  111. data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
  112. data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
  113. data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
  114. data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
  115. data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
  116. data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
  117. data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
  118. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
  119. data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
  120. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
  121. data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
  122. data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
  123. data/lib/omnizip/algorithms/lzma/match.rb +32 -0
  124. data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
  125. data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
  126. data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
  127. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
  128. data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
  129. data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
  130. data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
  131. data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
  132. data/lib/omnizip/algorithms/lzma/state.rb +127 -0
  133. data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
  134. data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
  135. data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
  136. data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
  137. data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
  138. data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
  139. data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
  140. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
  141. data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
  142. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
  143. data/lib/omnizip/algorithms/lzma.rb +238 -0
  144. data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
  145. data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
  146. data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
  147. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
  148. data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
  149. data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
  150. data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
  151. data/lib/omnizip/algorithms/lzma2.rb +141 -0
  152. data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
  153. data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
  154. data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
  155. data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
  156. data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
  157. data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
  158. data/lib/omnizip/algorithms/ppmd7.rb +116 -0
  159. data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
  160. data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
  161. data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
  162. data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
  163. data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
  164. data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
  165. data/lib/omnizip/algorithms/ppmd8.rb +82 -0
  166. data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
  167. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
  168. data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
  169. data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
  170. data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
  171. data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
  172. data/lib/omnizip/algorithms/zstandard.rb +138 -0
  173. data/lib/omnizip/buffer/memory_archive.rb +251 -0
  174. data/lib/omnizip/buffer/memory_extractor.rb +224 -0
  175. data/lib/omnizip/buffer.rb +176 -0
  176. data/lib/omnizip/checksum_registry.rb +114 -0
  177. data/lib/omnizip/checksums/crc32.rb +100 -0
  178. data/lib/omnizip/checksums/crc64.rb +101 -0
  179. data/lib/omnizip/checksums/crc_base.rb +158 -0
  180. data/lib/omnizip/checksums/verifier.rb +131 -0
  181. data/lib/omnizip/chunked/memory_manager.rb +194 -0
  182. data/lib/omnizip/chunked/reader.rb +78 -0
  183. data/lib/omnizip/chunked/writer.rb +120 -0
  184. data/lib/omnizip/chunked.rb +129 -0
  185. data/lib/omnizip/cli/output_formatter.rb +104 -0
  186. data/lib/omnizip/cli.rb +572 -0
  187. data/lib/omnizip/commands/.keep +0 -0
  188. data/lib/omnizip/commands/archive_create_command.rb +427 -0
  189. data/lib/omnizip/commands/archive_extract_command.rb +272 -0
  190. data/lib/omnizip/commands/archive_list_command.rb +218 -0
  191. data/lib/omnizip/commands/archive_repair_command.rb +131 -0
  192. data/lib/omnizip/commands/archive_verify_command.rb +117 -0
  193. data/lib/omnizip/commands/compress_command.rb +117 -0
  194. data/lib/omnizip/commands/decompress_command.rb +120 -0
  195. data/lib/omnizip/commands/list_command.rb +53 -0
  196. data/lib/omnizip/commands/metadata_command.rb +153 -0
  197. data/lib/omnizip/commands/parity_create_command.rb +122 -0
  198. data/lib/omnizip/commands/parity_repair_command.rb +122 -0
  199. data/lib/omnizip/commands/parity_verify_command.rb +124 -0
  200. data/lib/omnizip/commands/profile_list_command.rb +56 -0
  201. data/lib/omnizip/commands/profile_show_command.rb +44 -0
  202. data/lib/omnizip/convenience.rb +359 -0
  203. data/lib/omnizip/converter/conversion_registry.rb +49 -0
  204. data/lib/omnizip/converter/conversion_strategy.rb +121 -0
  205. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
  206. data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
  207. data/lib/omnizip/converter.rb +105 -0
  208. data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
  209. data/lib/omnizip/crypto/aes256/constants.rb +28 -0
  210. data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
  211. data/lib/omnizip/crypto/aes256.rb +102 -0
  212. data/lib/omnizip/error.rb +106 -0
  213. data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
  214. data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
  215. data/lib/omnizip/eta/rate_calculator.rb +104 -0
  216. data/lib/omnizip/eta/sample_history.rb +143 -0
  217. data/lib/omnizip/eta/time_estimator.rb +106 -0
  218. data/lib/omnizip/eta.rb +63 -0
  219. data/lib/omnizip/extraction/filter_chain.rb +177 -0
  220. data/lib/omnizip/extraction/glob_pattern.rb +140 -0
  221. data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
  222. data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
  223. data/lib/omnizip/extraction/regex_pattern.rb +50 -0
  224. data/lib/omnizip/extraction/selective_extractor.rb +240 -0
  225. data/lib/omnizip/extraction.rb +111 -0
  226. data/lib/omnizip/file_type/mime_classifier.rb +144 -0
  227. data/lib/omnizip/file_type.rb +113 -0
  228. data/lib/omnizip/filter.rb +139 -0
  229. data/lib/omnizip/filter_pipeline.rb +108 -0
  230. data/lib/omnizip/filter_registry.rb +166 -0
  231. data/lib/omnizip/filters/bcj.rb +279 -0
  232. data/lib/omnizip/filters/bcj2/constants.rb +53 -0
  233. data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
  234. data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
  235. data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
  236. data/lib/omnizip/filters/bcj2.rb +99 -0
  237. data/lib/omnizip/filters/bcj_arm.rb +176 -0
  238. data/lib/omnizip/filters/bcj_arm64.rb +244 -0
  239. data/lib/omnizip/filters/bcj_ia64.rb +196 -0
  240. data/lib/omnizip/filters/bcj_ppc.rb +190 -0
  241. data/lib/omnizip/filters/bcj_sparc.rb +176 -0
  242. data/lib/omnizip/filters/bcj_x86.rb +193 -0
  243. data/lib/omnizip/filters/delta.rb +196 -0
  244. data/lib/omnizip/filters/filter_base.rb +72 -0
  245. data/lib/omnizip/filters/registry.rb +123 -0
  246. data/lib/omnizip/filters/xz_delta.rb +258 -0
  247. data/lib/omnizip/format_detector.rb +162 -0
  248. data/lib/omnizip/format_registry.rb +59 -0
  249. data/lib/omnizip/formats/.keep +0 -0
  250. data/lib/omnizip/formats/bzip2_file.rb +172 -0
  251. data/lib/omnizip/formats/cpio/constants.rb +55 -0
  252. data/lib/omnizip/formats/cpio/entry.rb +385 -0
  253. data/lib/omnizip/formats/cpio/reader.rb +196 -0
  254. data/lib/omnizip/formats/cpio/writer.rb +234 -0
  255. data/lib/omnizip/formats/cpio.rb +140 -0
  256. data/lib/omnizip/formats/format_spec_loader.rb +230 -0
  257. data/lib/omnizip/formats/gzip.rb +238 -0
  258. data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
  259. data/lib/omnizip/formats/iso/directory_record.rb +152 -0
  260. data/lib/omnizip/formats/iso/joliet.rb +204 -0
  261. data/lib/omnizip/formats/iso/path_table.rb +125 -0
  262. data/lib/omnizip/formats/iso/reader.rb +197 -0
  263. data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
  264. data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
  265. data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
  266. data/lib/omnizip/formats/iso/writer.rb +530 -0
  267. data/lib/omnizip/formats/iso.rb +140 -0
  268. data/lib/omnizip/formats/lzip.rb +175 -0
  269. data/lib/omnizip/formats/lzma_alone.rb +171 -0
  270. data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
  271. data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
  272. data/lib/omnizip/formats/rar/block_parser.rb +243 -0
  273. data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
  274. data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
  275. data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
  276. data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
  277. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
  278. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
  279. data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
  280. data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
  281. data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
  282. data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
  283. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
  284. data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
  285. data/lib/omnizip/formats/rar/constants.rb +82 -0
  286. data/lib/omnizip/formats/rar/decompressor.rb +238 -0
  287. data/lib/omnizip/formats/rar/external_writer.rb +312 -0
  288. data/lib/omnizip/formats/rar/header.rb +192 -0
  289. data/lib/omnizip/formats/rar/license_validator.rb +109 -0
  290. data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
  291. data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
  292. data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
  293. data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
  294. data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
  295. data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
  296. data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
  297. data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
  298. data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
  299. data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
  300. data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
  301. data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
  302. data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
  303. data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
  304. data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
  305. data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
  306. data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
  307. data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
  308. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
  309. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
  310. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
  311. data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
  312. data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
  313. data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
  314. data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
  315. data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
  316. data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
  317. data/lib/omnizip/formats/rar/reader.rb +366 -0
  318. data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
  319. data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
  320. data/lib/omnizip/formats/rar/writer.rb +431 -0
  321. data/lib/omnizip/formats/rar.rb +205 -0
  322. data/lib/omnizip/formats/rar3/compressor.rb +73 -0
  323. data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
  324. data/lib/omnizip/formats/rar3/reader.rb +386 -0
  325. data/lib/omnizip/formats/rar3/writer.rb +219 -0
  326. data/lib/omnizip/formats/rar5/compressor.rb +73 -0
  327. data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
  328. data/lib/omnizip/formats/rar5/reader.rb +342 -0
  329. data/lib/omnizip/formats/rar5/writer.rb +214 -0
  330. data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
  331. data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
  332. data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
  333. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
  334. data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
  335. data/lib/omnizip/formats/seven_zip/header.rb +106 -0
  336. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
  337. data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
  338. data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
  339. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
  340. data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
  341. data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
  342. data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
  343. data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
  344. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
  345. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
  346. data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
  347. data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
  348. data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
  349. data/lib/omnizip/formats/seven_zip.rb +93 -0
  350. data/lib/omnizip/formats/tar/constants.rb +73 -0
  351. data/lib/omnizip/formats/tar/entry.rb +94 -0
  352. data/lib/omnizip/formats/tar/header.rb +168 -0
  353. data/lib/omnizip/formats/tar/reader.rb +121 -0
  354. data/lib/omnizip/formats/tar/writer.rb +216 -0
  355. data/lib/omnizip/formats/tar.rb +84 -0
  356. data/lib/omnizip/formats/xz/reader.rb +116 -0
  357. data/lib/omnizip/formats/xz.rb +237 -0
  358. data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
  359. data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
  360. data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
  361. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
  362. data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
  363. data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
  364. data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
  365. data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
  366. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
  367. data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
  368. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
  369. data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
  370. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
  371. data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
  372. data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
  373. data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
  374. data/lib/omnizip/formats/zip/constants.rb +69 -0
  375. data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
  376. data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
  377. data/lib/omnizip/formats/zip/reader.rb +250 -0
  378. data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
  379. data/lib/omnizip/formats/zip/writer.rb +375 -0
  380. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
  381. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
  382. data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
  383. data/lib/omnizip/formats/zip.rb +50 -0
  384. data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
  385. data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
  386. data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
  387. data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
  388. data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
  389. data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
  390. data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
  391. data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
  392. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
  393. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
  394. data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
  395. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
  396. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
  397. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
  398. data/lib/omnizip/io/buffered_input.rb +146 -0
  399. data/lib/omnizip/io/buffered_output.rb +105 -0
  400. data/lib/omnizip/io/stream_manager.rb +115 -0
  401. data/lib/omnizip/link_handler/hard_link.rb +79 -0
  402. data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
  403. data/lib/omnizip/link_handler.rb +124 -0
  404. data/lib/omnizip/metadata/archive_metadata.rb +114 -0
  405. data/lib/omnizip/metadata/entry_metadata.rb +146 -0
  406. data/lib/omnizip/metadata/metadata_editor.rb +171 -0
  407. data/lib/omnizip/metadata/metadata_registry.rb +64 -0
  408. data/lib/omnizip/metadata/metadata_validator.rb +99 -0
  409. data/lib/omnizip/metadata.rb +57 -0
  410. data/lib/omnizip/models/.keep +0 -0
  411. data/lib/omnizip/models/algorithm_metadata.rb +73 -0
  412. data/lib/omnizip/models/compression_options.rb +71 -0
  413. data/lib/omnizip/models/conversion_options.rb +87 -0
  414. data/lib/omnizip/models/conversion_result.rb +135 -0
  415. data/lib/omnizip/models/eta_result.rb +46 -0
  416. data/lib/omnizip/models/extraction_rule.rb +115 -0
  417. data/lib/omnizip/models/filter_chain.rb +144 -0
  418. data/lib/omnizip/models/filter_config.rb +183 -0
  419. data/lib/omnizip/models/match_result.rb +124 -0
  420. data/lib/omnizip/models/optimization_suggestion.rb +91 -0
  421. data/lib/omnizip/models/parallel_options.rb +104 -0
  422. data/lib/omnizip/models/performance_result.rb +79 -0
  423. data/lib/omnizip/models/profile_report.rb +82 -0
  424. data/lib/omnizip/models/progress_options.rb +38 -0
  425. data/lib/omnizip/models/split_options.rb +116 -0
  426. data/lib/omnizip/optimization_registry.rb +81 -0
  427. data/lib/omnizip/parallel/job_queue.rb +209 -0
  428. data/lib/omnizip/parallel/job_scheduler.rb +203 -0
  429. data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
  430. data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
  431. data/lib/omnizip/parallel/worker_pool.rb +223 -0
  432. data/lib/omnizip/parallel.rb +149 -0
  433. data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
  434. data/lib/omnizip/parity/galois16.rb +145 -0
  435. data/lib/omnizip/parity/models/creator_packet.rb +73 -0
  436. data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
  437. data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
  438. data/lib/omnizip/parity/models/main_packet.rb +128 -0
  439. data/lib/omnizip/parity/models/packet.rb +156 -0
  440. data/lib/omnizip/parity/models/packet_registry.rb +109 -0
  441. data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
  442. data/lib/omnizip/parity/par2_creator.rb +531 -0
  443. data/lib/omnizip/parity/par2_repairer.rb +407 -0
  444. data/lib/omnizip/parity/par2_verifier.rb +364 -0
  445. data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
  446. data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
  447. data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
  448. data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
  449. data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
  450. data/lib/omnizip/parity.rb +186 -0
  451. data/lib/omnizip/password/encryption_registry.rb +65 -0
  452. data/lib/omnizip/password/encryption_strategy.rb +96 -0
  453. data/lib/omnizip/password/password_validator.rb +129 -0
  454. data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
  455. data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
  456. data/lib/omnizip/password.rb +87 -0
  457. data/lib/omnizip/pipe/stream_compressor.rb +124 -0
  458. data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
  459. data/lib/omnizip/pipe.rb +121 -0
  460. data/lib/omnizip/platform/ntfs_streams.rb +201 -0
  461. data/lib/omnizip/platform.rb +189 -0
  462. data/lib/omnizip/profile/archive_profile.rb +39 -0
  463. data/lib/omnizip/profile/balanced_profile.rb +33 -0
  464. data/lib/omnizip/profile/binary_profile.rb +36 -0
  465. data/lib/omnizip/profile/compression_profile.rb +158 -0
  466. data/lib/omnizip/profile/custom_profile.rb +157 -0
  467. data/lib/omnizip/profile/fast_profile.rb +33 -0
  468. data/lib/omnizip/profile/maximum_profile.rb +33 -0
  469. data/lib/omnizip/profile/profile_detector.rb +110 -0
  470. data/lib/omnizip/profile/profile_registry.rb +161 -0
  471. data/lib/omnizip/profile/text_profile.rb +36 -0
  472. data/lib/omnizip/profile.rb +190 -0
  473. data/lib/omnizip/profiler/memory_profiler.rb +66 -0
  474. data/lib/omnizip/profiler/method_profiler.rb +49 -0
  475. data/lib/omnizip/profiler/report_generator.rb +169 -0
  476. data/lib/omnizip/profiler.rb +204 -0
  477. data/lib/omnizip/progress/callback_reporter.rb +36 -0
  478. data/lib/omnizip/progress/console_reporter.rb +62 -0
  479. data/lib/omnizip/progress/log_reporter.rb +91 -0
  480. data/lib/omnizip/progress/operation_progress.rb +118 -0
  481. data/lib/omnizip/progress/progress_bar.rb +156 -0
  482. data/lib/omnizip/progress/progress_reporter.rb +40 -0
  483. data/lib/omnizip/progress/progress_tracker.rb +190 -0
  484. data/lib/omnizip/progress/silent_reporter.rb +24 -0
  485. data/lib/omnizip/progress.rb +127 -0
  486. data/lib/omnizip/rubyzip_compat.rb +63 -0
  487. data/lib/omnizip/temp/safe_extract.rb +168 -0
  488. data/lib/omnizip/temp/temp_file.rb +124 -0
  489. data/lib/omnizip/temp/temp_file_pool.rb +109 -0
  490. data/lib/omnizip/temp.rb +181 -0
  491. data/lib/omnizip/version.rb +5 -0
  492. data/lib/omnizip/zip/entry.rb +156 -0
  493. data/lib/omnizip/zip/file.rb +485 -0
  494. data/lib/omnizip/zip/input_stream.rb +273 -0
  495. data/lib/omnizip/zip/output_stream.rb +324 -0
  496. data/lib/omnizip.rb +156 -0
  497. data/readme-docs/advanced-features.adoc +515 -0
  498. data/readme-docs/api-usage.adoc +444 -0
  499. data/readme-docs/architecture.adoc +449 -0
  500. data/readme-docs/archive-formats.adoc +479 -0
  501. data/readme-docs/cli-usage.adoc +222 -0
  502. data/readme-docs/compression-algorithms.adoc +442 -0
  503. data/readme-docs/compression-profiles.adoc +247 -0
  504. data/readme-docs/encryption-checksums.adoc +328 -0
  505. data/readme-docs/format-converter.adoc +325 -0
  506. data/readme-docs/installation.adoc +228 -0
  507. data/readme-docs/par2-archives.adoc +608 -0
  508. data/readme-docs/performance-profiler.adoc +389 -0
  509. data/readme-docs/preprocessing-filters.adoc +280 -0
  510. data/xz-file-format-1.2.1.txt +1174 -0
  511. 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