pax_global_header00006660000000000000000000000064136015356610014520gustar00rootroot0000000000000052 comment=91c0b7566635ee2d1b4e58c689cad18ea057f69d ffi-libarchive-1.0.0/000077500000000000000000000000001360153566100143705ustar00rootroot00000000000000ffi-libarchive-1.0.0/.expeditor/000077500000000000000000000000001360153566100164515ustar00rootroot00000000000000ffi-libarchive-1.0.0/.expeditor/config.yml000066400000000000000000000027001360153566100204400ustar00rootroot00000000000000# Documentation available at https://expeditor.chef.io/docs/getting-started/ --- # Slack channel in Chef Software slack to send notifications about build failures, etc slack: notify_channel: chef-found-notify # This publish is triggered by the `built_in:publish_rubygems` artifact_action. rubygems: - ffi-libarchive github: # This deletes the GitHub PR branch after successfully merged into the release branch delete_branch_on_merge: true # The tag format to use (e.g. v1.0.0) version_tag_format: "v{{version}}" # allow bumping the minor release via label minor_bump_labels: - "Expeditor: Bump Version Minor" # allow bumping the major release via label major_bump_labels: - "Expeditor: Bump Version Major" changelog: rollup_header: Changes not yet released to rubygems.org # These actions are taken, in order they are specified, anytime a Pull Request is merged. merge_actions: - built_in:bump_version: ignore_labels: - "Expeditor: Skip Version Bump" - "Expeditor: Skip All" - bash:.expeditor/update_version.sh: only_if: built_in:bump_version - built_in:update_changelog: ignore_labels: - "Expeditor: Skip Changelog" - "Expeditor: Skip All" - built_in:build_gem: only_if: built_in:bump_version promote: actions: - built_in:rollover_changelog - built_in:publish_rubygems pipelines: - verify: description: Pull Request validation tests public: true ffi-libarchive-1.0.0/.expeditor/run_linux_tests.sh000077500000000000000000000023451360153566100222610ustar00rootroot00000000000000#!/bin/bash # # This script runs a passed in command, but first setups up the bundler caching on the repo set -ue export USER="root" echo "--- dependencies" export LANG=C.UTF-8 LANGUAGE=C.UTF-8 S3_URL="s3://public-cd-buildkite-cache/${BUILDKITE_PIPELINE_SLUG}/${BUILDKITE_LABEL}" pull_s3_file() { aws s3 cp "${S3_URL}/$1" "$1" || echo "Could not pull $1 from S3" } push_s3_file() { if [ -f "$1" ]; then aws s3 cp "$1" "${S3_URL}/$1" || echo "Could not push $1 to S3 for caching." fi } apt-get update -y apt-get install awscli -y echo "--- bundle install" pull_s3_file "bundle.tar.gz" pull_s3_file "bundle.sha256" if [ -f bundle.tar.gz ]; then tar -xzf bundle.tar.gz fi if [ -n "${RESET_BUNDLE_CACHE:-}" ]; then rm bundle.sha256 fi bundle config --local path vendor/bundle bundle install --jobs=7 --retry=3 echo "--- bundle cache" if test -f bundle.sha256 && shasum --check bundle.sha256 --status; then echo "Bundled gems have not changed. Skipping upload to s3" else echo "Bundled gems have changed. Uploading to s3" shasum -a 256 Gemfile.lock > bundle.sha256 tar -czf bundle.tar.gz vendor/ push_s3_file bundle.tar.gz push_s3_file bundle.sha256 fi echo "+++ bundle exec task" bundle exec $1 ffi-libarchive-1.0.0/.expeditor/update_version.sh000066400000000000000000000007111360153566100220330ustar00rootroot00000000000000#!/bin/sh # # After a PR merge, Chef Expeditor will bump the PATCH version in the VERSION file. # It then executes this file to update any other files/components with that new version. # set -evx sed -i -r "s/^(\s*)VERSION = \".+\"/\1VERSION = \"$(cat VERSION)\"/" lib/ffi-libarchive/version.rb # Once Expeditor finishes executing this script, it will commit the changes and push # the commit as a new tag corresponding to the value in the VERSION file. ffi-libarchive-1.0.0/.expeditor/verify.pipeline.yml000066400000000000000000000016131360153566100223050ustar00rootroot00000000000000--- steps: - label: run-lint-and-specs-ruby-2.4 commands: - apt-get update - apt-get install -y libarchive13 - .expeditor/run_linux_tests.sh rake expeditor: executor: docker: image: ruby:2.4-stretch - label: run-lint-and-specs-ruby-2.5 commands: - apt-get update - apt-get install -y libarchive13 - .expeditor/run_linux_tests.sh rake expeditor: executor: docker: image: ruby:2.5-stretch - label: run-lint-and-specs-ruby-2.6 commands: - apt-get update - apt-get install -y libarchive13 - .expeditor/run_linux_tests.sh rake expeditor: executor: docker: image: ruby:2.6-stretch - label: run-lint-and-specs-ruby-2.7-rc commands: - apt-get update - apt-get install -y libarchive13 - .expeditor/run_linux_tests.sh rake expeditor: executor: docker: image: ruby:2.7-rc-busterffi-libarchive-1.0.0/.github/000077500000000000000000000000001360153566100157305ustar00rootroot00000000000000ffi-libarchive-1.0.0/.github/CODEOWNERS000066400000000000000000000003741360153566100173270ustar00rootroot00000000000000# Order is important. The last matching pattern has the most precedence. * @chef/chef-foundation-owners @chef/chef-foundation-approvers @chef/chef-foundation-reviewers .expeditor/ @chef/jex-team *.md @chef/docs-team ffi-libarchive-1.0.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001360153566100201135ustar00rootroot00000000000000ffi-libarchive-1.0.0/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md000066400000000000000000000011521360153566100223440ustar00rootroot00000000000000--- name: � Bug Report about: If something isn't working as expected �. labels: "Status: Untriaged" --- # Version: [Version of the project installed] # Environment: [Details about the environment such as the Operating System, cookbook details, etc...] # Scenario: [What you are trying to achieve and you can't?] # Steps to Reproduce: [If you are filing an issue what are the things we need to do in order to repro your problem?] # Expected Result: [What are you expecting to happen as the consequence of above reproduction steps?] # Actual Result: [What actually happens after the reproduction steps?] ffi-libarchive-1.0.0/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md000066400000000000000000000023431360153566100227270ustar00rootroot00000000000000--- name: Design Proposal about: I have a significant change I would like to propose and discuss before starting labels: "Status: Untriaged" --- ### When a Change Needs a Design Proposal A design proposal should be opened any time a change meets one of the following qualifications: - Significantly changes the user experience of a project in a way that impacts users. - Significantly changes the underlying architecture of the project in a way that impacts other developers. - Changes the development or testing process of the project such as a change of CI systems or test frameworks. ### Why We Use This Process - Allows all interested parties (including any community member) to discuss large impact changes to a project. - Serves as a durable paper trail for discussions regarding project architecture. - Forces design discussions to occur before PRs are created. - Reduces PR refactoring and rejected PRs. --- ## Motivation ## Specification ## Downstream Impact ffi-libarchive-1.0.0/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md000066400000000000000000000014071360153566100246670ustar00rootroot00000000000000--- name: 🚀 Enhancement Request about: I have a suggestion (and may want to implement it 🙂)! labels: "Status: Untriaged" --- ### Describe the Enhancement: ### Describe the Need: ### Current Alternative ### Can We Help You Implement This?: ffi-libarchive-1.0.0/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md000066400000000000000000000006601360153566100231420ustar00rootroot00000000000000--- name: 🤗 Support Question about: If you have a question 💬, please check out our Slack! --- We use GitHub issues to track bugs and feature requests. If you need help please post to our Mailing List or join the Chef Community Slack. * Chef Community Slack at http://community-slack.chef.io/. * Chef Mailing List https://discourse.chef.io/ Support issues opened here will be closed and redirected to Slack or Discourse. ffi-libarchive-1.0.0/.github/lock.yml000066400000000000000000000000221360153566100173750ustar00rootroot00000000000000daysUntilLock: 60 ffi-libarchive-1.0.0/.yardopts000066400000000000000000000001511360153566100162330ustar00rootroot00000000000000--plugin classmethods --embed-mixin ClassMethods --hide-api private --markup markdown --hide-void-return ffi-libarchive-1.0.0/CHANGELOG.md000066400000000000000000000056361360153566100162130ustar00rootroot00000000000000 # Change Log ## [v1.0.0](https://github.com/chef/ffi-libarchive/tree/v1.0.0) (2019-12-28) #### Merged Pull Requests - Add Ruby 2.7rc testing and cache gem installs in Buildkite [#28](https://github.com/chef/ffi-libarchive/pull/28) ([tas50](https://github.com/tas50)) ### Changes not yet released to rubygems.org #### Merged Pull Requests - Add Ruby 2.7rc testing and cache gem installs in Buildkite [#28](https://github.com/chef/ffi-libarchive/pull/28) ([tas50](https://github.com/tas50)) - Add support for custom read functions [#27](https://github.com/chef/ffi-libarchive/pull/27) ([jatoben](https://github.com/jatoben)) - Chefstyle fixes to get the build green again [#25](https://github.com/chef/ffi-libarchive/pull/25) ([tas50](https://github.com/tas50)) ## [v0.4.10](https://github.com/chef/ffi-libarchive/tree/v0.4.10) (2019-06-24) #### Merged Pull Requests - Add a code of conduct file [#17](https://github.com/chef/ffi-libarchive/pull/17) ([tas50](https://github.com/tas50)) - Update the project to be part of Chef Foundation [#19](https://github.com/chef/ffi-libarchive/pull/19) ([tas50](https://github.com/tas50)) - Replace Travis CI with Buildkite for testing [#20](https://github.com/chef/ffi-libarchive/pull/20) ([tas50](https://github.com/tas50)) - Silence warnings with constants [#21](https://github.com/chef/ffi-libarchive/pull/21) ([tas50](https://github.com/tas50)) ## [v0.4.6](https://github.com/chef/ffi-libarchive/tree/v0.4.6) (2018-12-07) #### Merged Pull Requests - Don't ship test and development files in the gem artifact [#15](https://github.com/chef/ffi-libarchive/pull/15) ([tas50](https://github.com/tas50)) - Add gem and travis badges [#16](https://github.com/chef/ffi-libarchive/pull/16) ([tas50](https://github.com/tas50)) ## [v0.4.4](https://github.com/chef/ffi-libarchive/tree/v0.4.4) (2018-11-01) #### Merged Pull Requests - Update expeditor / travis and add github templates [#14](https://github.com/chef/ffi-libarchive/pull/14) ([tas50](https://github.com/tas50)) - Add search pattern for Windows' libarchive-13.dll [#13](https://github.com/chef/ffi-libarchive/pull/13) ([bdwyertech](https://github.com/bdwyertech)) ## [v0.4.2](https://github.com/chef/ffi-libarchive/tree/v0.4.2) (2018-07-21) #### Merged Pull Requests - Use OR instead of AND for file extraction flags [#11](https://github.com/chef/ffi-libarchive/pull/11) ([RoboticCheese](https://github.com/RoboticCheese)) ## [v0.4.1](https://github.com/chef/ffi-libarchive/tree/v0.4.1) (2018-06-13) #### Merged Pull Requests - Add some basic usage docs [#10](https://github.com/chef/ffi-libarchive/pull/10) ([thommay](https://github.com/thommay))ffi-libarchive-1.0.0/CODE_OF_CONDUCT.md000066400000000000000000000001331360153566100171640ustar00rootroot00000000000000Please refer to the Chef Community Code of Conduct at https://www.chef.io/code-of-conduct/ ffi-libarchive-1.0.0/CONTRIBUTING.md000066400000000000000000000001111360153566100166120ustar00rootroot00000000000000Please refer to https://github.com/chef/chef/blob/master/CONTRIBUTING.md ffi-libarchive-1.0.0/LICENSE000066400000000000000000000251421360153566100154010ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ffi-libarchive-1.0.0/README.md000066400000000000000000000055011360153566100156500ustar00rootroot00000000000000# ffi-libarchive [![Build status](https://badge.buildkite.com/a6b96170c6257e384000f311fa0052cb7880762e06bc66c91e.svg)](https://buildkite.com/chef-oss/chef-ffi-libarchive-master-verify) [![Gem Version](https://badge.fury.io/rb/ffi-libarchive.svg)](https://badge.fury.io/rb/ffi-libarchive) **Umbrella Project**: [Chef Foundation](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-foundation.md) **Project State**: [Active](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md#active) **Issues [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days **Pull Request [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days A Ruby FFI binding to [libarchive][0]. This library provides Ruby FFI bindings to the well-known [libarchive library](https://github.com/libarchive/libarchive). ## Installation Ensure that you have libarchive installed. On Debian/Ubuntu: ```sh apt install libarchive13 ``` On macOS with Homebrew: ```sh brew install libarchive ``` Add this line to your application's Gemfile: ```ruby gem 'ffi-libarchive' ``` And then execute: ```shell $ bundle ``` Or install it yourself as: ```shell $ gem install ffi-libarchive ``` ## Usage To extract an archive into the current directory: ```ruby flags = Archive::EXTRACT_PERM reader = Archive::Reader.open_filename('/path/to/archive.tgz') reader.each_entry do |entry| reader.extract(entry, flags.to_i) end reader.close ``` To create a gzipped tar archive: ```ruby Archive.write_open_filename('my.tgz', Archive::COMPRESSION_GZIP, Archive::FORMAT_TAR_PAX_RESTRICTED) do |tar| content = File.read 'some_path' size = content.size tar.new_entry do |e| e.pathname = 'some_path' e.size = size e.filetype = Archive::Entry::FILE tar.write_header e tar.write_data content end end ``` ## Contributing Bug reports and pull requests are welcome on GitHub at . This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Community Guidelines](https://docs.chef.io/community_guidelines.html) code of conduct. ## License - License:: Apache License, Version 2.0 ```text Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ```ffi-libarchive-1.0.0/Rakefile000077500000000000000000000011751360153566100160440ustar00rootroot00000000000000require "bundler/gem_tasks" require "rake/testtask" begin require "chefstyle" require "rubocop/rake_task" desc "Run Chefstyle tests" RuboCop::RakeTask.new(:style) do |task| task.options += ["--display-cop-names", "--no-color"] end rescue LoadError puts "chefstyle gem is not installed. bundle install first to make sure all dependencies are installed." end Rake::TestTask.new(:test) do |t| t.libs << "test" t.libs << "lib" t.test_files = FileList["test/test_ffi-libarchive.rb"] end desc "Run style & unit tests on Travis" task travis: %w{style test} # Default desc "Run style, unit" task default: %w{style test} ffi-libarchive-1.0.0/VERSION000066400000000000000000000000051360153566100154330ustar00rootroot000000000000001.0.0ffi-libarchive-1.0.0/ffi-libarchive.gemspec000066400000000000000000000014521360153566100206110ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "ffi-libarchive/version" Gem::Specification.new do |s| s.name = "ffi-libarchive" s.version = Archive::VERSION s.authors = ["John Bellone", "Jamie Winsor", "Frank Fischer"] s.email = %w{jbellone@bloomberg.net jamie@vialstudios.com frank-fischer@shadow-soft.de} s.description = "A Ruby FFI binding to libarchive." s.summary = s.description s.homepage = "https://github.com/chef/ffi-libarchive" s.license = "Apache-2.0" s.files = %w{ LICENSE } + Dir.glob("lib/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) } s.require_paths = %w{lib} s.required_ruby_version = ">= 2.4.0" s.add_dependency "ffi", "~> 1.0" s.add_development_dependency "bundler" end ffi-libarchive-1.0.0/lib/000077500000000000000000000000001360153566100151365ustar00rootroot00000000000000ffi-libarchive-1.0.0/lib/ffi-libarchive.rb000066400000000000000000000034251360153566100203410ustar00rootroot00000000000000module Archive # :stopdoc: LIBPATH ||= ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR PATH ||= ::File.dirname(LIBPATH) + ::File::SEPARATOR # :startdoc: # Returns the library path for the module. If any arguments are given, # they will be joined to the end of the libray path using # File.join. # def self.libpath(*args) rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten) if block_given? begin $LOAD_PATH.unshift LIBPATH rv = yield ensure $LOAD_PATH.shift end end rv end # Returns the lpath for the module. If any arguments are given, # they will be joined to the end of the path using # File.join. # def self.path(*args) rv = args.empty? ? PATH : ::File.join(PATH, args.flatten) if block_given? begin $LOAD_PATH.unshift PATH rv = yield ensure $LOAD_PATH.shift end end rv end # Utility method used to require all files ending in .rb that lie in the # directory below this file that has the same name as the filename passed # in. Optionally, a specific _directory_ name can be passed in such that # the _filename_ does not have to be equivalent to the directory. # def self.require_all_libs_relative_to(fname, dir = nil) dir ||= ::File.basename(fname, ".*") search_me = ::File.expand_path( ::File.join(::File.dirname(fname), dir, "**", "*.rb") ) Dir.glob(search_me).sort.each { |rb| require rb } end end # module Archive require File.join(Archive::LIBPATH, "ffi-libarchive", "archive") require File.join(Archive::LIBPATH, "ffi-libarchive", "reader") require File.join(Archive::LIBPATH, "ffi-libarchive", "writer") require File.join(Archive::LIBPATH, "ffi-libarchive", "entry") ffi-libarchive-1.0.0/lib/ffi-libarchive/000077500000000000000000000000001360153566100200105ustar00rootroot00000000000000ffi-libarchive-1.0.0/lib/ffi-libarchive/archive.rb000066400000000000000000000421041360153566100217570ustar00rootroot00000000000000require "ffi" module Archive module C def self.attach_function_maybe(*args) attach_function(*args) rescue FFI::NotFoundError # rubocop:disable Lint/HandleExceptions end extend FFI::Library ffi_lib %w{libarchive.so.13 libarchive.13 libarchive-13 libarchive.so libarchive archive} attach_function :archive_version_number, [], :int attach_function :archive_version_string, [], :string attach_function :archive_error_string, [:pointer], :string attach_function :archive_errno, [:pointer], :int attach_function :archive_read_new, [], :pointer attach_function :archive_read_open_filename, %i{pointer string size_t}, :int attach_function :archive_read_open_memory, %i{pointer pointer size_t}, :int attach_function :archive_read_open1, [:pointer], :int attach_function :archive_read_support_compression_program, %i{pointer string}, :int attach_function :archive_read_support_compression_all, [:pointer], :int callback :archive_read_callback, %i{pointer pointer pointer}, :int callback :archive_skip_callback, %i{pointer pointer int64}, :int callback :archive_seek_callback, %i{pointer pointer int64 int}, :int attach_function :archive_read_set_read_callback, %i{pointer archive_read_callback}, :int attach_function :archive_read_set_callback_data, %i{pointer pointer}, :int attach_function :archive_read_set_skip_callback, %i{pointer archive_skip_callback}, :int attach_function :archive_read_set_seek_callback, %i{pointer archive_seek_callback}, :int attach_function_maybe :archive_read_set_format, %i{pointer int}, :int attach_function_maybe :archive_read_append_filter, %i{pointer int}, :int attach_function_maybe :archive_read_append_filter_program, %i{pointer pointer}, :int attach_function_maybe :archive_read_append_filter_program_signature, %i{pointer string pointer size_t}, :int attach_function_maybe :archive_read_support_filter_all, [:pointer], :int attach_function_maybe :archive_read_support_filter_bzip2, [:pointer], :int attach_function_maybe :archive_read_support_filter_compress, [:pointer], :int attach_function_maybe :archive_read_support_filter_gzip, [:pointer], :int attach_function_maybe :archive_read_support_filter_grzip, [:pointer], :int attach_function_maybe :archive_read_support_filter_lrzip, [:pointer], :int attach_function_maybe :archive_read_support_filter_lz4, [:pointer], :int attach_function_maybe :archive_read_support_filter_lzip, [:pointer], :int attach_function_maybe :archive_read_support_filter_lzma, [:pointer], :int attach_function_maybe :archive_read_support_filter_lzop, [:pointer], :int attach_function_maybe :archive_read_support_filter_none, [:pointer], :int attach_function_maybe :archive_read_support_filter_program, [:pointer], :int attach_function_maybe :archive_read_support_filter_program_signature, [:pointer], :int attach_function_maybe :archive_read_support_filter_rpm, [:pointer], :int attach_function_maybe :archive_read_support_filter_uu, [:pointer], :int attach_function_maybe :archive_read_support_filter_xz, [:pointer], :int attach_function_maybe :archive_read_support_format_all, [:pointer], :int attach_function_maybe :archive_read_support_format_7zip, [:pointer], :int attach_function_maybe :archive_read_support_format_ar, [:pointer], :int attach_function_maybe :archive_read_support_format_by_code, [:pointer], :int attach_function_maybe :archive_read_support_format_cab, [:pointer], :int attach_function_maybe :archive_read_support_format_cpio, [:pointer], :int attach_function_maybe :archive_read_support_format_empty, [:pointer], :int attach_function_maybe :archive_read_support_format_gnutar, [:pointer], :int attach_function_maybe :archive_read_support_format_iso9660, [:pointer], :int attach_function_maybe :archive_read_support_format_lha, [:pointer], :int attach_function_maybe :archive_read_support_format_mtree, [:pointer], :int attach_function_maybe :archive_read_support_format_rar, [:pointer], :int attach_function_maybe :archive_read_support_format_raw, [:pointer], :int attach_function_maybe :archive_read_support_format_tar, [:pointer], :int attach_function_maybe :archive_read_support_format_warc, [:pointer], :int attach_function_maybe :archive_read_support_format_xar, [:pointer], :int attach_function_maybe :archive_read_support_format_zip, [:pointer], :int attach_function_maybe :archive_read_support_format_zip_streamable, [:pointer], :int attach_function_maybe :archive_read_support_format_zip_seekable, [:pointer], :int attach_function :archive_read_finish, [:pointer], :int attach_function :archive_read_extract, %i{pointer pointer int}, :int attach_function :archive_read_header_position, [:pointer], :int attach_function :archive_read_next_header, %i{pointer pointer}, :int attach_function :archive_read_data, %i{pointer pointer size_t}, :size_t attach_function :archive_read_data_into_fd, %i{pointer int}, :int attach_function :archive_write_new, [], :pointer attach_function :archive_write_open_filename, %i{pointer string}, :int callback :archive_open_callback, %i{pointer pointer}, :int callback :archive_write_callback, %i{pointer pointer pointer size_t}, :int callback :archive_close_callback, %i{pointer pointer}, :int attach_function :archive_write_open, %i{pointer pointer pointer archive_write_callback pointer}, :int attach_function :archive_write_set_compression_none, [:pointer], :int attach_function_maybe :archive_write_set_compression_gzip, [:pointer], :int attach_function_maybe :archive_write_set_compression_bzip2, [:pointer], :int attach_function_maybe :archive_write_set_compression_deflate, [:pointer], :int attach_function_maybe :archive_write_set_compression_compress, [:pointer], :int attach_function_maybe :archive_write_set_compression_lzma, [:pointer], :int attach_function_maybe :archive_write_set_compression_xz, [:pointer], :int attach_function :archive_write_set_compression_program, %i{pointer string}, :int def self.archive_write_set_compression(archive, compression) case compression when String archive_write_set_compression_program archive, compression when COMPRESSION_BZIP2 archive_write_set_compression_bzip2 archive when COMPRESSION_GZIP archive_write_set_compression_gzip archive when COMPRESSION_LZMA archive_write_set_compression_lzma archive when COMPRESSION_XZ archive_write_set_compression_xz archive when COMPRESSION_COMPRESS archive_write_set_compression_compress archive when COMPRESSION_NONE archive_write_set_compression_none archive else raise "Unknown compression type: #{compression}" end end attach_function :archive_write_set_format, %i{pointer int}, :int attach_function :archive_write_data, %i{pointer pointer size_t}, :ssize_t attach_function :archive_write_header, %i{pointer pointer}, :int attach_function :archive_write_finish, [:pointer], :void attach_function :archive_write_get_bytes_in_last_block, [:pointer], :int attach_function :archive_write_set_bytes_in_last_block, %i{pointer int}, :int attach_function :archive_entry_new, [], :pointer attach_function :archive_entry_free, [:pointer], :void attach_function :archive_entry_atime, [:pointer], :time_t attach_function :archive_entry_atime_nsec, %i{pointer time_t long}, :void attach_function_maybe :archive_entry_atime_is_set, [:pointer], :int attach_function :archive_entry_set_atime, %i{pointer time_t long}, :int attach_function_maybe :archive_entry_unset_atime, [:pointer], :int attach_function_maybe :archive_entry_birthtime, [:pointer], :time_t attach_function_maybe :archive_entry_birthtime_nsec, %i{pointer time_t long}, :void attach_function_maybe :archive_entry_birthtime_is_set, [:pointer], :int attach_function_maybe :archive_entry_set_birthtime, %i{pointer time_t long}, :int attach_function_maybe :archive_entry_unset_birthtime, [:pointer], :int attach_function :archive_entry_ctime, [:pointer], :time_t attach_function :archive_entry_ctime_nsec, %i{pointer time_t long}, :void attach_function_maybe :archive_entry_ctime_is_set, [:pointer], :int attach_function :archive_entry_set_ctime, %i{pointer time_t long}, :int attach_function_maybe :archive_entry_unset_ctime, [:pointer], :int attach_function :archive_entry_mtime, [:pointer], :time_t attach_function :archive_entry_mtime_nsec, %i{pointer time_t long}, :void attach_function_maybe :archive_entry_mtime_is_set, [:pointer], :int attach_function :archive_entry_set_mtime, %i{pointer time_t long}, :int attach_function_maybe :archive_entry_unset_mtime, [:pointer], :int attach_function :archive_entry_dev, [:pointer], :dev_t attach_function :archive_entry_set_dev, %i{pointer dev_t}, :void attach_function :archive_entry_devmajor, [:pointer], :dev_t attach_function :archive_entry_set_devmajor, %i{pointer dev_t}, :void attach_function :archive_entry_devminor, [:pointer], :dev_t attach_function :archive_entry_set_devminor, %i{pointer dev_t}, :void attach_function :archive_entry_filetype, [:pointer], :mode_t attach_function :archive_entry_set_filetype, %i{pointer mode_t}, :void attach_function :archive_entry_fflags, %i{pointer pointer pointer}, :void attach_function :archive_entry_set_fflags, %i{pointer ulong ulong}, :void attach_function :archive_entry_fflags_text, [:pointer], :string attach_function :archive_entry_gid, [:pointer], :gid_t attach_function :archive_entry_set_gid, %i{pointer gid_t}, :void attach_function :archive_entry_gname, [:pointer], :string attach_function :archive_entry_set_gname, %i{pointer string}, :void attach_function :archive_entry_hardlink, [:pointer], :string attach_function :archive_entry_set_hardlink, %i{pointer string}, :void attach_function :archive_entry_set_link, %i{pointer string}, :void attach_function :archive_entry_ino, [:pointer], :ino_t attach_function :archive_entry_set_ino, %i{pointer ino_t}, :void attach_function :archive_entry_mode, [:pointer], :mode_t attach_function :archive_entry_set_mode, %i{pointer mode_t}, :void attach_function :archive_entry_set_perm, %i{pointer mode_t}, :void attach_function :archive_entry_nlink, [:pointer], :uint attach_function :archive_entry_set_nlink, %i{pointer uint}, :void attach_function :archive_entry_pathname, [:pointer], :string attach_function :archive_entry_set_pathname, %i{pointer string}, :void attach_function :archive_entry_rdev, [:pointer], :dev_t attach_function :archive_entry_set_rdev, %i{pointer dev_t}, :void attach_function :archive_entry_rdevmajor, [:pointer], :dev_t attach_function :archive_entry_set_rdevmajor, %i{pointer dev_t}, :void attach_function :archive_entry_rdevminor, [:pointer], :dev_t attach_function :archive_entry_set_rdevminor, %i{pointer dev_t}, :void attach_function :archive_entry_size, [:pointer], :int64_t attach_function :archive_entry_set_size, %i{pointer int64_t}, :void attach_function_maybe :archive_entry_unset_size, [:pointer], :void attach_function_maybe :archive_entry_size_is_set, [:pointer], :int attach_function :archive_entry_sourcepath, [:pointer], :string attach_function :archive_entry_strmode, [:pointer], :string attach_function :archive_entry_symlink, [:pointer], :string attach_function :archive_entry_set_symlink, %i{pointer string}, :void attach_function :archive_entry_uid, [:pointer], :uid_t attach_function :archive_entry_set_uid, %i{pointer uid_t}, :void attach_function :archive_entry_uname, [:pointer], :string attach_function :archive_entry_set_uname, %i{pointer string}, :void attach_function :archive_entry_copy_stat, %i{pointer pointer}, :void attach_function :archive_entry_copy_fflags_text, %i{pointer string}, :string attach_function :archive_entry_copy_gname, %i{pointer string}, :string attach_function :archive_entry_copy_uname, %i{pointer string}, :string attach_function :archive_entry_copy_hardlink, %i{pointer string}, :string attach_function :archive_entry_copy_link, %i{pointer string}, :string attach_function :archive_entry_copy_symlink, %i{pointer string}, :string attach_function :archive_entry_copy_sourcepath, %i{pointer string}, :string attach_function :archive_entry_copy_pathname, %i{pointer string}, :string attach_function :archive_entry_xattr_clear, [:pointer], :void attach_function :archive_entry_xattr_add_entry, %i{pointer string pointer size_t}, :void attach_function :archive_entry_xattr_count, [:pointer], :int attach_function :archive_entry_xattr_reset, [:pointer], :int attach_function :archive_entry_xattr_next, %i{pointer pointer pointer pointer}, :int EOF = 1 OK = 0 RETRY = -10 WARN = -20 FAILED = -25 FATAL = -30 DATA_BUFFER_SIZE = 2**16 end COMPRESSION_NONE = 0 COMPRESSION_GZIP = 1 COMPRESSION_BZIP2 = 2 COMPRESSION_COMPRESS = 3 COMPRESSION_PROGRAM = 4 COMPRESSION_LZMA = 5 COMPRESSION_XZ = 6 COMPRESSION_UU = 7 COMPRESSION_RPM = 8 COMPRESSION_LZIP = 9 COMPRESSION_LRZIP = 10 COMPRESSION_LZOP = 11 COMPRESSION_GRZIP = 12 COMPRESSION_LZ4 = 13 FORMAT_BASE_MASK = 0xff0000 FORMAT_CPIO = 0x10000 FORMAT_CPIO_POSIX = (FORMAT_CPIO | 1) FORMAT_CPIO_BIN_LE = (FORMAT_CPIO | 2) FORMAT_CPIO_BIN_BE = (FORMAT_CPIO | 3) FORMAT_CPIO_SVR4_NOCRC = (FORMAT_CPIO | 4) FORMAT_CPIO_SVR4_CRC = (FORMAT_CPIO | 5) FORMAT_SHAR = 0x20000 FORMAT_SHAR_BASE = (FORMAT_SHAR | 1) FORMAT_SHAR_DUMP = (FORMAT_SHAR | 2) FORMAT_TAR = 0x30000 FORMAT_TAR_USTAR = (FORMAT_TAR | 1) FORMAT_TAR_PAX_INTERCHANGE = (FORMAT_TAR | 2) FORMAT_TAR_PAX_RESTRICTED = (FORMAT_TAR | 3) FORMAT_TAR_GNUTAR = (FORMAT_TAR | 4) FORMAT_ISO9660 = 0x40000 FORMAT_ISO9660_ROCKRIDGE = (FORMAT_ISO9660 | 1) FORMAT_ZIP = 0x50000 FORMAT_EMPTY = 0x60000 FORMAT_AR = 0x70000 FORMAT_AR_GNU = (FORMAT_AR | 1) FORMAT_AR_BSD = (FORMAT_AR | 2) FORMAT_MTREE = 0x80000 FORMAT_RAW = 0x90000 FORMAT_XAR = 0xA0000 FORMAT_LHA = 0xB0000 FORMAT_CAB = 0xC0000 FORMAT_RAR = 0xD0000 FORMAT_7ZIP = 0xE0000 FORMAT_WARC = 0xF0000 EXTRACT_OWNER = 0x0001 EXTRACT_PERM = 0x0002 EXTRACT_TIME = 0x0004 EXTRACT_NO_OVERWRITE = 0x0008 EXTRACT_UNLINK = 0x0010 EXTRACT_ACL = 0x0020 EXTRACT_FFLAGS = 0x0040 EXTRACT_XATTR = 0x0080 EXTRACT_SECURE_SYMLINKS = 0x0100 EXTRACT_SECURE_NODOTDOT = 0x0200 EXTRACT_NO_AUTODIR = 0x0400 EXTRACT_NO_OVERWRITE_NEWER = 0x0800 EXTRACT_SPARSE = 0x1000 EXTRACT_MAC_METADATA = 0x2000 EXTRACT_NO_HFS_COMPRESSION = 0x4000 EXTRACT_HFS_COMPRESSION_FORCED = 0x8000 EXTRACT_SECURE_NOABSOLUTEPATHS = 0x10000 EXTRACT_CLEAR_NOCHANGE_FFLAGS = 0x20000 def self.read_open_filename(file_name, command = nil, &block) Reader.open_filename file_name, command, &block end def self.read_open_memory(string, command = nil, &block) Reader.open_memory string, command, &block end def self.read_open_stream(reader, &block) Reader.open_stream reader, &block end def self.write_open_filename(file_name, compression, format, &block) Writer.open_filename file_name, compression, format, &block end def self.write_open_memory(string, compression, format, &block) Writer.open_memory string, compression, format, &block end def self.version_number C.archive_version_number end def self.version_string C.archive_version_string end class Error < StandardError def initialize(archive) if archive.is_a? String super archive else super C.archive_error_string(archive).to_s end end end class BaseArchive def initialize(alloc, free) @archive = nil @archive_free = nil @archive = alloc.call @archive_free = [nil] raise Error, @archive unless @archive @archive_free[0] = free ObjectSpace.define_finalizer(self, BaseArchive.finalizer(@archive, @archive_free)) end def self.finalizer(archive, archive_free) proc do |*_args| archive_free[0].call(archive) if archive_free[0] end end def close # TODO: do we need synchronization here? if @archive # TODO: Error check? @archive_free[0].call(@archive) end ensure @archive = nil @archive_free[0] = nil @data = nil end def archive raise Error, "No archive open" unless @archive @archive end protected :archive def error_string C.archive_error_string(@archive) end def errno C.archive_errno(@archive) end end end ffi-libarchive-1.0.0/lib/ffi-libarchive/entry.rb000066400000000000000000000233011360153566100214750ustar00rootroot00000000000000module Archive class Entry S_IFMT = 0170000 S_IFSOCK = 0140000 # socket S_IFLNK = 0120000 # symbolic link S_IFREG = 0100000 # regular file S_IFBLK = 0060000 # block device S_IFDIR = 0040000 # directory S_IFCHR = 0020000 # character device S_IFIFO = 0010000 # FIFO SOCKET = 0140000 # socket SYMBOLIC_LINK = 0120000 # symbolic link FILE = 0100000 # regular file BLOCK_SPECIAL = 0060000 # block device DIRECTORY = 0040000 # directory CHARACTER_SPECIAL = 0020000 # character device FIFO = 0010000 # FIFO def self.from_pointer(entry) new entry end def initialize(entry = nil) @entry_free = [true] if entry @entry = entry yield self if block_given? else @entry = C.archive_entry_new raise Error, @entry unless @entry if block_given? result = yield self C.archive_entry_free(@entry) @entry = nil return result else @entry_free[0] = false ObjectSpace.define_finalizer(self, Entry.finalizer(@entry, @entry_free)) end end end def self.finalizer(entry, entry_free) proc do |*_args| C.archive_entry_free(entry) unless entry_free[0] end end def close # TODO: do we need synchronization here? if @entry && !@entry_free[0] @entry_free[0] = true C.archive_entry_free(@entry) end ensure @entry = nil end def entry raise "No entry object" unless @entry @entry end def atime Time.at C.archive_entry_atime(entry) end def atime=(time) set_atime time, 0 end def set_atime(time, nsec) C.archive_entry_set_atime(entry, time.to_i, nsec) end def atime_is_set? C.archive_entry_atime_is_set(entry) != 0 end def atime_nsec C.archive_entry_atime_nsec(entry) end def birthtime Time.at C.archive_entry_birthtime(entry) end def birthtime=(time) set_birthtime time, 0 end def set_birthtime(time, nsec) C.archive_entry_set_birthtime(entry, time.to_i, nsec) end def birthtime_is_set? C.archive_entry_birthtime_is_set(entry) != 0 end def birthtime_nsec C.archive_entry_birthtime_nsec(entry) end def ctime Time.at C.archive_entry_ctime(entry) end def ctime=(time) set_ctime time, 0 end def set_ctime(time, nsec) C.archive_entry_set_ctime(entry, time.to_i, nsec) end def ctime_is_set? C.archive_entry_ctime_is_set(entry) != 0 end def ctime_nsec C.archive_entry_ctime_nsec(entry) end def block_special? C.archive_entry_filetype(entry) & S_IFMT == S_IFBLK end def character_special? C.archive_entry_filetype(entry) & S_IFMT == S_IFCHR end def directory? C.archive_entry_filetype(entry) & S_IFMT == S_IFDIR end def fifo? C.archive_entry_filetype(entry) & S_IFMT == S_IFIFO end def regular? C.archive_entry_filetype(entry) & S_IFMT == S_IFREG end alias file? regular? def socket? C.archive_entry_filetype(entry) & S_IFMT == S_IFSOCK end def symbolic_link? C.archive_entry_filetype(entry) & S_IFMT == S_IFLNK end def copy_fflags_text(fflags_text) C.archive_entry_copy_fflags_text(entry, fflags_text) nil end def copy_gname(gname) C.archive_entry_copy_gname(entry, gname) nil end def copy_hardlink(lnk) C.archive_entry_copy_hardlink(entry, lnk) nil end def copy_link(lnk) C.archive_entry_copy_link(entry, lnk) nil end def copy_lstat(filename) # TODO: get this work without ffi-inliner begin require File.join(Archive::LIBPATH, "ffi-libarchive", "stat") rescue => e raise "ffi-inliner build for copy_stat failed:\n#{e}" end stat = Archive::Stat.ffi_libarchive_create_lstat(filename) raise Error, "Copy stat failed: #{Archive::Stat.ffi_error}" if stat.null? C.archive_entry_copy_stat(entry, stat) ensure Archive::Stat.ffi_libarchive_free_stat(stat) end def copy_pathname(file_name) C.archive_entry_copy_pathname(entry, file_name) nil end def copy_sourcepath(path) C.archive_copy_sourcepath(entry, path) nil end def copy_stat(filename) # TODO: get this work without ffi-inliner begin require File.join(Archive::LIBPATH, "ffi-libarchive", "stat") rescue => e raise "ffi-inliner build for copy_stat failed:\n#{e}" end stat = Archive::Stat.ffi_libarchive_create_stat(filename) raise Error, "Copy stat failed: #{Archive::Stat.ffi_error}" if stat.null? C.archive_entry_copy_stat(entry, stat) ensure Archive::Stat.ffi_libarchive_free_stat(stat) end def copy_symlink(slnk) C.archive_copy_symlink(entry, slnk) nil end def copy_uname(uname) C.archive_copy_uname(entry, uname) nil end def dev C.archive_entry_dev(entry) end def dev=(dev) C.archive_entry_set_dev(entry, dev) end def devmajor C.archive_entry_devmajor(entry) end def devmajor=(dev) C.archive_entry_set_devmajor(entry, dev) end def devminor C.archive_entry_devminor(entry) end def devminor=(dev) C.archive_entry_set_devminor(entry, dev) end def fflags set = FFI::MemoryPointer.new :long clear = FFI::MemoryPointer.new :long C.archive_entry_fflags(entry, set, clear) [set.get_long(0), clear.get_long(0)] end def fflags_text C.archive_entry_fflags_text(entry) end def filetype C.archive_entry_filetype(entry) end def filetype=(type) type = Entry.const_get type.to_s.upcase.to_sym if type.is_a? Symbol C.archive_entry_set_filetype(entry, type) end def gid C.archive_entry_gid(entry) end def gid=(gid) C.archive_entry_set_gid(entry, gid) end def gname C.archive_entry_gname(entry) end def gname=(gname) C.archive_entry_set_gname(entry, gname) end def hardlink C.archive_entry_hardlink(entry) end def hardlink=(lnk) C.archive_entry_set_hardlink(entry, lnk) end def ino C.archive_entry_ino(entry) end def ino=(ino) C.archive_entry_set_ino(entry, ino) end def link=(lnk) C.archive_entry_set_link(entry, lnk) end def mode C.archive_entry_mode(entry) end def mode=(mode) C.archive_entry_set_mode(entry, mode) end def mtime Time.at C.archive_entry_mtime(entry) end def mtime=(time) set_mtime time, 0 end def set_mtime(time, nsec) C.archive_entry_set_mtime(entry, time.to_i, nsec) end def mtime_is_set? C.archive_entry_mtime_is_set(entry) != 0 end def mtime_nsec C.archive_entry_mtime_nsec(entry) end def nlink C.archive_entry_nlink(entry) end def nlink=(nlink) C.archive_entry_set_nlink(entry, nlink) end def pathname C.archive_entry_pathname(entry) end def pathname=(path) C.archive_entry_set_pathname(entry, path) end def perm=(perm) C.archive_entry_set_perm(entry, perm) end def rdev C.archive_entry_rdev(entry) end def rdev=(dev) C.archive_entry_set_rdev(entry, dev) end def rdevmajor C.archive_entry_rdevmajor(entry) end def rdevmajor=(dev) C.archive_entry_set_rdevmajor(entry, dev) end def rdevminor C.archive_entry_rdevminor(entry) end def rdevminor=(dev) C.archive_entry_set_rdevminor(entry, dev) end def set_fflags(set, clear) C.archive_entry_set_fflags(entry, set, clear) end def size C.archive_entry_size(entry) end def size=(size) C.archive_entry_set_size(entry, size) end def size_is_set? C.archive_entry_size_is_set(entry) != 0 end def sourcepath C.archive_entry_sourcepath(entry) end def strmode C.archive_entry_strmode(entry) end def symlink C.archive_entry_symlink(entry) end def symlink=(slnk) C.archive_entry_set_symlink(entry, slnk) end def uid C.archive_entry_uid(entry) end def uid=(uid) C.archive_entry_set_uid(entry, uid) end def uname C.archive_entry_uname(entry) end def uname=(uname) C.archive_entry_set_uname(entry, uname) end def unset_atime C.archive_entry_unset_atime(entry) end def unset_birthtime C.archive_entry_unset_birthtime(entry) end def unset_ctime C.archive_entry_unset_ctime(entry) end def unset_mtime C.archive_entry_unset_mtime(entry) end def unset_size C.archive_entry_unset_size(entry) end def xattr_add_entry(name, value) C.archive_entry_xattr_add_entry(entry, name, value, value.size) end def xattr_clear C.archive_entry_xattr_clear(entry) end def xattr_count C.archive_entry_xattr_count(entry) end def xattr_next name = FFI::MemoryPointer.new :pointer value = FFI::MemoryPointer.new :pointer size = FFI::MemoryPointer.new :size_t if C.archive_entry_xattr_next(entry, name, value, size) != C::OK return nil else # TODO: someday size.read_size_t could work return [name.null? ? nil : name.read_string, value.null? ? nil : value.get_string(0, size.read_ulong)] end end def xattr_reset C.archive_entry_xattr_reset(entry) end end end ffi-libarchive-1.0.0/lib/ffi-libarchive/reader.rb000066400000000000000000000114071360153566100216020ustar00rootroot00000000000000module Archive class Reader < BaseArchive private_class_method :new def self.open_filename(file_name, command = nil) if block_given? reader = open_filename file_name, command begin yield reader ensure reader.close end else new file_name: file_name, command: command end end def self.open_memory(string, command = nil) if block_given? reader = open_memory string, command begin yield reader ensure reader.close end else new memory: string, command: command end end def self.open_stream(reader) if block_given? reader = new reader: reader begin yield reader ensure reader.close end else new reader: reader end end def initialize(params = {}) super C.method(:archive_read_new), C.method(:archive_read_finish) if params[:command] cmd = params[:command] raise Error, @archive if C.archive_read_support_compression_program(archive, cmd) != C::OK else raise Error, @archive if C.archive_read_support_compression_all(archive) != C::OK end raise Error, @archive if C.archive_read_support_format_all(archive) != C::OK case when params[:file_name] raise Error, @archive if C.archive_read_open_filename(archive, params[:file_name], 1024) != C::OK when params[:memory] str = params[:memory] @data = FFI::MemoryPointer.new(str.bytesize + 1) @data.write_string str, str.bytesize raise Error, @archive if C.archive_read_open_memory(archive, @data, str.bytesize) != C::OK when params[:reader] @reader = params[:reader] @buffer = nil @read_callback = FFI::Function.new(:int, %i{pointer pointer pointer}) do |_, _, archive_data| data = @reader.call || "" @buffer = FFI::MemoryPointer.new(:char, data.size) if @buffer.nil? || @buffer.size < data.size @buffer.write_bytes(data) archive_data.write_pointer(@buffer) data.size end C.archive_read_set_read_callback(archive, @read_callback) if @reader.respond_to?(:skip) @skip_callback = FFI::Function.new(:int, %i{pointer pointer int64}) do |_, _, offset| @reader.skip(offset) end C.archive_read_set_skip_callback(archive, @skip_callback) end if @reader.respond_to?(:seek) @seek_callback = FFI::Function.new(:int, %i{pointer pointer int64 int}) do |_, _, offset, whence| @reader.seek(offset, whence) end C.archive_read_set_seek_callback(archive, @seek_callback) end # Required or open1 will segfault, even though the callback data is not used. C.archive_read_set_callback_data(archive, nil) raise Error, @archive if C.archive_read_open1(archive) != C::OK end rescue close raise end def extract(entry, flags = 0) raise ArgumentError, "Expected Archive::Entry as first argument" unless entry.is_a? Entry raise ArgumentError, "Expected Integer as second argument" unless flags.is_a? Integer flags |= EXTRACT_FFLAGS raise Error, @archive if C.archive_read_extract(archive, entry.entry, flags) != C::OK end def header_position raise Error, @archive if C.archive_read_header_position archive end def next_header entry_ptr = FFI::MemoryPointer.new(:pointer) case C.archive_read_next_header(archive, entry_ptr) when C::OK Entry.from_pointer entry_ptr.read_pointer when C::EOF @eof = true nil else raise Error, @archive end end def each_entry while (entry = next_header) yield entry end end def each_entry_with_data(_size = C::DATA_BUFFER_SIZE) while (entry = next_header) yield entry, read_data end end def read_data(size = C::DATA_BUFFER_SIZE) raise ArgumentError, "Buffer size must be > 0 (was: #{size})" unless size.is_a?(Integer) && size > 0 data = nil buffer = FFI::MemoryPointer.new(size) len = 0 while (n = C.archive_read_data(archive, buffer, size)) > 0 case n when C::FATAL, C::WARN, C::RETRY raise Error, @archive else if block_given? yield buffer.get_bytes(0, n) else data ||= "" data.concat(buffer.get_bytes(0, n)) end end len += n end data || len end def save_data(file_name) IO.sysopen(file_name, "wb") do |fd| raise Error, @archive if C.archive_read_data_into_fd(archive, fd) != C::OK end end end end ffi-libarchive-1.0.0/lib/ffi-libarchive/stat.rb000066400000000000000000000021141360153566100213060ustar00rootroot00000000000000require "ffi-inliner" module Archive module Stat extend Inliner inline do |builder| builder.include "stdlib.h" builder.include "sys/types.h" builder.include "sys/stat.h" builder.include "string.h" builder.include "errno.h" builder.c ' void* ffi_libarchive_create_stat(const char* filename) { struct stat* s = malloc(sizeof(struct stat)); if (stat(filename, s) != 0) return NULL; return s; } ' builder.c ' void* ffi_libarchive_create_lstat(const char* filename) { struct stat* s = malloc(sizeof(struct stat)); lstat(filename, s); return s; } ' builder.c ' void ffi_libarchive_free_stat(void* s) { free((struct stat*)s); } ' builder.c ' const char* ffi_error() { return strerror(errno); } ' end end end ffi-libarchive-1.0.0/lib/ffi-libarchive/version.rb000066400000000000000000000000561360153566100220230ustar00rootroot00000000000000module Archive VERSION = "1.0.0".freeze end ffi-libarchive-1.0.0/lib/ffi-libarchive/writer.rb000066400000000000000000000064501360153566100216560ustar00rootroot00000000000000module Archive class Writer < BaseArchive private_class_method :new def self.open_filename(file_name, compression, format) if block_given? writer = open_filename file_name, compression, format begin yield writer ensure writer.close end else new file_name: file_name, compression: compression, format: format end end def self.open_memory(string, compression, format) if block_given? writer = open_memory string, compression, format begin yield writer ensure writer.close end else if compression.is_a? String compression = -1 end new memory: string, compression: compression, format: format end end def initialize(params = {}) super C.method(:archive_write_new), C.method(:archive_write_finish) compression = params[:compression] case compression when Symbol compression = Archive.const_get("COMPRESSION_#{compression.to_s.upcase}".to_sym) end format = params[:format] case format when Symbol format = Archive.const_get("FORMAT_#{format.to_s.upcase}".to_sym) end raise Error, @archive if C.archive_write_set_compression(archive, compression) != C::OK raise Error, @archive if C.archive_write_set_format(archive, format) != C::OK if params[:file_name] raise Error, @archive if C.archive_write_open_filename(archive, params[:file_name]) != C::OK elsif params[:memory] if C.archive_write_get_bytes_in_last_block(@archive) == -1 C.archive_write_set_bytes_in_last_block(archive, 1) end @data = write_callback params[:memory] raise Error, @archive if C.archive_write_open(archive, nil, nil, @data, nil) != C::OK end rescue close raise end def write_callback(data) proc do |_ar, _client, buffer, length| data.concat buffer.get_bytes(0, length) length end end private :write_callback def new_entry entry = Entry.new if block_given? begin result = yield entry ensure entry.close end result else entry end end def add_entry raise ArgumentError, "No block given" unless block_given? entry = Entry.new data = yield entry if data entry.size = data.bytesize write_header entry write_data data else write_header entry end nil ensure entry.close end def write_data(*args) if block_given? raise ArgumentError, "wrong number of argument (#{args.size} for 0)" if args.size > 0 ar = archive len = 0 loop do str = yield if (n = C.archive_write_data(ar, str, str.bytesize)) < 1 return len end len += n end else raise ArgumentError, "wrong number of argument (#{args.size}) for 1)" if args.size != 1 str = args[0] C.archive_write_data(archive, str, str.bytesize) end end def write_header(entry) raise Error, @archive if C.archive_write_header(archive, entry.entry) != C::OK end end end ffi-libarchive-1.0.0/test/000077500000000000000000000000001360153566100153475ustar00rootroot00000000000000ffi-libarchive-1.0.0/test/data/000077500000000000000000000000001360153566100162605ustar00rootroot00000000000000ffi-libarchive-1.0.0/test/data/test.tar.gz000066400000000000000000000006551360153566100203740ustar00rootroot00000000000000fOM1HQ©UHΥwk ZmIN @h*Ph "piB[Z\h^e6 wp}ߧ?c^H; 5߿3b1Ή 1L.%βixk]'m(!2S:O@Wo CAh䟊_wȀEkΟ~?'*mDz_*JuV~E" oË[τ置 ?Ʋ Ӻ6ʟ.hXLZ]?O1OqoPNQX'Ns׏^RmS@aps'Y;yNSr_̞Mf^wv(ffi-libarchive-1.0.0/test/data/test.zip000066400000000000000000000024761360153566100177740ustar00rootroot00000000000000PK `F>test/UT NM]ux PK aF>test/b/UT OM]ux PK r`F> test/b/c/UT NM]ux PK *bF>[ test/b/c/c.datUT OM]ux  _1fbuYPK `F> test/b/c/d/UT NM]ux PK *bF>[ test/b/c/d/d.datUT OM]ux  _1fbuYPK aF>-q test/b/b.datUT  OM]ux s&(M=!s'NY 8!PK aF>>$ test/a.datUT OM]ux Y잹ދR{D{ĉxu\@~ SRMPK `F>Atest/UTNMux PK aF>A?test/b/UTOMux PK r`F> Atest/b/c/UTNMux PK *bF>[ test/b/c/c.datUTOMux PK `F> A+test/b/c/d/UTNMux PK *bF>[ ptest/b/c/d/d.datUTOMux PK aF>-q test/b/b.datUT OMux PK aF>>$ @test/a.datUTOMux PKffi-libarchive-1.0.0/test/sets/000077500000000000000000000000001360153566100163255ustar00rootroot00000000000000ffi-libarchive-1.0.0/test/sets/ts_read.rb000066400000000000000000000130261360153566100202750ustar00rootroot00000000000000require "ffi-libarchive" require "tmpdir" require "test/unit" class TS_ReadArchive < Test::Unit::TestCase CONTENT_SPEC = [ ["test/", "directory", 0755, nil ], ["test/b/", "directory", 0755, nil ], ["test/b/c/", "directory", 0755, nil ], ["test/b/c/c.dat", "file", 0600, "\266\262\v_\266\243\305\3601\204\277\351\354\265\003\036\036\365f\377\210\205\032\222\346\370b\360u\032Y\301".b ], ["test/b/c/d/", "directory", 0711, nil ], ["test/b/c/d/d.dat", "symbolic_link", 0777, "../c.dat" ], ["test/b/b.dat", "file", 0640, "s&\245\354(M\331=\270\000!s\355\240\252\355'N\304\343\bY\317\t\274\210\3128\321\347\234!".b ], ["test/a.dat", "file", 0777, "\021\216\231Y\354\236\271\372\336\213\224R\211{D{\277\262\304\211xu\330\\\275@~\035\vSRM".b ], ].freeze def setup File.open("data/test.tar.gz", "rb") do |f| @archive_content = f.read end end def test_read_tar_gz_from_file Archive.read_open_filename("data/test.tar.gz") do |ar| verify_content(ar) end end def test_read_tar_gz_from_file_with_external_gunzip Archive.read_open_filename("data/test.tar.gz", "gunzip") do |ar| verify_content(ar) end end def test_read_tar_gz_from_memory Archive.read_open_memory(@archive_content) do |ar| verify_content(ar) end end def test_read_tar_gz_from_memory_with_external_gunzip Archive.read_open_memory(@archive_content, "gunzip") do |ar| verify_content(ar) end end def test_read_entry_bigger_than_internal_buffer alphabet = "abcdefghijklmnopqrstuvwxyz" entry_size = 1024 * 4 - 3 srand content = "" 1.upto(entry_size) do |i| index = rand(alphabet.size) content += alphabet[index, 1] end Dir.mktmpdir do |dir| Archive.write_open_filename(dir + "/test.tar.gz", Archive::COMPRESSION_BZIP2, Archive::FORMAT_TAR) do |ar| ar.new_entry do |entry| entry.pathname = "chubby.dat" entry.mode = 0666 entry.filetype = Archive::Entry::FILE entry.atime = Time.now.to_i entry.mtime = Time.now.to_i entry.size = entry_size ar.write_header(entry) ar.write_data(content) end end Archive.read_open_filename(dir + "/test.tar.gz") do |ar| ar.next_header data = ar.read_data assert_equal entry_size, data.size assert_equal content.size, data.size assert_equal content, data end Archive.read_open_filename(dir + "/test.tar.gz") do |ar| ar.next_header data = "" ar.read_data(128) { |chunk| data += chunk } assert_equal content, data end end end def test_extract_no_additional_flags Dir.mktmpdir do |dir| Archive.read_open_filename("data/test.tar.gz") do |ar| Dir.chdir(dir) do ar.each_entry do |e| ar.extract(e) assert_not_equal File.mtime(e.pathname), e.mtime end end end end end def test_extract_extract_time Dir.mktmpdir do |dir| Archive.read_open_filename("data/test.tar.gz") do |ar| Dir.chdir(dir) do ar.each_entry do |e| ar.extract(e, Archive::EXTRACT_TIME.to_i) next if e.directory? || e.symbolic_link? assert_equal File.mtime(e.pathname), e.mtime end end end end end def test_read_from_stream_with_proc fp = File.open("data/test.tar.gz", "rb") reader = Proc.new do fp.read(32) end Archive.read_open_stream(reader) do |ar| verify_content(ar) end end class TestReader def initialize @fp = File.open("data/test.tar.gz", "rb") end def call @fp.read(32) end end def test_read_from_stream_with_object Archive.read_open_stream(TestReader.new) do |ar| verify_content(ar) end end class SkipNSeekTestReader < TestReader attr_reader :skip_called, :seek_called def initialize @fp = File.open("data/test.zip") end def skip(offset) @skip_called = true orig_pos = fp.tell fp.seek(offset, :CUR) fp.tell - orig_pos end def seek(offset, whence) @seek_called = true @fp.seek(offset, whence) @fp.tell end end def test_read_from_stream_with_skip_seek_object expect_pathname, expect_type, _, expect_content = CONTENT_SPEC[6] verified = false reader = SkipNSeekTestReader.new Archive.read_open_stream(reader) do |ar| ar.each_entry do |entry| next unless entry.pathname == expect_pathname verified = true assert_equal expect_pathname, entry.pathname assert_equal entry.send("#{expect_type}?"), true # Skip verifying file mode; Zip files don't store it. assert entry.file? content = ar.read_data(1024) assert_equal expect_content, content end end assert verified assert reader.skip_called assert reader.seek_called end private def verify_content(ar) content_spec_idx = 0 while (entry = ar.next_header) expect_pathname, expect_type, expect_mode, expect_content = CONTENT_SPEC[content_spec_idx] assert_equal expect_pathname, entry.pathname assert_equal entry.send("#{expect_type}?"), true assert_equal expect_mode, (entry.mode & 07777) if entry.symbolic_link? assert_equal expect_content, entry.symlink elsif entry.file? content = ar.read_data(1024) assert_equal expect_content, content end content_spec_idx += 1 end end end ffi-libarchive-1.0.0/test/sets/ts_write.rb000066400000000000000000000071161360153566100205170ustar00rootroot00000000000000require "ffi-libarchive" require "tmpdir" require "test/unit" class TS_WriteArchive < Test::Unit::TestCase CONTENT_SPEC = [ ["test/", "directory", 0755, nil ], ["test/b/", "directory", 0755, nil ], ["test/b/c/", "directory", 0755, nil ], ["test/b/c/c.dat", "file", 0600, "\266\262\v_\266\243\305\3601\204\277\351\354\265\003\036\036\365f\377\210\205\032\222\346\370b\360u\032Y\301".b ], ["test/b/c/d/", "directory", 0711, nil ], ["test/b/c/d/d.dat", "symbolic_link", 0777, "../c.dat" ], ["test/b/b.dat", "file", 0640, "s&\245\354(M\331=\270\000!s\355\240\252\355'N\304\343\bY\317\t\274\210\3128\321\347\234!".b ], ["test/a.dat", "file", 0777, "\021\216\231Y\354\236\271\372\336\213\224R\211{D{\277\262\304\211xu\330\\\275@~\035\vSRM".b ], ].freeze def test_end_to_end_write_read_tar_gz Dir.mktmpdir do |dir| Archive.write_open_filename(dir + "/test.tar.gz", :gzip, :tar) do |ar| write_content(ar) end verify_content(dir + "/test.tar.gz") end end def test_end_to_end_write_read_memory memory = "" Archive.write_open_memory(memory, Archive::COMPRESSION_GZIP, Archive::FORMAT_TAR) do |ar| write_content ar end verify_content_memory(memory) end def test_end_to_end_write_read_tar_gz_with_external_gzip Dir.mktmpdir do |dir| Archive.write_open_filename(dir + "/test.tar.gz", "gzip", :tar) do |ar| write_content(ar) end verify_content(dir + "/test.tar.gz") end end private def write_content(ar) content_spec_idx = 0 while content_spec_idx < CONTENT_SPEC.size entry_path, entry_type, entry_mode, entry_content = \ CONTENT_SPEC[content_spec_idx] ar.new_entry do |entry| entry.pathname = entry_path entry.mode = entry_mode entry.filetype = eval "Archive::Entry::#{entry_type.upcase}" # rubocop:disable Security/Eval entry.size = entry_content.size if entry_content entry.symlink = entry_content if entry_type == "symbolic_link" entry.atime = Time.now.to_i entry.mtime = Time.now.to_i ar.write_header(entry) if entry_type == "file" ar.write_data(entry_content) end end content_spec_idx += 1 end end def verify_content_memory(memory) Archive.read_open_memory(memory) do |ar| content_spec_idx = 0 while (entry = ar.next_header) expect_pathname, expect_type, expect_mode, expect_content = \ CONTENT_SPEC[content_spec_idx] assert_equal expect_pathname, entry.pathname assert_equal entry.send("#{expect_type}?"), true assert_equal expect_mode, (entry.mode & 07777) if entry.symbolic_link? assert_equal expect_content, entry.symlink elsif entry.file? content = ar.read_data(1024) assert_equal expect_content, content end content_spec_idx += 1 end end end def verify_content(filename) Archive.read_open_filename(filename) do |ar| content_spec_idx = 0 while (entry = ar.next_header) expect_pathname, expect_type, expect_mode, expect_content = \ CONTENT_SPEC[content_spec_idx] assert_equal expect_pathname, entry.pathname assert_equal entry.send("#{expect_type}?"), true assert_equal expect_mode, (entry.mode & 07777) if entry.symbolic_link? assert_equal expect_content, entry.symlink elsif entry.file? content = ar.read_data(1024) assert_equal expect_content, content end content_spec_idx += 1 end end end end ffi-libarchive-1.0.0/test/test_ffi-libarchive.rb000066400000000000000000000002111360153566100215770ustar00rootroot00000000000000 Dir.chdir File.dirname(__FILE__) $LOAD_PATH.unshift "../lib/", "." require "test/unit" require "sets/ts_read" require "sets/ts_write"