Class: Minitar::Input

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/minitar/input.rb

Overview

Wraps a Minitar::Reader with convenience methods and wrapped stream management; Input only works with data streams that can be rewound.

Security Notice

Constructing a Minitar::Input will use Kernel.open if the provided input is not a readable stream object. Using an untrusted value for input may allow a malicious user to execute arbitrary system commands. It is the caller’s responsibility to ensure that the input value is safe.

This notice applies to Minitar::Input.open, Minitar::Input.each_entry, and Minitar::Input.new.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input) ⇒ Input

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:

Minitar::Input.new(io) -> input
Minitar::Input.new(path) -> input


83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/minitar/input.rb', line 83

def initialize(input)
  @io =
    if input.respond_to?(:read)
      input
    else
      ::Kernel.open(input, "rb")
    end

  raise Minitar::NonSeekableStream unless Minitar.seekable?(@io, :rewind)

  @tar = Reader.new(@io)
end

Instance Attribute Details

#tarObject (readonly)

Returns the Reader object for direct access.



162
163
164
# File 'lib/minitar/input.rb', line 162

def tar
  @tar
end

Class Method Details

.each_entry(input) ⇒ Object

Iterates over each entry in the provided input. This wraps the common pattern of:

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:

Minitar::Input.each_entry(io) -> enumerator
Minitar::Input.each_entry(io) { |entry| block } -> obj


63
64
65
66
67
68
69
70
71
# File 'lib/minitar/input.rb', line 63

def self.each_entry(input)
  return to_enum(__method__, input) unless block_given?

  Input.open(input) do |stream|
    stream.each do |entry|
      yield entry
    end
  end
end

.open(input) ⇒ Object

With no associated block, Input.open is a synonym for Input.new.

If a block is given, the new Input will be yielded to the block as an argument and the Input object will automatically be closed when the block terminates (this also closes the wrapped stream object). The return value will be the value of the block.

:call-seq:

Minitar::Input.open(io) -> input
Minitar::Input.open(io) { |input| block } -> obj


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/minitar/input.rb', line 34

def self.open(input)
  stream = new(input)

  if 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
  else
    stream
  end
end

Instance Method Details

#closeObject

Closes both the Reader object and the wrapped data stream.



165
166
167
168
# File 'lib/minitar/input.rb', line 165

def close
  @io.close
  @tar.close
end

#closed?Boolean

Returns false if the wrapped data stream is open.

Returns:

  • (Boolean)


159
# File 'lib/minitar/input.rb', line 159

def closed? = @io.closed?

#each_entryObject Also known as: each

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.



100
101
102
103
104
105
106
107
108
# File 'lib/minitar/input.rb', line 100

def each_entry
  return to_enum unless block_given?

  @tar.each do |entry|
    yield entry
  end
ensure
  @tar.rewind
end

#extract_entry(destdir, entry, options = {}) ⇒ Object

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.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/minitar/input.rb', line 130

def extract_entry(destdir, entry, options = {}, &) # :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 /\.{2}(?:\/|\z)/.match?(full_name)
    raise SecureRelativePathError, "Path contains '..'"
  end

  if entry.directory?
    extract_directory(destdir, full_name, entry, stats, options, &)
  else # it's a file
    extract_file(destdir, full_name, entry, stats, options, &)
  end
end