minitar-0.6.1/ 0000755 0000041 0000041 00000000000 13053507500 013207 5 ustar www-data www-data minitar-0.6.1/Rakefile 0000644 0000041 0000041 00000002623 13053507500 014657 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'rubygems'
require 'hoe'
require 'rake/clean'
$LOAD_PATH.unshift('support')
Hoe.plugin :doofus
Hoe.plugin :gemspec2
Hoe.plugin :git
Hoe.plugin :minitest
Hoe.plugin :travis
Hoe.plugin :deprecated_gem
Hoe.plugin :email unless ENV['CI'] or ENV['TRAVIS']
spec = Hoe.spec 'minitar' do
developer('Austin Ziegler', 'halostatue@gmail.com')
require_ruby_version '>= 1.8'
self.history_file = 'History.md'
self.readme_file = 'README.rdoc'
self.licenses = ['Ruby', 'BSD-2-Clause']
self.post_install_message = <<-EOS
The `minitar` executable is no longer bundled with `minitar`. If you are
expecting this executable, make sure you also install `minitar-cli`.
EOS
extra_dev_deps << ['hoe-doofus', '~> 1.0']
extra_dev_deps << ['hoe-gemspec2', '~> 1.1']
extra_dev_deps << ['hoe-git', '~> 1.6']
extra_dev_deps << ['hoe-rubygems', '~> 1.0']
extra_dev_deps << ['hoe-travis', '~> 1.2']
extra_dev_deps << ['minitest', '~> 5.3']
extra_dev_deps << ['minitest-autotest', ['>= 1.0', '<2']]
extra_dev_deps << ['rake', '>= 10.0', '< 12']
extra_dev_deps << ['rdoc', '>= 0.0']
end
if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby'
namespace :test do
desc 'Run test coverage'
task :coverage do
spec.test_prelude = 'load ".simplecov-prelude.rb"'
Rake::Task['test'].execute
end
end
Rake::Task['travis'].prerequisites.replace(%w(test:coverage))
end
minitar-0.6.1/Manifest.txt 0000644 0000041 0000041 00000001033 13053507500 015513 0 ustar www-data www-data Code-of-Conduct.md
Contributing.md
History.md
Licence.md
Manifest.txt
README.rdoc
Rakefile
docs/bsdl.txt
docs/ruby.txt
lib/archive-tar-minitar.rb
lib/archive/tar/minitar.rb
lib/archive/tar/minitar/input.rb
lib/archive/tar/minitar/output.rb
lib/archive/tar/minitar/posix_header.rb
lib/archive/tar/minitar/reader.rb
lib/archive/tar/minitar/writer.rb
lib/minitar.rb
test/minitest_helper.rb
test/support/tar_test_helpers.rb
test/test_tar_header.rb
test/test_tar_input.rb
test/test_tar_output.rb
test/test_tar_reader.rb
test/test_tar_writer.rb
minitar-0.6.1/README.rdoc 0000644 0000041 0000041 00000006156 13053507500 015025 0 ustar www-data www-data = minitar
home :: https://github.com/halostatue/minitar/
code :: https://github.com/halostatue/minitar/
bugs :: https://github.com/halostatue/minitar/issues
rdoc :: http://rdoc.info/gems/minitar/
cli :: https://github.com/halostatue/minitar-cli
travis :: {
}[https://travis-ci.org/halostatue/minitar]
appveyor :: {
}[https://ci.appveyor.com/project/halostatue/minitar]
coveralls :: {
}[https://coveralls.io/r/halostatue/minitar]
== Description
The minitar library is a pure-Ruby library that provides the ability to deal
with POSIX tar(1) archive files.
This is release 0.6, providing a number of bug fixes including a directory
traversal vulnerability, CVE-2016-10173. This release starts the migration and
modernization of the code:
* the licence has been changed to match the modern Ruby licensing scheme
(Ruby and Simplified BSD instead of Ruby and GNU GPL);
* the +minitar+ command-line program has been separated into the
+minitar-cli+ gem; and
* the +archive-tar-minitar+ gem now points to the +minitar+ and +minitar-cli+
gems and discourages its installation.
Some of these changes may break existing programs that depend on the internal
structure of the minitar library, but every effort has been made to ensure
compatibility; inasmuch as is possible, this compatibility will be maintained
through the release of minitar 1.0 (which will have strong breaking changes).
minitar (previously called Archive::Tar::Minitar) is based heavily on code
originally written by Mauricio Julio Fernández Pradier for the rpa-base
project.
== Synopsis
Using minitar is easy. The simplest case is:
require 'minitar'
# Packs everything that matches Find.find('tests').
# test.tar will automatically be closed by Minitar.pack.
Minitar.pack('tests', File.open('test.tar', 'wb'))
# Unpacks 'test.tar' to 'x', creating 'x' if necessary.
Minitar.unpack('test.tar', 'x')
A gzipped tar can be written with:
require 'zlib'
# test.tgz will be closed automatically.
Minitar.pack('tests', Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))
# test.tgz will be closed automatically.
Minitar.unpack(Zlib::GzipReader.new(File.open('test.tgz', 'rb')), 'x')
As the case above shows, one need not write to a file. However, it will
sometimes require that one dive a little deeper into the API, as in the case of
StringIO objects. Note that I'm not providing a block with Minitar::Output, as
Minitar::Output#close automatically closes both the Output object and the
wrapped data stream object.
begin
sgz = Zlib::GzipWriter.new(StringIO.new(String.new))
tar = Output.new(sgz)
Find.find('tests') do |entry|
Minitar.pack_file(entry, tar)
end
ensure
# Closes both tar and sgz.
tar.close
end
== minitar Semantic Versioning
The minitar library uses a {Semantic Versioning}[http://semver.org/] scheme
with one change:
* When PATCH is zero (+0+), it will be omitted from version references.
minitar-0.6.1/lib/ 0000755 0000041 0000041 00000000000 13053507500 013755 5 ustar www-data www-data minitar-0.6.1/lib/archive/ 0000755 0000041 0000041 00000000000 13053507500 015376 5 ustar www-data www-data minitar-0.6.1/lib/archive/tar/ 0000755 0000041 0000041 00000000000 13053507500 016164 5 ustar www-data www-data minitar-0.6.1/lib/archive/tar/minitar.rb 0000644 0000041 0000041 00000022453 13053507500 020162 0 ustar www-data www-data # coding: utf-8
##
module Archive; end
##
module Archive::Tar; end
require 'fileutils'
require 'rbconfig'
class << Archive::Tar #:nodoc:
def const_missing(const) #:nodoc:
case const
when :PosixHeader
warn 'Archive::Tar::PosixHeader has been renamed to ' \
'Archive::Tar::Minitar::PosixHeader'
const_set :PosixHeader, Archive::Tar::Minitar::PosixHeader
else
super
end
end
private
def included(mod)
return if modules.include?(mod)
warn "Including Minitar via the #{self} namespace is deprecated."
modules.add mod
end
def modules
require 'set'
@modules ||= Set.new
end
end
# == Synopsis
#
# Using minitar is easy. The simplest case is:
#
# require 'zlib'
# require 'minitar'
#
# # Packs everything that matches Find.find('tests').
# # test.tar will automatically be closed by Minitar.pack.
# Minitar.pack('tests', File.open('test.tar', 'wb'))
#
# # Unpacks 'test.tar' to 'x', creating 'x' if necessary.
# Minitar.unpack('test.tar', 'x')
#
# A gzipped tar can be written with:
#
# # test.tgz will be closed automatically.
# Minitar.pack('tests', Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))
#
# # test.tgz will be closed automatically.
# Minitar.unpack(Zlib::GzipReader.new(File.open('test.tgz', 'rb')), 'x')
#
# As the case above shows, one need not write to a file. However, it will
# sometimes require that one dive a little deeper into the API, as in the case
# of StringIO objects. Note that I'm not providing a block with
# Minitar::Output, as Minitar::Output#close automatically closes both the
# Output object and the wrapped data stream object.
#
# begin
# sgz = Zlib::GzipWriter.new(StringIO.new(""))
# tar = Output.new(sgz)
# Find.find('tests') do |entry|
# Minitar.pack_file(entry, tar)
# end
# ensure
# # Closes both tar and sgz.
# tar.close
# end
module Archive::Tar::Minitar
VERSION = '0.6.1'.freeze # :nodoc:
# The base class for any minitar error.
Error = Class.new(::StandardError)
# Raised when a wrapped data stream class is not seekable.
NonSeekableStream = Class.new(Error)
# The exception raised when operations are performed on a stream that has
# previously been closed.
ClosedStream = Class.new(Error)
# The exception raised when a filename exceeds 256 bytes in length, the
# maximum supported by the standard Tar format.
FileNameTooLong = Class.new(Error)
# The exception raised when a data stream ends before the amount of data
# expected in the archive's PosixHeader.
UnexpectedEOF = Class.new(StandardError)
# The exception raised when a file contains a relative path in secure mode
# (the default for this version).
SecureRelativePathError = Class.new(Error)
class << self
# Tests if +path+ refers to a directory. Fixes an apparently
# corrupted stat() call on Windows.
def dir?(path)
# rubocop:disable Style/CharacterLiteral
File.directory?(path[-1] == ?/ ? path : "#{path}/")
# rubocop:enable Style/CharacterLiteral
end
# A convenience method for wrapping Archive::Tar::Minitar::Input.open
# (mode +r+) and Archive::Tar::Minitar::Output.open (mode +w+). No other
# modes are currently supported.
def open(dest, mode = 'r', &block)
case mode
when 'r'
Input.open(dest, &block)
when 'w'
Output.open(dest, &block)
else
raise 'Unknown open mode for Archive::Tar::Minitar.open.'
end
end
def const_missing(c) #:nodoc:
case c
when :BlockRequired
warn 'This constant has been removed.'
const_set(:BlockRequired, Class.new(StandardError))
else
super
end
end
def windows? #:nodoc:
RbConfig::CONFIG['host_os'] =~ /^(mswin|mingw|cygwin)/
end
# A convenience method to pack the file provided. +entry+ may either be a
# filename (in which case various values for the file (see below) will be
# obtained from File#stat(entry) or a Hash with the fields:
#
# :name:: The filename to be packed into the archive. Required.
# :mode:: The mode to be applied.
# :uid:: The user owner of the file. (Ignored on Windows.)
# :gid:: The group owner of the file. (Ignored on Windows.)
# :mtime:: The modification Time of the file.
#
# During packing, if a block is provided, #pack_file yields an +action+
# Symol, the full name of the file being packed, and a Hash of
# statistical information, just as with
# Archive::Tar::Minitar::Input#extract_entry.
#
# The +action+ will be one of:
# :dir:: The +entry+ is a directory.
# :file_start:: The +entry+ is a file; the extract of the
# file is just beginning.
# :file_progress:: Yielded every 4096 bytes during the extract
# of the +entry+.
# :file_done:: Yielded when the +entry+ is completed.
#
# The +stats+ hash contains the following keys:
# :current:: The current total number of bytes read in the
# +entry+.
# :currinc:: The current number of bytes read in this read
# cycle.
# :name:: The filename to be packed into the tarchive.
# *REQUIRED*.
# :mode:: The mode to be applied.
# :uid:: The user owner of the file. (+nil+ on Windows.)
# :gid:: The group owner of the file. (+nil+ on Windows.)
# :mtime:: The modification Time of the file.
def pack_file(entry, outputter) #:yields action, name, stats:
if outputter.kind_of?(Archive::Tar::Minitar::Output)
outputter = outputter.tar
end
stats = {}
if entry.kind_of?(Hash)
name = entry[:name]
entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
else
name = entry
end
name = name.sub(%r{\./}, '')
stat = File.stat(name)
stats[:mode] ||= stat.mode
stats[:mtime] ||= stat.mtime
stats[:size] = stat.size
if windows?
stats[:uid] = nil
stats[:gid] = nil
else
stats[:uid] ||= stat.uid
stats[:gid] ||= stat.gid
end
if File.file?(name)
outputter.add_file_simple(name, stats) do |os|
stats[:current] = 0
yield :file_start, name, stats if block_given?
File.open(name, 'rb') do |ff|
until ff.eof?
stats[:currinc] = os.write(ff.read(4096))
stats[:current] += stats[:currinc]
yield :file_progress, name, stats if block_given?
end
end
yield :file_done, name, stats if block_given?
end
elsif dir?(name)
yield :dir, name, stats if block_given?
outputter.mkdir(name, stats)
else
raise %q(Don't yet know how to pack this type of file.)
end
end
# A convenience method to pack files specified by +src+ into +dest+. If
# +src+ is an Array, then each file detailed therein will be packed into
# the resulting Archive::Tar::Minitar::Output stream; if +recurse_dirs+ is
# true, then directories will be recursed.
#
# If +src+ is an Array, it will be treated as the result of Find.find; all
# files matching will be packed.
def pack(src, dest, recurse_dirs = true, &block)
require 'find'
Output.open(dest) do |outp|
if src.kind_of?(Array)
src.each do |entry|
if dir?(entry) and recurse_dirs
Find.find(entry) do |ee|
pack_file(ee, outp, &block)
end
else
pack_file(entry, outp, &block)
end
end
else
Find.find(src) do |entry|
pack_file(entry, outp, &block)
end
end
end
end
# A convenience method to unpack files from +src+ into the directory
# specified by +dest+. Only those files named explicitly in +files+
# will be extracted.
def unpack(src, dest, files = [], &block)
Input.open(src) do |inp|
if File.exist?(dest) and !dir?(dest)
raise %q(Can't unpack to a non-directory.)
end
FileUtils.mkdir_p(dest) unless File.exist?(dest)
inp.each do |entry|
if files.empty? or files.include?(entry.full_name)
inp.extract_entry(dest, entry, &block)
end
end
end
end
# Check whether +io+ can seek without errors.
def seekable?(io, methods = nil)
# The IO class throws an exception at runtime if we try to change
# position on a non-regular file.
if io.respond_to?(:stat)
io.stat.file?
else
# Duck-type the rest of this.
methods ||= [ :pos, :pos=, :seek, :rewind ]
methods = [ methods ] unless methods.kind_of?(Array)
methods.all? { |m| io.respond_to?(m) }
end
end
private
def included(mod)
return if modules.include?(mod)
warn "Including #{self} has been deprecated."
modules << mod
end
def modules
require 'set'
@modules ||= Set.new
end
end
end
require 'archive/tar/minitar/posix_header'
require 'archive/tar/minitar/input'
require 'archive/tar/minitar/output'
minitar-0.6.1/lib/archive/tar/minitar/ 0000755 0000041 0000041 00000000000 13053507500 017627 5 ustar www-data www-data minitar-0.6.1/lib/archive/tar/minitar/output.rb 0000644 0000041 0000041 00000005074 13053507500 021522 0 ustar www-data www-data # coding: utf-8
require 'archive/tar/minitar/writer'
module Archive::Tar::Minitar
# Wraps a Archive::Tar::Minitar::Writer with convenience methods and wrapped
# stream management. If the stream provided to Output does not support random
# access, only Writer#add_file_simple and Writer#mkdir are guaranteed to
# work.
class Output
# With no associated block, +Output.open+ is a synonym for +Output.new+. If
# the optional code block is given, it will be given the new Output as an
# argument and the Output object will automatically be closed when the
# block terminates (this also closes the wrapped stream object). In this
# instance, +Output.open+ returns the value of the block.
#
# call-seq:
# Archive::Tar::Minitar::Output.open(io) -> output
# Archive::Tar::Minitar::Output.open(io) { |output| block } -> obj
def self.open(output)
stream = new(output)
return stream unless block_given?
# This exception context must remain, otherwise the stream closes on open
# even if a block is not given.
begin
yield stream
ensure
stream.close
end
end
# Output.tar is a wrapper for Output.open that yields the owned tar object
# instead of the Output object. If a block is not provided, an enumerator
# will be created with the same behaviour.
#
# call-seq:
# Archive::Tar::Minitar::Output.tar(io) -> enumerator
# Archive::Tar::Minitar::Output.tar(io) { |tar| block } -> obj
def self.tar(output)
return to_enum(__method__, output) unless block_given?
open(output) do |stream|
yield stream.tar
end
end
# Creates a new Output object. If +output+ is a stream object that responds
# to #write, then it will simply be wrapped. Otherwise, one will be created
# and opened using Kernel#open. When Output#close is called, the stream
# object wrapped will be closed.
#
# call-seq:
# Archive::Tar::Minitar::Output.new(io) -> output
# Archive::Tar::Minitar::Output.new(path) -> output
def initialize(output)
@io = if output.respond_to?(:write)
output
else
::Kernel.open(output, 'wb')
end
@tar = Archive::Tar::Minitar::Writer.new(@io)
end
# Returns the Writer object for direct access.
attr_reader :tar
# Returns false if the wrapped data stream is open.
def closed?
@io.closed?
end
# Closes the Writer object and the wrapped data stream.
def close
@tar.close
@io.close
end
end
end
minitar-0.6.1/lib/archive/tar/minitar/posix_header.rb 0000644 0000041 0000041 00000016324 13053507500 022634 0 ustar www-data www-data # coding: utf-8
##
module Archive; end
##
module Archive::Tar; end
##
module Archive::Tar::Minitar; end
# Implements the POSIX tar header as a Ruby class. The structure of
# the POSIX tar header is:
#
# struct tarfile_entry_posix
# { // pack/unpack
# char name[100]; // ASCII (+ Z unless filled) a100/Z100
# char mode[8]; // 0 padded, octal, null a8 /A8
# char uid[8]; // 0 padded, octal, null a8 /A8
# char gid[8]; // 0 padded, octal, null a8 /A8
# char size[12]; // 0 padded, octal, null a12 /A12
# char mtime[12]; // 0 padded, octal, null a12 /A12
# char checksum[8]; // 0 padded, octal, null, space a8 /A8
# char typeflag[1]; // see below a /a
# char linkname[100]; // ASCII + (Z unless filled) a100/Z100
# char magic[6]; // "ustar\0" a6 /A6
# char version[2]; // "00" a2 /A2
# char uname[32]; // ASCIIZ a32 /Z32
# char gname[32]; // ASCIIZ a32 /Z32
# char devmajor[8]; // 0 padded, octal, null a8 /A8
# char devminor[8]; // 0 padded, octal, null a8 /A8
# char prefix[155]; // ASCII (+ Z unless filled) a155/Z155
# };
#
# The #typeflag is one of several known values.
#
# POSIX indicates that "A POSIX-compliant implementation must treat any
# unrecognized typeflag value as a regular file."
class Archive::Tar::Minitar::PosixHeader
BLOCK_SIZE = 512
# Fields that must be set in a POSIX tar(1) header.
REQUIRED_FIELDS = [ :name, :size, :prefix, :mode ].freeze
# Fields that may be set in a POSIX tar(1) header.
OPTIONAL_FIELDS = [
:uid, :gid, :mtime, :checksum, :typeflag, :linkname, :magic, :version,
:uname, :gname, :devmajor, :devminor
].freeze
# All fields available in a POSIX tar(1) header.
FIELDS = (REQUIRED_FIELDS + OPTIONAL_FIELDS).freeze
FIELDS.each do |f|
attr_reader f.to_sym unless f.to_sym == :name
end
# The name of the file. By default, limited to 100 bytes. Required. May be
# longer (up to BLOCK_SIZE bytes) if using the GNU long name tar extension.
attr_accessor :name
# The pack format passed to Array#pack for encoding a header.
HEADER_PACK_FORMAT = 'a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155'.freeze
# The unpack format passed to String#unpack for decoding a header.
HEADER_UNPACK_FORMAT = 'Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155'.freeze
class << self
# Creates a new PosixHeader from a data stream.
def from_stream(stream)
from_data(stream.read(BLOCK_SIZE))
end
# Creates a new PosixHeader from a data stream. Deprecated; use
# PosixHeader.from_stream instead.
def new_from_stream(stream)
warn "#{__method__} has been deprecated; use from_stream instead."
from_stream(stream)
end
# Creates a new PosixHeader from a BLOCK_SIZE-byte data buffer.
def from_data(data)
fields = data.unpack(HEADER_UNPACK_FORMAT)
name = fields.shift
mode = fields.shift.oct
uid = fields.shift.oct
gid = fields.shift.oct
size = fields.shift.oct
mtime = fields.shift.oct
checksum = fields.shift.oct
typeflag = fields.shift
linkname = fields.shift
magic = fields.shift
version = fields.shift.oct
uname = fields.shift
gname = fields.shift
devmajor = fields.shift.oct
devminor = fields.shift.oct
prefix = fields.shift
empty = !data.each_byte.any?(&:nonzero?)
new(
:name => name,
:mode => mode,
:uid => uid,
:gid => gid,
:size => size,
:mtime => mtime,
:checksum => checksum,
:typeflag => typeflag,
:magic => magic,
:version => version,
:uname => uname,
:gname => gname,
:devmajor => devmajor,
:devminor => devminor,
:prefix => prefix,
:empty => empty,
:linkname => linkname
)
end
end
# Creates a new PosixHeader. A PosixHeader cannot be created unless
# +name+, +size+, +prefix+, and +mode+ are provided.
def initialize(v)
REQUIRED_FIELDS.each do |f|
raise ArgumentError, "Field #{f} is required." unless v.key?(f)
end
v[:mtime] = v[:mtime].to_i
v[:checksum] ||= ''
v[:typeflag] ||= '0'
v[:magic] ||= 'ustar'
v[:version] ||= '00'
FIELDS.each do |f|
instance_variable_set("@#{f}", v[f])
end
@empty = v[:empty]
end
# Indicates if the header was an empty header.
def empty?
@empty
end
# Returns +true+ if the header is a long name special header which indicates
# that the next block of data is the filename.
def long_name?
typeflag == 'L' && name == '././@LongLink'
end
# A string representation of the header.
def to_s
update_checksum
header(@checksum)
end
alias to_str to_s
# Update the checksum field.
def update_checksum
hh = header(' ' * 8)
@checksum = oct(calculate_checksum(hh), 6)
end
private
def oct(num, len)
if num.nil?
"\0" * (len + 1)
else
"%0#{len}o" % num
end
end
def calculate_checksum(hdr)
hdr.unpack('C*').inject { |a, e| a + e }
end
def header(chksum)
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
oct(mtime, 11), chksum, ' ', typeflag, linkname, magic, version,
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
str = arr.pack(HEADER_PACK_FORMAT)
str + "\0" * ((BLOCK_SIZE - str.size) % BLOCK_SIZE)
end
##
# :attr_reader: size
# The size of the file. Required.
##
# :attr_reader: prefix
# The prefix of the file; the path before #name. Limited to 155 bytes.
# Required.
##
# :attr_reader: mode
# The Unix file mode of the file. Stored as an octal integer. Required.
##
# :attr_reader: uid
# The Unix owner user ID of the file. Stored as an octal integer.
##
# :attr_reader: uname
# The user name of the Unix owner of the file.
##
# :attr_reader: gid
# The Unix owner group ID of the file. Stored as an octal integer.
##
# :attr_reader: gname
# The group name of the Unix owner of the file.
##
# :attr_reader: mtime
# The modification time of the file in epoch seconds. Stored as an
# octal integer.
##
# :attr_reader: checksum
# The checksum of the file. Stored as an octal integer. Calculated
# before encoding the header as a string.
##
# :attr_reader: typeflag
# The type of record in the file.
#
# +0+:: Regular file. NULL should be treated as a synonym, for compatibility
# purposes.
# +1+:: Hard link.
# +2+:: Symbolic link.
# +3+:: Character device node.
# +4+:: Block device node.
# +5+:: Directory.
# +6+:: FIFO node.
# +7+:: Reserved.
##
# :attr_reader: linkname
# The name of the link stored. Not currently used.
##
# :attr_reader: magic
# Always "ustar\0".
##
# :attr_reader: version
# Always "00"
##
# :attr_reader: devmajor
# The major device ID. Not currently used.
##
# :attr_reader: devminor
# The minor device ID. Not currently used.
end
minitar-0.6.1/lib/archive/tar/minitar/writer.rb 0000644 0000041 0000041 00000022001 13053507500 021463 0 ustar www-data www-data # coding: utf-8
module Archive::Tar::Minitar
# The class that writes a tar format archive to a data stream.
class Writer
# The exception raised when the user attempts to write more data to a
# BoundedWriteStream than has been allocated.
WriteBoundaryOverflow = Class.new(StandardError)
# A stream wrapper that can only be written to. Any attempt to read
# from this restricted stream will result in a NameError being thrown.
class WriteOnlyStream
def initialize(io)
@io = io
end
def write(data)
@io.write(data)
end
end
private_constant :WriteOnlyStream if respond_to?(:private_constant)
# A WriteOnlyStream that also has a size limit.
class BoundedWriteStream < WriteOnlyStream
def self.const_missing(c)
case c
when :FileOverflow
warn 'Writer::BoundedWriteStream::FileOverflow has been renamed ' \
'to Writer::WriteBoundaryOverflow'
const_set :FileOverflow,
Archive::Tar::Minitar::Writer::WriteBoundaryOverflow
else
super
end
end
# The maximum number of bytes that may be written to this data
# stream.
attr_reader :limit
# The current total number of bytes written to this data stream.
attr_reader :written
def initialize(io, limit)
@io = io
@limit = limit
@written = 0
end
def write(data)
raise WriteBoundaryOverflow if (data.size + @written) > @limit
@io.write(data)
@written += data.size
data.size
end
end
private_constant :BoundedWriteStream if respond_to?(:private_constant)
def self.const_missing(c)
case c
when :BoundedStream
warn 'BoundedStream has been renamed to BoundedWriteStream'
const_set(:BoundedStream, BoundedWriteStream)
else
super
end
end
# With no associated block, +Writer::open+ is a synonym for +Writer::new+.
# If the optional code block is given, it will be passed the new _writer_
# as an argument and the Writer object will automatically be closed when
# the block terminates. In this instance, +Writer::open+ returns the value
# of the block.
#
# call-seq:
# w = Archive::Tar::Minitar::Writer.open(STDOUT)
# w.add_file_simple('foo.txt', :size => 3)
# w.close
#
# Archive::Tar::Minitar::Writer.open(STDOUT) do |w|
# w.add_file_simple('foo.txt', :size => 3)
# end
def self.open(io) # :yields Writer:
writer = new(io)
return writer unless block_given?
# This exception context must remain, otherwise the stream closes on open
# even if a block is not given.
begin
yield writer
ensure
writer.close
end
end
# Creates and returns a new Writer object.
def initialize(io)
@io = io
@closed = false
end
# Adds a file to the archive as +name+. The data can be provided in the
# opts[:data] or provided to a BoundedWriteStream that is
# yielded to the provided block.
#
# If opts[:data] is provided, all other values to +opts+ are
# optional. If the data is provided to the yielded BoundedWriteStream,
# opts[:size] must be provided.
#
# Valid parameters to +opts+ are:
#
# :data:: Optional. The data to write to the archive.
# :mode:: The Unix file permissions mode value. If not
# provided, defaults to 0644.
# :size:: The size, in bytes. If :data is provided,
# this parameter may be ignored (if it is less than
# the size of the data provided) or used to add
# padding (if it is greater than the size of the data
# provided).
# :uid:: The Unix file owner user ID number.
# :gid:: The Unix file owner group ID number.
# :mtime:: File modification time, interpreted as an integer.
#
# An exception will be raised if the Writer is already closed, or if
# more data is written to the BoundedWriteStream than expected.
#
# call-seq:
# writer.add_file_simple('foo.txt', :data => "bar")
# writer.add_file_simple('foo.txt', :size => 3) do |w|
# w.write("bar")
# end
def add_file_simple(name, opts = {}) # :yields BoundedWriteStream:
raise ClosedStream if @closed
name, prefix = split_name(name)
header = {
:prefix => prefix,
:name => name,
:mode => opts.fetch(:mode, 0o644),
:mtime => opts.fetch(:mtime, nil),
:gid => opts.fetch(:gid, nil),
:uid => opts.fetch(:uid, nil)
}
data = opts.fetch(:data, nil)
size = opts.fetch(:size, nil)
if block_given?
if data
raise ArgumentError,
'Too much data (opts[:data] and block_given?).'
end
raise ArgumentError, 'No size provided' unless size
else
raise ArgumentError, 'No data provided' unless data
size = data.size if size < data.size
end
header[:size] = size
@io.write(PosixHeader.new(header))
os = BoundedWriteStream.new(@io, opts[:size])
if block_given?
yield os
else
os.write(data)
end
min_padding = opts[:size] - os.written
@io.write("\0" * min_padding)
remainder = (512 - (opts[:size] % 512)) % 512
@io.write("\0" * remainder)
end
# Adds a file to the archive as +name+. The data can be provided in the
# opts[:data] or provided to a yielded +WriteOnlyStream+. The
# size of the file will be determined from the amount of data written
# to the stream.
#
# Valid parameters to +opts+ are:
#
# :mode:: The Unix file permissions mode value. If not
# provided, defaults to 0644.
# :uid:: The Unix file owner user ID number.
# :gid:: The Unix file owner group ID number.
# :mtime:: File modification time, interpreted as an integer.
# :data:: Optional. The data to write to the archive.
#
# If opts[:data] is provided, this acts the same as
# #add_file_simple. Otherwise, the file's size will be determined from
# the amount of data written to the stream.
#
# For #add_file to be used without opts[:data], the Writer
# must be wrapping a stream object that is seekable. Otherwise,
# #add_file_simple must be used.
#
# +opts+ may be modified during the writing of the file to the stream.
def add_file(name, opts = {}, &block) # :yields WriteOnlyStream, +opts+:
raise ClosedStream if @closed
return add_file_simple(name, opts, &block) if opts[:data]
unless Archive::Tar::Minitar.seekable?(@io)
raise Archive::Tar::Minitar::NonSeekableStream
end
name, prefix = split_name(name)
init_pos = @io.pos
@io.write("\0" * 512) # placeholder for the header
yield WriteOnlyStream.new(@io), opts
size = @io.pos - (init_pos + 512)
remainder = (512 - (size % 512)) % 512
@io.write("\0" * remainder)
final_pos, @io.pos = @io.pos, init_pos
header = {
:name => name,
:mode => opts[:mode],
:mtime => opts[:mtime],
:size => size,
:gid => opts[:gid],
:uid => opts[:uid],
:prefix => prefix
}
@io.write(PosixHeader.new(header))
@io.pos = final_pos
end
# Creates a directory entry in the tar.
def mkdir(name, opts = {})
raise ClosedStream if @closed
name, prefix = split_name(name)
header = {
:name => name,
:mode => opts[:mode],
:typeflag => '5',
:size => 0,
:gid => opts[:gid],
:uid => opts[:uid],
:mtime => opts[:mtime],
:prefix => prefix
}
@io.write(PosixHeader.new(header))
nil
end
# Passes the #flush method to the wrapped stream, used for buffered
# streams.
def flush
raise ClosedStream if @closed
@io.flush if @io.respond_to?(:flush)
end
# Returns false if the writer is open.
def closed?
@closed
end
# Closes the Writer. This does not close the underlying wrapped output
# stream.
def close
return if @closed
@io.write("\0" * 1024)
@closed = true
end
private
def split_name(name)
# TODO: Enable long-filename write support.
raise FileNameTooLong if name.size > 256
if name.size <= 100
prefix = ''
else
parts = name.split(/\//)
newname = parts.pop
nxt = ''
loop do
nxt = parts.pop || ''
break if newname.size + 1 + nxt.size >= 100
newname = "#{nxt}/#{newname}"
end
prefix = (parts + [nxt]).join('/')
name = newname
raise FileNameTooLong if name.size > 100 || prefix.size > 155
end
[ name, prefix ]
end
end
end
minitar-0.6.1/lib/archive/tar/minitar/input.rb 0000644 0000041 0000041 00000015641 13053507500 021322 0 ustar www-data www-data # coding: utf-8
require 'archive/tar/minitar/reader'
module Archive::Tar::Minitar
# Wraps a Archive::Tar::Minitar::Reader with convenience methods and wrapped
# stream management; Input only works with data streams that can be rewound.
class Input
include Enumerable
# With no associated block, +Input.open+ is a synonym for +Input.new+. If
# the optional code block is given, it will be given the new Input as an
# argument and the Input object will automatically be closed when the block
# terminates (this also closes the wrapped stream object). In this
# instance, +Input.open+ returns the value of the block.
#
# call-seq:
# Archive::Tar::Minitar::Input.open(io) -> input
# Archive::Tar::Minitar::Input.open(io) { |input| block } -> obj
def self.open(input)
stream = new(input)
return stream unless block_given?
# This exception context must remain, otherwise the stream closes on open
# even if a block is not given.
begin
yield stream
ensure
stream.close
end
end
# Iterates over each entry in the provided input. This wraps the common
# pattern of:
#
# Archive::Tar::Minitar::Input.open(io) do |i|
# inp.each do |entry|
# # ...
# end
# end
#
# If a block is not provided, an enumerator will be created with the same
# behaviour.
#
# call-seq:
# Archive::Tar::Minitar::Input.each_entry(io) -> enumerator
# Archive::Tar::Minitar::Input.each_entry(io) { |entry| block } -> obj
def self.each_entry(input)
return to_enum(__method__, input) unless block_given?
open(input) do |stream|
stream.each do |entry|
yield entry
end
end
end
# Creates a new Input object. If +input+ is a stream object that responds
# to #read, then it will simply be wrapped. Otherwise, one will be created
# and opened using Kernel#open. When Input#close is called, the stream
# object wrapped will be closed.
#
# An exception will be raised if the stream that is wrapped does not
# support rewinding.
#
# call-seq:
# Archive::Tar::Minitar::Input.new(io) -> input
# Archive::Tar::Minitar::Input.new(path) -> input
def initialize(input)
@io = if input.respond_to?(:read)
input
else
::Kernel.open(input, 'rb')
end
unless Archive::Tar::Minitar.seekable?(@io, :rewind)
raise Archive::Tar::Minitar::NonSeekableStream
end
@tar = Reader.new(@io)
end
# When provided a block, iterates through each entry in the archive. When
# finished, rewinds to the beginning of the stream.
#
# If not provided a block, creates an enumerator with the same semantics.
def each_entry
return to_enum unless block_given?
@tar.each do |entry|
yield entry
end
ensure
@tar.rewind
end
alias each each_entry
# Extracts the current +entry+ to +destdir+. If a block is provided, it
# yields an +action+ Symbol, the full name of the file being extracted
# (+name+), and a Hash of statistical information (+stats+).
#
# The +action+ will be one of:
# :dir:: The +entry+ is a directory.
# :file_start:: The +entry+ is a file; the extract of the
# file is just beginning.
# :file_progress:: Yielded every 4096 bytes during the extract
# of the +entry+.
# :file_done:: Yielded when the +entry+ is completed.
#
# The +stats+ hash contains the following keys:
# :current:: The current total number of bytes read in the
# +entry+.
# :currinc:: The current number of bytes read in this read
# cycle.
# :entry:: The entry being extracted; this is a
# Reader::EntryStream, with all methods thereof.
def extract_entry(destdir, entry) # :yields action, name, stats:
stats = {
:current => 0,
:currinc => 0,
:entry => entry
}
# extract_entry is not vulnerable to prefix '/' vulnerabilities, but it
# is vulnerable to relative path directories. This code will break this
# vulnerability. For this version, we are breaking relative paths HARD by
# throwing an exception.
#
# Future versions may permit relative paths as long as the file does not
# leave +destdir+.
#
# However, squeeze consecutive '/' characters together.
full_name = entry.full_name.squeeze('/')
if full_name =~ /\.{2}(?:\/|\z)/
raise SecureRelativePathError, %q(Path contains '..')
end
if entry.directory?
dest = File.join(destdir, full_name)
yield :dir, full_name, stats if block_given?
if Archive::Tar::Minitar.dir?(dest)
begin
FileUtils.chmod(entry.mode, dest)
rescue
nil
end
else
File.unlink(dest.chomp('/')) if File.symlink?(dest.chomp('/'))
FileUtils.mkdir_p(dest, :mode => entry.mode)
FileUtils.chmod(entry.mode, dest)
end
fsync_dir(dest)
fsync_dir(File.join(dest, '..'))
return
else # it's a file
destdir = File.join(destdir, File.dirname(full_name))
FileUtils.mkdir_p(destdir, :mode => 0o755)
destfile = File.join(destdir, File.basename(full_name))
File.unlink(destfile) if File.symlink?(destfile)
# Errno::ENOENT
# rubocop:disable Style/RescueModifier
FileUtils.chmod(0o600, destfile) rescue nil
# rubocop:enable Style/RescueModifier
yield :file_start, full_name, stats if block_given?
File.open(destfile, 'wb', entry.mode) do |os|
loop do
data = entry.read(4096)
break unless data
stats[:currinc] = os.write(data)
stats[:current] += stats[:currinc]
yield :file_progress, full_name, stats if block_given?
end
os.fsync
end
FileUtils.chmod(entry.mode, destfile)
fsync_dir(File.dirname(destfile))
fsync_dir(File.join(File.dirname(destfile), '..'))
yield :file_done, full_name, stats if block_given?
end
end
# Returns false if the wrapped data stream is open.
def closed?
@io.closed?
end
# Returns the Reader object for direct access.
attr_reader :tar
# Closes both the Reader object and the wrapped data stream.
def close
@io.close
@tar.close
end
private
def fsync_dir(dirname)
# make sure this hits the disc
dir = open(dirname, 'rb')
dir.fsync
rescue # ignore IOError if it's an unpatched (old) Ruby
nil
ensure
dir.close if dir rescue nil # rubocop:disable Style/RescueModifier
end
end
end
minitar-0.6.1/lib/archive/tar/minitar/reader.rb 0000644 0000041 0000041 00000015421 13053507500 021421 0 ustar www-data www-data # coding: utf-8
module Archive::Tar::Minitar
# The class that reads a tar format archive from a data stream. The data
# stream may be sequential or random access, but certain features only work
# with random access data streams.
class Reader
# This marks the EntryStream closed for reading without closing the
# actual data stream.
module InvalidEntryStream
# rubocop:disable Style/SingleLineMethods
# rubocop:disable Style/EmptyLineBetweenDefs
def read(*); raise ClosedStream; end
def getc; raise ClosedStream; end
def rewind; raise ClosedStream; end
def closed?; true; end
# rubocop:enable Style/EmptyLineBetweenDefs
# rubocop:enable Style/SingleLineMethods Style/EmptyLineBetweenDefs
end
# EntryStreams are pseudo-streams on top of the main data stream.
class EntryStream
Archive::Tar::Minitar::PosixHeader::FIELDS.each do |field|
attr_reader field.to_sym
end
def initialize(header, io)
@io = io
@name = header.name
@mode = header.mode
@uid = header.uid
@gid = header.gid
@size = header.size
@mtime = header.mtime
@checksum = header.checksum
@typeflag = header.typeflag
@linkname = header.linkname
@magic = header.magic
@version = header.version
@uname = header.uname
@gname = header.gname
@devmajor = header.devmajor
@devminor = header.devminor
@prefix = header.prefix
@read = 0
@orig_pos =
if Archive::Tar::Minitar.seekable?(@io)
@io.pos
else
0
end
end
# Reads +len+ bytes (or all remaining data) from the entry. Returns
# +nil+ if there is no more data to read.
def read(len = nil)
return nil if @read >= @size
len ||= @size - @read
max_read = [len, @size - @read].min
ret = @io.read(max_read)
@read += ret.size
ret
end
# Reads one byte from the entry. Returns +nil+ if there is no more data
# to read.
def getc
return nil if @read >= @size
ret = @io.getc
@read += 1 if ret
ret
end
# Returns +true+ if the entry represents a directory.
def directory?
@typeflag == '5'
end
alias directory directory?
# Returns +true+ if the entry represents a plain file.
def file?
@typeflag == '0' || @typeflag == "\0"
end
alias file file?
# Returns +true+ if the current read pointer is at the end of the
# EntryStream data.
def eof?
@read >= @size
end
# Returns the current read pointer in the EntryStream.
def pos
@read
end
# Sets the current read pointer to the beginning of the EntryStream.
def rewind
unless Archive::Tar::Minitar.seekable?(@io, :pos=)
raise Archive::Tar::Minitar::NonSeekableStream
end
@io.pos = @orig_pos
@read = 0
end
def bytes_read
@read
end
# Returns the full and proper name of the entry.
def full_name
if @prefix != ''
File.join(@prefix, @name)
else
@name
end
end
# Returns false if the entry stream is valid.
def closed?
false
end
# Closes the entry.
def close
invalidate
end
private
def invalidate
extend InvalidEntryStream
end
end
# With no associated block, +Reader::open+ is a synonym for
# +Reader::new+. If the optional code block is given, it will be passed
# the new _writer_ as an argument and the Reader object will
# automatically be closed when the block terminates. In this instance,
# +Reader::open+ returns the value of the block.
def self.open(io)
reader = new(io)
return reader unless block_given?
# This exception context must remain, otherwise the stream closes on open
# even if a block is not given.
begin
yield reader
ensure
reader.close
end
end
# Iterates over each entry in the provided input. This wraps the common
# pattern of:
#
# Archive::Tar::Minitar::Input.open(io) do |i|
# inp.each do |entry|
# # ...
# end
# end
#
# If a block is not provided, an enumerator will be created with the same
# behaviour.
#
# call-seq:
# Archive::Tar::Minitar::Reader.each_entry(io) -> enumerator
# Archive::Tar::Minitar::Reader.each_entry(io) { |entry| block } -> obj
def self.each_entry(io)
return to_enum(__method__, io) unless block_given?
open(io) do |reader|
reader.each_entry do |entry|
yield entry
end
end
end
# Creates and returns a new Reader object.
def initialize(io)
@io = io
@init_pos = io.pos
end
# Resets the read pointer to the beginning of data stream. Do not call
# this during a #each or #each_entry iteration. This only works with
# random access data streams that respond to #rewind and #pos.
def rewind
if @init_pos.zero?
unless Archive::Tar::Minitar.seekable?(@io, :rewind)
raise Archive::Tar::Minitar::NonSeekableStream
end
@io.rewind
else
unless Archive::Tar::Minitar.seekable?(@io, :pos=)
raise Archive::Tar::Minitar::NonSeekableStream
end
@io.pos = @init_pos
end
end
# Iterates through each entry in the data stream.
def each_entry
return to_enum unless block_given?
loop do
return if @io.eof?
header = Archive::Tar::Minitar::PosixHeader.from_stream(@io)
return if header.empty?
if header.long_name?
name = @io.read(512).rstrip
header = PosixHeader.from_stream(@io)
return if header.empty?
header.name = name
end
entry = EntryStream.new(header, @io)
size = entry.size
yield entry
skip = (512 - (size % 512)) % 512
if Archive::Tar::Minitar.seekable?(@io, :seek)
# avoid reading...
@io.seek(size - entry.bytes_read, IO::SEEK_CUR)
else
pending = size - entry.bytes_read
while pending > 0
bread = @io.read([pending, 4096].min).size
raise UnexpectedEOF if @io.eof?
pending -= bread
end
end
@io.read(skip) # discard trailing zeros
# make sure nobody can use #read, #getc or #rewind anymore
entry.close
end
end
alias each each_entry
# Returns false if the reader is open (it never closes).
def closed?
false
end
def close
end
end
end
minitar-0.6.1/lib/archive-tar-minitar.rb 0000644 0000041 0000041 00000000057 13053507500 020152 0 ustar www-data www-data # coding: utf-8
require 'archive/tar/minitar'
minitar-0.6.1/lib/minitar.rb 0000644 0000041 0000041 00000000331 13053507500 015742 0 ustar www-data www-data # coding: utf-8
require 'archive/tar/minitar'
if defined? ::Minitar
warn <<-EOS
::Minitar is already defined.
This will conflict with future versions of minitar.
EOS
else
::Minitar = Archive::Tar::Minitar
end
minitar-0.6.1/test/ 0000755 0000041 0000041 00000000000 13053507500 014166 5 ustar www-data www-data minitar-0.6.1/test/test_tar_output.rb 0000644 0000041 0000041 00000002540 13053507500 017761 0 ustar www-data www-data #!/usr/bin/env ruby
require 'minitar'
require 'minitest_helper'
class TestTarOutput < Minitest::Test
def setup
FileUtils.mkdir_p('data__')
%w(a b c).each do |filename|
name = File.join('data__', filename)
File.open(name, 'wb') { |f|
f.puts "#{name}: 123456789012345678901234567890"
}
end
@tarfile = 'data__/bla2.tar'
end
def teardown
FileUtils.rm_rf('data__')
end
def test_open_no_block
output = Minitar::Output.open(@tarfile)
refute output.closed?
ensure
output.close
assert output.closed?
end
def test_file_looks_good
Minitar::Output.open(@tarfile) do |os|
Dir.chdir('data__') do
%w(a b c).each do |name|
stat = File.stat(name)
opts = { :size => stat.size, :mode => 0o644 }
os.tar.add_file_simple(name, opts) do |ss|
File.open(name, 'rb') { |ff| ss.write(ff.read(4096)) until ff.eof? }
end
end
end
end
ff = File.open(@tarfile, 'rb')
Minitar::Reader.open(ff) do |is|
ii = 0
is.each do |entry|
case ii
when 0
assert_equal('a', entry.name)
when 1
assert_equal('b', entry.name)
when 2
assert_equal('c', entry.name)
end
ii += 1
end
assert_equal(3, ii)
end
ensure
ff.close if ff
end
end
minitar-0.6.1/test/test_tar_input.rb 0000644 0000041 0000041 00000012755 13053507500 017571 0 ustar www-data www-data #!/usr/bin/env ruby
require 'minitar'
require 'minitest_helper'
require 'base64'
require 'zlib'
class TestTarInput < Minitest::Test
TEST_TGZ = Base64.decode64(<<-EOS).freeze
H4sIAKJpllQAA0tJLEnUK0ks0kuvYqAVMDAwMDMxUQDR5mbmYNrACMIHA2MjIwUDc3NzEzMz
QxMDAwUDQ2NTczMGBQOauQgJlBYDfQ90SiKQkZmHWx1QWVoaHnMgXlGA00MEyHdzMMzOnBbC
wPz28n2uJgOR44Xrq7tsHc/utNe/9FdihkmH3pZ7+zOTRFREzkzYJ99iHHDn4n0/Wb3E8Ceq
S0uOdSyMMg9Z+WVvX0vJucxs77vrvZf2arWcvHP9wa1Yp9lRnJmC59/P9+43PXum+tj7Ga+8
rtT+u3d941e765Y/bOrnvpv8X6jtz+wKqyk/v3n8P5xlO3l/1dn9q9Zotpy5funw/Of77Y/5
LVltz7ToTl7dXf5ppmf3n9p+PPxz/sz/qjZn9yf9Y4R7I2Ft3tqfPTUMGgMYlEMSpGXmpBrT
2A5Qvjc1xZ3/DTDyv5GJmfFo/qcHCMnILFYAIlA6UDDWU+DlGmgXjYJRMApGwSgYBaNgFIyC
UTAKRsEoGAWjYBSMglEwCkbBKBgFo2AUjIJRMApGwSgYBaNgFIwCUgAAGnyo6wAoAAA=
EOS
FILETIMES = Time.utc(2004).to_i
TEST_CONTENTS = {
'data.tar.gz' => { :size => 210, :mode => 0o644 },
'file3' => { :size => 18, :mode => 0o755 }
}.freeze
TEST_DATA_CONTENTS = {
'data/' => { :size => 0, :mode => 0o755 },
'data/__dir__/' => { :size => 0, :mode => 0o755 },
'data/file1' => { :size => 16, :mode => 0o644 },
'data/file2' => { :size => 16, :mode => 0o644 }
}.freeze
def setup
FileUtils.mkdir_p('data__')
end
def teardown
FileUtils.rm_rf('data__')
end
def test_open_no_block
reader = Zlib::GzipReader.new(StringIO.new(TEST_TGZ))
input = Minitar::Input.open(reader)
refute input.closed?
ensure
input.close
assert input.closed?
end
def test_each_works
reader = Zlib::GzipReader.new(StringIO.new(TEST_TGZ))
Minitar::Input.open(reader) do |stream|
outer = 0
stream.each.with_index do |entry, i|
assert_kind_of(Minitar::Reader::EntryStream, entry)
assert TEST_CONTENTS.key?(entry.name)
assert_equal(TEST_CONTENTS[entry.name][:size], entry.size, entry.name)
assert_modes_equal(TEST_CONTENTS[entry.name][:mode],
entry.mode, entry.name)
assert_equal(FILETIMES, entry.mtime, 'entry.mtime')
if i.zero?
data_reader = Zlib::GzipReader.new(StringIO.new(entry.read))
Minitar::Input.open(data_reader) do |is2|
inner = 0
is2.each_with_index do |entry2, _j|
assert_kind_of(Minitar::Reader::EntryStream, entry2)
assert TEST_DATA_CONTENTS.key?(entry2.name)
assert_equal(TEST_DATA_CONTENTS[entry2.name][:size], entry2.size,
entry2.name)
assert_modes_equal(TEST_DATA_CONTENTS[entry2.name][:mode],
entry2.mode, entry2.name)
assert_equal(FILETIMES, entry2.mtime, entry2.name)
inner += 1
end
assert_equal(4, inner)
end
end
outer += 1
end
assert_equal(2, outer)
end
end
def test_extract_entry_works
reader = Zlib::GzipReader.new(StringIO.new(TEST_TGZ))
Minitar::Input.open(reader) do |stream|
outer_count = 0
stream.each_with_index do |entry, i|
stream.extract_entry('data__', entry)
name = File.join('data__', entry.name)
assert TEST_CONTENTS.key?(entry.name)
if entry.directory?
assert(File.directory?(name))
else
assert(File.file?(name))
assert_equal(TEST_CONTENTS[entry.name][:size], File.stat(name).size)
end
assert_modes_equal(TEST_CONTENTS[entry.name][:mode],
File.stat(name).mode, entry.name)
if i.zero?
begin
ff = File.open(name, 'rb')
data_reader = Zlib::GzipReader.new(ff)
Minitar::Input.open(data_reader) do |is2|
is2.each_with_index do |entry2, _j|
is2.extract_entry('data__', entry2)
name2 = File.join('data__', entry2.name)
assert TEST_DATA_CONTENTS.key?(entry2.name)
if entry2.directory?
assert(File.directory?(name2))
else
assert(File.file?(name2))
assert_equal(TEST_DATA_CONTENTS[entry2.name][:size],
File.stat(name2).size)
end
assert_modes_equal(TEST_DATA_CONTENTS[entry2.name][:mode],
File.stat(name2).mode, name2)
end
end
ensure
ff.close unless ff.closed?
end
end
outer_count += 1
end
assert_equal(2, outer_count)
end
end
def test_extract_entry_breaks_symlinks
return if Minitar.windows?
IO.respond_to?(:write) &&
IO.write('data__/file4', '') ||
File.open('data__/file4', 'w') { |f| f.write '' }
File.symlink('data__/file4', 'data__/file3')
File.symlink('data__/file4', 'data__/data')
Minitar.unpack(Zlib::GzipReader.new(StringIO.new(TEST_TGZ)), 'data__')
Minitar.unpack(Zlib::GzipReader.new(File.open('data__/data.tar.gz', 'rb')),
'data__')
refute File.symlink?('data__/file3')
refute File.symlink?('data__/data')
end
RELATIVE_DIRECTORY_TGZ = Base64.decode64 <<-EOS
H4sICIIoKVgCA2JhZC1kaXIudGFyANPT0y8sTy0qqWSgHTAwMDAzMVEA0eZmpmDawAjChwEFQ2MDQyMg
MDUzVDAwNDY0N2VQMGCgAygtLkksAjolEcjIzMOtDqgsLQ2/J0H+gNOjYBSMglEwyAEA2LchrwAGAAA=
EOS
def test_extract_entry_fails_with_relative_directory
reader = Zlib::GzipReader.new(StringIO.new(RELATIVE_DIRECTORY_TGZ))
Minitar::Input.open(reader) do |stream|
stream.each do |entry|
assert_raises Archive::Tar::Minitar::SecureRelativePathError do
stream.extract_entry('data__', entry)
end
end
end
end
end
minitar-0.6.1/test/test_tar_header.rb 0000644 0000041 0000041 00000004176 13053507500 017660 0 ustar www-data www-data # frozen_string_literal: true
require 'minitest_helper'
class TestTarHeader < Minitest::Test
def test_arguments_are_checked
ph = Archive::Tar::Minitar::PosixHeader
assert_raises(ArgumentError) {
ph.new(:name => '', :size => '', :mode => '')
}
assert_raises(ArgumentError) {
ph.new(:name => '', :size => '', :prefix => '')
}
assert_raises(ArgumentError) {
ph.new(:name => '', :prefix => '', :mode => '')
}
assert_raises(ArgumentError) {
ph.new(:prefix => '', :size => '', :mode => '')
}
end
def test_basic_headers
header = {
:name => 'bla',
:mode => 0o12345,
:size => 10,
:prefix => '',
:typeflag => '0'
}
assert_headers_equal(tar_file_header('bla', '', 0o12345, 10),
Archive::Tar::Minitar::PosixHeader.new(header).to_s)
header = {
:name => 'bla',
:mode => 0o12345,
:size => 0,
:prefix => '',
:typeflag => '5'
}
assert_headers_equal(tar_dir_header('bla', '', 0o12345),
Archive::Tar::Minitar::PosixHeader.new(header).to_s)
end
def test_long_name_works
header = {
:name => 'a' * 100, :mode => 0o12345, :size => 10, :prefix => ''
}
assert_headers_equal(tar_file_header('a' * 100, '', 0o12345, 10),
Archive::Tar::Minitar::PosixHeader.new(header).to_s)
header = {
:name => 'a' * 100, :mode => 0o12345, :size => 10, :prefix => 'bb' * 60
}
assert_headers_equal(tar_file_header('a' * 100, 'bb' * 60, 0o12345, 10),
Archive::Tar::Minitar::PosixHeader.new(header).to_s)
end
def test_from_stream
header = tar_file_header('a' * 100, '', 0o12345, 10)
header = StringIO.new(header)
h = Archive::Tar::Minitar::PosixHeader.from_stream(header)
assert_equal('a' * 100, h.name)
assert_equal(0o12345, h.mode)
assert_equal(10, h.size)
assert_equal('', h.prefix)
assert_equal('ustar', h.magic)
end
def test_from_stream_with_evil_name
header = tar_file_header("a \0" + "\0" * 97, '', 0o12345, 10)
header = StringIO.new(header)
h = Archive::Tar::Minitar::PosixHeader.from_stream header
assert_equal('a ', h.name)
end
end
minitar-0.6.1/test/minitest_helper.rb 0000644 0000041 0000041 00000000326 13053507500 017707 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'fileutils'
require 'minitar'
gem 'minitest'
require 'minitest/autorun'
Dir.glob(File.join(File.dirname(__FILE__), 'support/*.rb')).each do |support|
require support
end
minitar-0.6.1/test/support/ 0000755 0000041 0000041 00000000000 13053507500 015702 5 ustar www-data www-data minitar-0.6.1/test/support/tar_test_helpers.rb 0000644 0000041 0000041 00000005457 13053507500 021611 0 ustar www-data www-data # frozen_string_literal: true
module TarTestHelpers
Field = Struct.new(:name, :offset, :length)
def self.Field(name, length) # rubocop:disable Style/MethodName
@offset ||= 0
field = Field.new(name, @offset, length)
@offset += length
FIELDS[name] = field
FIELD_ORDER << name
field
end
private
FIELDS = {} # rubocop:disable Style/MutableConstant
FIELD_ORDER = [] # rubocop:disable Style/MutableConstant
Field('name', 100)
Field('mode', 8)
Field('uid', 8)
Field('gid', 8)
Field('size', 12)
Field('mtime', 12)
Field('checksum', 8)
Field('typeflag', 1)
Field('linkname', 100)
Field('magic', 6)
Field('version', 2)
Field('uname', 32)
Field('gname', 32)
Field('devmajor', 8)
Field('devminor', 8)
Field('prefix', 155)
BLANK_CHECKSUM = ' ' * 8
NULL_100 = "\0" * 100
USTAR = "ustar\0".freeze
DOUBLE_ZERO = '00'.freeze
def assert_headers_equal(expected, actual)
FIELD_ORDER.each do |field|
message = if field == 'checksum'
'Header checksums are expected to match.'
else
"Header field #{field} is expected to match."
end
offset = FIELDS[field].offset
length = FIELDS[field].length
assert_equal(expected[offset, length], actual[offset, length], message)
end
end
def assert_modes_equal(expected, actual, name)
return if Minitar.windows?
assert_equal(
mode_string(expected),
mode_string(actual),
"Mode for #{name} does not match"
)
end
def tar_file_header(fname, dname, mode, length)
update_checksum(header('0', fname, dname, length, mode))
end
def tar_dir_header(name, prefix, mode)
update_checksum(header('5', name, prefix, 0, mode))
end
def header(type, fname, dname, length, mode)
arr = [
asciiz(fname, 100), z(to_oct(mode, 7)), z(to_oct(nil, 7)),
z(to_oct(nil, 7)), z(to_oct(length, 11)), z(to_oct(0, 11)),
BLANK_CHECKSUM, type, NULL_100, USTAR, DOUBLE_ZERO, asciiz('', 32),
asciiz('', 32), z(to_oct(nil, 7)), z(to_oct(nil, 7)), asciiz(dname, 155)
]
h = arr.join.bytes.to_a.pack('C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155')
ret = h + "\0" * (512 - h.size)
assert_equal(512, ret.size)
ret
end
def update_checksum(header)
header[FIELDS['checksum'].offset, FIELDS['checksum'].length] =
# inject(:+) was introduced in which version?
sp(z(to_oct(header.unpack('C*').inject { |a, e| a + e }, 6)))
header
end
def to_oct(n, pad_size)
if n.nil?
"\0" * pad_size
else
"%0#{pad_size}o" % n
end
end
def asciiz(str, length)
str + "\0" * (length - str.length)
end
def sp(s)
s + ' '
end
def z(s)
s + "\0"
end
def mode_string(value)
'%04o' % (value & 0o777)
end
Minitest::Test.send(:include, self)
end
minitar-0.6.1/test/test_tar_writer.rb 0000644 0000041 0000041 00000013124 13053507500 017735 0 ustar www-data www-data #!/usr/bin/env ruby
require 'minitar'
require 'minitest_helper'
class TestTarWriter < Minitest::Test
class DummyIO
attr_reader :data
def initialize
@data = ''
end
def write(dat)
data << dat
dat.size
end
def reset
@data = ''
end
end
def setup
@data = 'a' * 10
@dummyos = DummyIO.new
@os = Minitar::Writer.new(@dummyos)
end
def teardown
@os.close
end
def test_open_no_block
writer = Minitar::Writer.open(@dummyos)
refute writer.closed?
ensure
writer.close
assert writer.closed?
end
def test_add_file_simple
@dummyos.reset
Minitar::Writer.open(@dummyos) do |os|
os.add_file_simple('lib/foo/bar', :mode => 0o644, :size => 10) do |f|
f.write 'a' * 10
end
os.add_file_simple('lib/bar/baz', :mode => 0o644, :size => 100) do |f|
f.write 'fillme'
end
end
assert_headers_equal(tar_file_header('lib/foo/bar', '', 0o644, 10),
@dummyos.data[0, 512])
assert_equal('a' * 10 + "\0" * 502, @dummyos.data[512, 512])
assert_headers_equal(tar_file_header('lib/bar/baz', '', 0o644, 100),
@dummyos.data[512 * 2, 512])
assert_equal('fillme' + "\0" * 506, @dummyos.data[512 * 3, 512])
assert_equal("\0" * 512, @dummyos.data[512 * 4, 512])
assert_equal("\0" * 512, @dummyos.data[512 * 5, 512])
end
def test_write_operations_fail_after_closed
@dummyos.reset
@os.add_file_simple('sadd', :mode => 0o644, :size => 20) { |f| }
@os.close
assert_raises(Minitar::ClosedStream) { @os.flush }
assert_raises(Minitar::ClosedStream) {
@os.add_file('dfdsf', :mode => 0o644) {}
}
assert_raises(Minitar::ClosedStream) { @os.mkdir 'sdfdsf', :mode => 0o644 }
end
def test_file_name_is_split_correctly
# test insane file lengths, and: a{100}/b{155}, etc
@dummyos.reset
names = [
"#{'a' * 155}/#{'b' * 100}",
"#{'a' * 151}/#{'qwer/' * 19}bla",
"/#{'a' * 49}/#{'b' * 50}",
"#{'a' * 49}/#{'b' * 50}x",
"#{'a' * 49}x/#{'b' * 50}"
]
o_names = [
'b' * 100,
"#{'qwer/' * 19}bla",
'b' * 50,
"#{'b' * 50}x",
'b' * 50
]
o_prefixes = [
'a' * 155,
'a' * 151,
"/#{'a' * 49}",
'a' * 49,
"#{'a' * 49}x"
]
names.each do |name|
@os.add_file_simple(name, :mode => 0o644, :size => 10) {}
end
names.each_index do |i|
assert_headers_equal(
tar_file_header(o_names[i], o_prefixes[i], 0o644, 10),
@dummyos.data[2 * i * 512, 512]
)
end
assert_raises(Minitar::FileNameTooLong) do
@os.add_file_simple(File.join('a' * 152, 'b' * 10, 'a' * 92),
:mode => 0o644, :size => 10) {}
end
assert_raises(Minitar::FileNameTooLong) do
@os.add_file_simple(File.join('a' * 162, 'b' * 10),
:mode => 0o644, :size => 10) {}
end
assert_raises(Minitar::FileNameTooLong) do
@os.add_file_simple(File.join('a' * 10, 'b' * 110),
:mode => 0o644, :size => 10) {}
end
# Issue #6.
assert_raises(Minitar::FileNameTooLong) do
@os.add_file_simple('a' * 114, :mode => 0o644, :size => 10) {}
end
end
def test_add_file
dummyos = StringIO.new
def dummyos.method_missing(meth, *a) # rubocop:disable Style/MethodMissing
string.send(meth, *a)
end
content1 = ('a'..'z').to_a.join('') # 26
content2 = ('aa'..'zz').to_a.join('') # 1352
Minitar::Writer.open(dummyos) do |os|
os.add_file('lib/foo/bar', :mode => 0o644) { |f, _opts| f.write 'a' * 10 }
os.add_file('lib/bar/baz', :mode => 0o644) { |f, _opts| f.write content1 }
os.add_file('lib/bar/baz', :mode => 0o644) { |f, _opts| f.write content2 }
os.add_file('lib/bar/baz', :mode => 0o644) { |_f, _opts| }
end
assert_headers_equal(tar_file_header('lib/foo/bar', '', 0o644, 10),
dummyos[0, 512])
assert_equal(%Q(#{'a' * 10}#{"\0" * 502}), dummyos[512, 512])
offset = 512 * 2
[content1, content2, ''].each do |data|
assert_headers_equal(tar_file_header('lib/bar/baz', '', 0o644,
data.size), dummyos[offset, 512])
offset += 512
until !data || data == ''
chunk = data[0, 512]
data[0, 512] = ''
assert_equal(chunk + "\0" * (512 - chunk.size),
dummyos[offset, 512])
offset += 512
end
end
assert_equal("\0" * 1024, dummyos[offset, 1024])
end
def test_add_file_tests_seekability
assert_raises(Archive::Tar::Minitar::NonSeekableStream) do
@os.add_file('libdfdsfd', :mode => 0o644) { |f| }
end
end
def test_write_header
@dummyos.reset
@os.add_file_simple('lib/foo/bar', :mode => 0o644, :size => 0) {}
@os.flush
assert_headers_equal(tar_file_header('lib/foo/bar', '', 0o644, 0),
@dummyos.data[0, 512])
@dummyos.reset
@os.mkdir('lib/foo', :mode => 0o644)
assert_headers_equal(tar_dir_header('lib/foo', '', 0o644),
@dummyos.data[0, 512])
@os.mkdir('lib/bar', :mode => 0o644)
assert_headers_equal(tar_dir_header('lib/bar', '', 0o644),
@dummyos.data[512 * 1, 512])
end
def test_write_data
@dummyos.reset
@os.add_file_simple('lib/foo/bar', :mode => 0o644, :size => 10) do |f|
f.write @data
end
@os.flush
assert_equal(@data + ("\0" * (512 - @data.size)),
@dummyos.data[512, 512])
end
def test_file_size_is_checked
@dummyos.reset
assert_raises(Minitar::Writer::WriteBoundaryOverflow) do
@os.add_file_simple('lib/foo/bar', :mode => 0o644, :size => 10) do |f|
f.write '1' * 100
end
end
@os.add_file_simple('lib/foo/bar', :mode => 0o644, :size => 10) { |f| }
end
end
minitar-0.6.1/test/test_tar_reader.rb 0000644 0000041 0000041 00000012044 13053507500 017663 0 ustar www-data www-data #!/usr/bin/env ruby
require 'minitar'
require 'minitest_helper'
class TestTarReader < Minitest::Test
def test_open_no_block
str = tar_file_header('lib/foo', '', 0o10644, 10) + "\0" * 512
str += tar_file_header('bar', 'baz', 0o644, 0)
str += tar_dir_header('foo', 'bar', 0o12345)
str += "\0" * 1024
reader = Minitar::Reader.open(StringIO.new(str))
refute reader.closed?
ensure
reader.close
refute reader.closed? # Reader doesn't actually close anything
end
def test_multiple_entries
str = tar_file_header('lib/foo', '', 0o10644, 10) + "\0" * 512
str += tar_file_header('bar', 'baz', 0o644, 0)
str += tar_dir_header('foo', 'bar', 0o12345)
str += "\0" * 1024
names = %w(lib/foo bar foo)
prefixes = ['', 'baz', 'bar']
modes = [0o10644, 0o644, 0o12345]
sizes = [10, 0, 0]
isdir = [false, false, true]
isfile = [true, true, false]
Minitar::Reader.new(StringIO.new(str)) do |is|
i = 0
is.each_entry do |entry|
assert_kind_of(Minitar::Reader::EntryStream, entry)
assert_equal(names[i], entry.name)
assert_equal(prefixes[i], entry.prefix)
assert_equal(sizes[i], entry.size)
assert_equal(modes[i], entry.mode)
assert_equal(isdir[i], entry.directory?)
assert_equal(isfile[i], entry.file?)
if prefixes[i] != ''
assert_equal(File.join(prefixes[i], names[i]), entry.full_name)
else
assert_equal(names[i], entry.name)
end
i += 1
end
assert_equal(names.size, i)
end
end
def test_rewind_entry_works
content = ('a'..'z').to_a.join(' ')
str = tar_file_header('lib/foo', '', 0o10644, content.size) + content +
"\0" * (512 - content.size)
str << "\0" * 1024
Minitar::Reader.new(StringIO.new(str)) do |is|
is.each_entry do |entry|
3.times do
entry.rewind
assert_equal(content, entry.read)
assert_equal(content.size, entry.pos)
end
end
end
end
def test_rewind_works
content = ('a'..'z').to_a.join(' ')
str = tar_file_header('lib/foo', '', 0o10644, content.size) + content +
"\0" * (512 - content.size)
str << "\0" * 1024
Minitar::Reader.new(StringIO.new(str)) do |is|
3.times do
is.rewind
i = 0
is.each_entry do |entry|
assert_equal(content, entry.read)
i += 1
end
assert_equal(1, i)
end
end
end
def test_read_works
contents = ('a'..'z').inject('') { |a, e| a << e * 100 }
str = tar_file_header('lib/foo', '', 0o10644, contents.size) + contents
str += "\0" * (512 - (str.size % 512))
Minitar::Reader.new(StringIO.new(str)) do |is|
is.each_entry do |entry|
assert_kind_of(Minitar::Reader::EntryStream, entry)
data = entry.read(3000) # bigger than contents.size
assert_equal(contents, data)
assert_equal(true, entry.eof?)
end
end
Minitar::Reader.new(StringIO.new(str)) do |is|
is.each_entry do |entry|
assert_kind_of(Minitar::Reader::EntryStream, entry)
data = entry.read(100)
(entry.size - data.size).times { data << entry.getc.chr }
assert_equal(contents, data)
assert_equal(nil, entry.read(10))
assert_equal(true, entry.eof?)
end
end
Minitar::Reader.new(StringIO.new(str)) do |is|
is.each_entry do |entry|
assert_kind_of(Minitar::Reader::EntryStream, entry)
data = entry.read
assert_equal(contents, data)
assert_equal(nil, entry.read(10))
assert_equal(nil, entry.read)
assert_equal(nil, entry.getc)
assert_equal(true, entry.eof?)
end
end
end
def test_eof_works
str = tar_file_header('bar', 'baz', 0o644, 0)
Minitar::Reader.new(StringIO.new(str)) do |is|
is.each_entry do |entry|
assert_kind_of(Minitar::Reader::EntryStream, entry)
data = entry.read
assert_equal(nil, data)
assert_equal(nil, entry.read(10))
assert_equal(nil, entry.read)
assert_equal(nil, entry.getc)
assert_equal(true, entry.eof?)
end
end
str = tar_dir_header('foo', 'bar', 0o12345)
Minitar::Reader.new(StringIO.new(str)) do |is|
is.each_entry do |entry|
assert_kind_of(Minitar::Reader::EntryStream, entry)
data = entry.read
assert_equal(nil, data)
assert_equal(nil, entry.read(10))
assert_equal(nil, entry.read)
assert_equal(nil, entry.getc)
assert_equal(true, entry.eof?)
end
end
str = tar_dir_header('foo', 'bar', 0o12345)
str += tar_file_header('bar', 'baz', 0o644, 0)
str += tar_file_header('bar', 'baz', 0o644, 0)
Minitar::Reader.new(StringIO.new(str)) do |is|
is.each_entry do |entry|
assert_kind_of(Minitar::Reader::EntryStream, entry)
data = entry.read
assert_equal(nil, data)
assert_equal(nil, entry.read(10))
assert_equal(nil, entry.read)
assert_equal(nil, entry.getc)
assert_equal(true, entry.eof?)
end
end
end
end
minitar-0.6.1/Code-of-Conduct.md 0000644 0000041 0000041 00000006236 13053507500 016411 0 ustar www-data www-data # Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
minitar-0.6.1/docs/ 0000755 0000041 0000041 00000000000 13053507500 014137 5 ustar www-data www-data minitar-0.6.1/docs/ruby.txt 0000644 0000041 0000041 00000004707 13053507500 015671 0 ustar www-data www-data Ruby is copyrighted free software by Yukihiro Matsumoto .
You can redistribute it and/or modify it under either the terms of the
2-clause BSDL (see the file BSDL), or the conditions below:
1. You may make and give away verbatim copies of the source form of the
software without restriction, provided that you duplicate all of the
original copyright notices and associated disclaimers.
2. You may modify your copy of the software in any way, provided that
you do at least ONE of the following:
a) place your modifications in the Public Domain or otherwise
make them Freely Available, such as by posting said
modifications to Usenet or an equivalent medium, or by allowing
the author to include your modifications in the software.
b) use the modified software only within your corporation or
organization.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
3. You may distribute the software in object code or binary form,
provided that you do at least ONE of the following:
a) distribute the binaries and library files of the software,
together with instructions (in the manual page or equivalent)
on where to get the original distribution.
b) accompany the distribution with the machine-readable source of
the software.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
4. You may modify and include the part of the software into any other
software (possibly commercial). But some files in the distribution
are not written by the author, so that they are not under these terms.
For the list of those files and their copying conditions, see the
file LEGAL.
5. The scripts and library files supplied as input to or produced as
output from the software do not automatically fall under the
copyright of the software, but belong to whomever generated them,
and may be sold commercially, and may be aggregated with this
software.
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. minitar-0.6.1/docs/bsdl.txt 0000644 0000041 0000041 00000002312 13053507500 015622 0 ustar www-data www-data Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
minitar-0.6.1/minitar.gemspec 0000644 0000041 0000041 00000011305 13053507500 016217 0 ustar www-data www-data #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = "minitar"
s.version = "0.6.1"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Austin Ziegler"]
s.date = "2017-02-08"
s.description = "The minitar library is a pure-Ruby library that provides the ability to deal\nwith POSIX tar(1) archive files.\n\nThis is release 0.6, providing a number of bug fixes including a directory\ntraversal vulnerability, CVE-2016-10173. This release starts the migration and\nmodernization of the code:\n\n* the licence has been changed to match the modern Ruby licensing scheme\n (Ruby and Simplified BSD instead of Ruby and GNU GPL);\n* the +minitar+ command-line program has been separated into the\n +minitar-cli+ gem; and\n* the +archive-tar-minitar+ gem now points to the +minitar+ and +minitar-cli+\n gems and discourages its installation.\n\nSome of these changes may break existing programs that depend on the internal\nstructure of the minitar library, but every effort has been made to ensure\ncompatibility; inasmuch as is possible, this compatibility will be maintained\nthrough the release of minitar 1.0 (which will have strong breaking changes).\n\nminitar (previously called Archive::Tar::Minitar) is based heavily on code\noriginally written by Mauricio Julio Fern\u{e1}ndez Pradier for the rpa-base\nproject."
s.email = ["halostatue@gmail.com"]
s.extra_rdoc_files = ["Code-of-Conduct.md", "Contributing.md", "History.md", "Licence.md", "Manifest.txt", "README.rdoc", "docs/bsdl.txt", "docs/ruby.txt"]
s.files = ["Code-of-Conduct.md", "Contributing.md", "History.md", "Licence.md", "Manifest.txt", "README.rdoc", "Rakefile", "docs/bsdl.txt", "docs/ruby.txt", "lib/archive-tar-minitar.rb", "lib/archive/tar/minitar.rb", "lib/archive/tar/minitar/input.rb", "lib/archive/tar/minitar/output.rb", "lib/archive/tar/minitar/posix_header.rb", "lib/archive/tar/minitar/reader.rb", "lib/archive/tar/minitar/writer.rb", "lib/minitar.rb", "test/minitest_helper.rb", "test/support/tar_test_helpers.rb", "test/test_tar_header.rb", "test/test_tar_input.rb", "test/test_tar_output.rb", "test/test_tar_reader.rb", "test/test_tar_writer.rb"]
s.homepage = "https://github.com/halostatue/minitar/"
s.licenses = ["Ruby", "BSD-2-Clause"]
s.post_install_message = "The `minitar` executable is no longer bundled with `minitar`. If you are\nexpecting this executable, make sure you also install `minitar-cli`.\n"
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.required_ruby_version = Gem::Requirement.new(">= 1.8")
s.rubygems_version = "1.8.23"
s.summary = "The minitar library is a pure-Ruby library that provides the ability to deal with POSIX tar(1) archive files"
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q, ["~> 3.16"])
s.add_development_dependency(%q, ["~> 1.0"])
s.add_development_dependency(%q, ["~> 1.1"])
s.add_development_dependency(%q, ["~> 1.6"])
s.add_development_dependency(%q, ["~> 1.0"])
s.add_development_dependency(%q, ["~> 1.2"])
s.add_development_dependency(%q, ["~> 5.10"])
s.add_development_dependency(%q, ["< 2", ">= 1.0"])
s.add_development_dependency(%q, ["< 12", ">= 10.0"])
s.add_development_dependency(%q, [">= 0.0"])
else
s.add_dependency(%q, ["~> 3.16"])
s.add_dependency(%q, ["~> 1.0"])
s.add_dependency(%q, ["~> 1.1"])
s.add_dependency(%q, ["~> 1.6"])
s.add_dependency(%q, ["~> 1.0"])
s.add_dependency(%q, ["~> 1.2"])
s.add_dependency(%q, ["~> 5.10"])
s.add_dependency(%q, ["< 2", ">= 1.0"])
s.add_dependency(%q, ["< 12", ">= 10.0"])
s.add_dependency(%q, [">= 0.0"])
end
else
s.add_dependency(%q, ["~> 3.16"])
s.add_dependency(%q, ["~> 1.0"])
s.add_dependency(%q, ["~> 1.1"])
s.add_dependency(%q, ["~> 1.6"])
s.add_dependency(%q, ["~> 1.0"])
s.add_dependency(%q, ["~> 1.2"])
s.add_dependency(%q, ["~> 5.10"])
s.add_dependency(%q, ["< 2", ">= 1.0"])
s.add_dependency(%q, ["< 12", ">= 10.0"])
s.add_dependency(%q, [">= 0.0"])
end
end
minitar-0.6.1/Licence.md 0000644 0000041 0000041 00000000626 13053507500 015077 0 ustar www-data www-data ## Licence
minitar is free software that may be redistributed and/or modified under the
terms of Ruby’s licence or the Simplified BSD licence.
* Copyright 2004–2017 Austin Ziegler.
* Portions copyright 2004 Mauricio Julio Fernández Pradier.
### Simplified BSD Licence
See the file docs/bsdl.txt in the main distribution.
### Ruby’s Licence
See the file docs/ruby.txt in the main distribution.
minitar-0.6.1/History.md 0000644 0000041 0000041 00000012636 13053507500 015202 0 ustar www-data www-data ## 0.6.1 / 2017-02-07
* Fixed issue [#24][] where streams were being improperly closed immediately
on open unless there was a block provided.
* Hopefully fixes issue [#23][] by releasing archive-tar-minitar after
minitar-cli is available.
## 0.6 / 2017-02-07
* Breaking Changes:
* Extracted `bin/minitar` into a new gem, `minitar-cli`. No, I am *not*
going to bump the major version for this. As far as I can tell, few
people use the command-line utility anyway. (Installing
`archive-tar-minitar` will install both `minitar` and `minitar-cli`, at
least until version 1.0.)
* Minitar extraction before 0.6 traverses directories if the tarball
includes a relative directory reference, as reported in [#16][] by
@ecneladis. This has been disallowed entirely and will throw a
SecureRelativePathError when found. Additionally, if the final
destination of an entry is an already-existing symbolic link, the
existing symbolic link will be removed and the file will be written
correctly (on platforms that support symblic links).
* Enhancements:
* Licence change. After speaking with Mauricio Fernández, we have changed
the licensing of this library to Ruby and Simplified BSD and have
dropped the GNU GPL license. This takes effect from the 0.6 release.
* Printing a deprecation warning for including Archive::Tar to put
Minitar in the top-level namespace.
* Printing a deprecation warning for including Archive::Tar::Minitar into
a class (Minitar will be a class for version 1.0).
* Moved Archive::Tar::PosixHeader to Archive::Tar::Minitar::PosixHeader
with a deprecation warning. Do not depend on
Archive::Tar::Minitar::PosixHeader, as it will be moving to
::Minitar::PosixHeader in a future release.
* Added an alias, ::Minitar, for Archive::Tar::Minitar, opted in with
`require 'minitar'`. In future releases, this alias will be enabled by
default, and the Archive::Tar namespace will be removed entirely for
version 1.0.
* Modified the handling of `mtime` in PosixHeader to do an integer
conversion (#to_i) so that a Time object can be used instead of the
integer value of the time object.
* Writer::RestrictedStream was renamed to Writer::WriteOnlyStream for
clarity. No alias or deprecation warning was provided for this as it is
an internal implementation detail.
* Writer::BoundedStream was renamed to Writer::BoundedWriteStream for
clarity. A deprecation warning is provided on first use because a
BoundedWriteStream may raise a BoundedWriteStream::FileOverflow
exception.
* Writer::BoundedWriteStream::FileOverflow has been renamed to
Writer::WriteBoundaryOverflow and inherits from StandardError instead
of RuntimeError. Note that for Ruby 2.0 or higher, an error will be
raised when specifying Writer::BoundedWriteStream::FileOverflow because
Writer::BoundedWriteStream has been declared a private constant.
* Modified Writer#add_file_simple to accept the data for a
file in `opts[:data]`. When `opts[:data]` is provided, a stream block
must not be provided. Improved the documentation for this method.
* Modified Writer#add_file to accept `opts[:data]` and transparently call
Writer#add_file_simple in this case.
* Methods that require blocks are no longer required, so the
Archive::Tar::Minitar::BlockRequired exception has been removed with a
warning (this may not work on Ruby 1.8).
* Dramatically reduced the number of strings created when creating a
POSIX tarball header.
* Added a helper, Input.each_entry that iterates over each entry in an
opened entry object.
* Bugs:
* Fix [#2][] to handle IO streams that are not seekable, such as pipes,
STDIN, or STDOUT.
* Fix [#3][] to make the test timezone resilient.
* Fix [#4][] for supporting the reading of tar files with filenames in
the GNU long filename extension format. Ported from @atoulme’s fork,
originally provided by Curtis Sampson.
* Fix [#6][] by making it raise the correct error for a long filename
with no path components.
* Fix [#13][] provided by @fetep fixes an off-by-one error on filename
splitting.
* Fix [#14][] provided by @kzys should fix Windows detection issues.
* Fix [#16][] as specified above.
* Fix an issue where Minitar.pack would not include Unix hidden files
when creating a tarball.
* Development:
* Modernized minitar tooling around Hoe.
* Added travis and coveralls.
## 0.5.2 / 2008-02-26
* Bugs:
* Fixed a Ruby 1.9 compatibility error.
## 0.5.1 / 2004-09-27
* Bugs:
* Fixed a variable name error.
## 0.5.0
* Initial release. Does files and directories. Command does create, extract,
and list.
[#2]: https://github.com/halostatue/minitar/issues/2
[#3]: https://github.com/halostatue/minitar/issues/3
[#4]: https://github.com/halostatue/minitar/issues/4
[#6]: https://github.com/halostatue/minitar/issues/6
[#13]: https://github.com/halostatue/minitar/issues/13
[#14]: https://github.com/halostatue/minitar/issues/14
[#16]: https://github.com/halostatue/minitar/issues/16
[#23]: https://github.com/halostatue/minitar/issues/23
[#24]: https://github.com/halostatue/minitar/issues/24
minitar-0.6.1/Contributing.md 0000644 0000041 0000041 00000005246 13053507500 016207 0 ustar www-data www-data ## Contributing
I value any contribution to minitar you can provide: a bug report, a feature
request, or code contributions. There are a few guidelines for contributing to
minitar:
* Code changes *will not* be accepted without tests. The test suite is
written with [Minitest][].
* Match my coding style.
* Use a thoughtfully-named topic branch that contains your change. Rebase
your commits into logical chunks as necessary.
* Use [quality commit messages][].
* Do not change the version number; when your patch is accepted and a release
is made, the version will be updated at that point.
* Submit a GitHub pull request with your changes.
* New or changed behaviours require appropriate documentation.
### Test Dependencies
minitar uses Ryan Davis’s [Hoe][] to manage the release process, and it adds a
number of rake tasks. You will mostly be interested in:
$ rake
which runs the tests the same way that:
$ rake test
$ rake travis
will do.
To assist with the installation of the development dependencies for minitar, I
have provided the simplest possible Gemfile pointing to the (generated)
`minitar.gemspec` file. This will permit you to do:
$ bundle install
to get the development dependencies. If you aleady have `hoe` installed, you
can accomplish the same thing with:
$ rake newb
This task will install any missing dependencies, run the tests/specs, and
generate the RDoc.
You can run tests with code coverage analysis by running:
$ rake test:coverage
### Workflow
Here's the most direct way to get your work merged into the project:
* Fork the project.
* Clone down your fork (`git clone git://github.com//minitar.git`).
* Create a topic branch to contain your change (`git checkout -b
my_awesome_feature`).
* Hack away, add tests. Not necessarily in that order.
* Make sure everything still passes by running `rake`.
* If necessary, rebase your commits into logical chunks, without errors.
* Push the branch up (`git push origin my_awesome_feature`).
* Create a pull request against halostatue/minitar and describe what your
change does and the why you think it should be merged.
### Contributors
* Austin Ziegler created minitar, based on work originally written by
Mauricio Fernández for rpa-base.
Thanks to everyone who has contributed to minitar:
* Antoine Toulme
* Curtis Sampson
* Daniel J. Berger
* Kazuyoshi Kato
* Matthew Kent
* Michal Suchanek
* Mike Furr
* Pete Fritchman
* Zach Dennis
[Minitest]: https://github.com/seattlerb/minitest
[quality commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
[Hoe]: https://github.com/seattlerb/hoe