diff --git a/lib/mixlib/archive.rb b/lib/mixlib/archive.rb index 1d9c849..09b3929 100644 --- a/lib/mixlib/archive.rb +++ b/lib/mixlib/archive.rb @@ -5,6 +5,8 @@ module Mixlib class Archive + class TarError < StandardError; end + attr_reader :archiver alias_method :extractor, :archiver diff --git a/lib/mixlib/archive/tar.rb b/lib/mixlib/archive/tar.rb index b3cb640..89bae4f 100644 --- a/lib/mixlib/archive/tar.rb +++ b/lib/mixlib/archive/tar.rb @@ -34,7 +34,7 @@ def extract(destination, perms: true, ignore: []) Mixlib::Archive::Log.warn "ignoring entry #{entry.full_name}" next end - dest ||= File.join(destination, entry.full_name) + dest ||= File.expand_path(File.join(destination, entry.full_name)) parent = File.dirname(dest) FileUtils.mkdir_p(parent) @@ -76,11 +76,18 @@ def create(files, gzip: false) Gem::Package::TarWriter.new(target) do |tar| files.each do |fn| mode = File.stat(fn).mode - file = File.open(fn, "rb") - tar.add_file(fn, mode) do |io| - io.write(file) + if File.symlink?(fn) + target = File.readlink(fn) + tar.add_symlink(fn, target, mode) + elsif File.directory?(fn) + tar.mkdir(fn, mode) + elsif File.file?(fn) + file = File.open(fn, "rb") + tar.add_file(fn, mode) do |io| + io.write(file.read) + end + file.close end - file.close end end @@ -110,6 +117,14 @@ def is_gzip_file?(path) IO.binread(path, 2) == [0x1F, 0x8B].pack("C*") end + # tar's magic is at byte 257 and is "ustar\0" + def is_tar_archive?(io) + io.rewind + magic = io.read[257..262] + io.rewind + magic == "ustar\0" + end + def reader(&block) raw = File.open(archive, "rb") @@ -120,6 +135,8 @@ def reader(&block) raw end + raise Mixlib::Archive::TarError, "Unrecognized archive format" unless is_tar_archive?(file) + Gem::Package::TarReader.new(file, &block) ensure if file diff --git a/spec/mixlib/tar_spec.rb b/spec/mixlib/tar_spec.rb index 10e8de3..598649c 100644 --- a/spec/mixlib/tar_spec.rb +++ b/spec/mixlib/tar_spec.rb @@ -20,10 +20,11 @@ end describe "#reader" do - let(:raw) { double(File, closed?: true) } + let(:raw) { double(IO, closed?: true, rewind: 0) } before do allow(Gem::Package::TarReader).to receive(:new).with(raw, &extraction).and_return(true) + allow_any_instance_of(Mixlib::Archive::Tar).to receive(:is_tar_archive?).and_return(true) end context "with a gzipped file" do