minitar 0.12.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Code-of-Conduct.md +105 -51
- data/History.md +62 -51
- data/Licence.md +2 -2
- data/Manifest.txt +7 -8
- data/README.rdoc +1 -1
- data/Rakefile +3 -33
- data/lib/{archive/tar/minitar → minitar}/input.rb +24 -28
- data/lib/{archive/tar/minitar → minitar}/output.rb +10 -12
- data/lib/minitar/posix_header.rb +267 -0
- data/lib/{archive/tar/minitar → minitar}/reader.rb +18 -23
- data/lib/{archive/tar/minitar → minitar}/writer.rb +43 -49
- data/lib/minitar.rb +297 -9
- data/test/minitest_helper.rb +0 -2
- data/test/support/tar_test_helpers.rb +7 -8
- data/test/test_issue_46.rb +49 -0
- data/test/test_minitar.rb +60 -0
- data/test/test_tar_header.rb +34 -36
- data/test/test_tar_input.rb +55 -55
- data/test/test_tar_output.rb +3 -3
- data/test/test_tar_reader.rb +54 -56
- data/test/test_tar_writer.rb +37 -41
- metadata +40 -15
- data/lib/archive/tar/minitar/posix_header.rb +0 -278
- data/lib/archive/tar/minitar.rb +0 -311
- data/lib/archive-tar-minitar.rb +0 -3
- data/support/hoe/deprecated_gem.rb +0 -64
data/lib/minitar.rb
CHANGED
@@ -1,12 +1,300 @@
|
|
1
|
-
|
1
|
+
require "fileutils"
|
2
|
+
require "rbconfig"
|
2
3
|
|
3
|
-
|
4
|
+
# == Synopsis
|
5
|
+
#
|
6
|
+
# Using minitar is easy. The simplest case is:
|
7
|
+
#
|
8
|
+
# require 'zlib'
|
9
|
+
# require 'minitar'
|
10
|
+
#
|
11
|
+
# # Packs everything that matches Find.find('tests').
|
12
|
+
# # test.tar will automatically be closed by Minitar.pack.
|
13
|
+
# Minitar.pack('tests', File.open('test.tar', 'wb'))
|
14
|
+
#
|
15
|
+
# # Unpacks 'test.tar' to 'x', creating 'x' if necessary.
|
16
|
+
# Minitar.unpack('test.tar', 'x')
|
17
|
+
#
|
18
|
+
# A gzipped tar can be written with:
|
19
|
+
#
|
20
|
+
# # test.tgz will be closed automatically.
|
21
|
+
# Minitar.pack('tests', Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))
|
22
|
+
#
|
23
|
+
# # test.tgz will be closed automatically.
|
24
|
+
# Minitar.unpack(Zlib::GzipReader.new(File.open('test.tgz', 'rb')), 'x')
|
25
|
+
#
|
26
|
+
# As the case above shows, one need not write to a file. However, it will
|
27
|
+
# sometimes require that one dive a little deeper into the API, as in the case
|
28
|
+
# of StringIO objects. Note that I'm not providing a block with
|
29
|
+
# Minitar::Output, as Minitar::Output#close automatically closes both the
|
30
|
+
# Output object and the wrapped data stream object.
|
31
|
+
#
|
32
|
+
# begin
|
33
|
+
# sgz = Zlib::GzipWriter.new(StringIO.new(""))
|
34
|
+
# tar = Output.new(sgz)
|
35
|
+
# Find.find('tests') do |entry|
|
36
|
+
# Minitar.pack_file(entry, tar)
|
37
|
+
# end
|
38
|
+
# ensure
|
39
|
+
# # Closes both tar and sgz.
|
40
|
+
# tar.close
|
41
|
+
# end
|
42
|
+
class Minitar
|
43
|
+
VERSION = "1.0.0".freeze # :nodoc:
|
4
44
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
45
|
+
# The base class for any minitar error.
|
46
|
+
Error = Class.new(::StandardError)
|
47
|
+
# Raised when a wrapped data stream class is not seekable.
|
48
|
+
NonSeekableStream = Class.new(Error)
|
49
|
+
# The exception raised when operations are performed on a stream that has
|
50
|
+
# previously been closed.
|
51
|
+
ClosedStream = Class.new(Error)
|
52
|
+
# The exception raised when a filename exceeds 256 bytes in length, the
|
53
|
+
# maximum supported by the standard Tar format.
|
54
|
+
FileNameTooLong = Class.new(Error)
|
55
|
+
# The exception raised when a data stream ends before the amount of data
|
56
|
+
# expected in the archive's PosixHeader.
|
57
|
+
UnexpectedEOF = Class.new(StandardError)
|
58
|
+
# The exception raised when a file contains a relative path in secure mode
|
59
|
+
# (the default for this version).
|
60
|
+
SecureRelativePathError = Class.new(Error)
|
61
|
+
# The exception raised when a file contains an invalid Posix header.
|
62
|
+
InvalidTarStream = Class.new(Error)
|
12
63
|
end
|
64
|
+
|
65
|
+
class << Minitar
|
66
|
+
# Tests if +path+ refers to a directory. Fixes an apparently
|
67
|
+
# corrupted <tt>stat()</tt> call on Windows.
|
68
|
+
def dir?(path)
|
69
|
+
File.directory?((path[-1] == "/") ? path : "#{path}/")
|
70
|
+
end
|
71
|
+
|
72
|
+
# A convenience method for wrapping Minitar::Input.open
|
73
|
+
# (mode +r+) and Minitar::Output.open (mode +w+). No other
|
74
|
+
# modes are currently supported.
|
75
|
+
def open(dest, mode = "r", &)
|
76
|
+
case mode
|
77
|
+
when "r"
|
78
|
+
Input.open(dest, &)
|
79
|
+
when "w"
|
80
|
+
Output.open(dest, &block)
|
81
|
+
else
|
82
|
+
raise "Unknown open mode for Minitar.open."
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def const_missing(c) # :nodoc:
|
87
|
+
case c
|
88
|
+
when :BlockRequired
|
89
|
+
warn "This constant has been removed."
|
90
|
+
const_set(:BlockRequired, Class.new(StandardError))
|
91
|
+
else
|
92
|
+
super
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def windows? # :nodoc:
|
97
|
+
RbConfig::CONFIG["host_os"] =~ /^(mswin|mingw|cygwin)/
|
98
|
+
end
|
99
|
+
|
100
|
+
# A convenience method to pack the provided +data+ as a file named +entry+. +entry+ may
|
101
|
+
# either be a name or a Hash with the fields described below. When only a name is
|
102
|
+
# provided, or only some Hash fields are provided, the default values will apply.
|
103
|
+
#
|
104
|
+
# <tt>:name</tt>:: The filename to be packed into the archive. Required.
|
105
|
+
# <tt>:mode</tt>:: The mode to be applied. Defaults to 0o644 for files and 0o755 for
|
106
|
+
# directories.
|
107
|
+
# <tt>:uid</tt>:: The user owner of the file. Default is +nil+.
|
108
|
+
# <tt>:gid</tt>:: The group owner of the file. Default is +nil+.
|
109
|
+
# <tt>:mtime</tt>:: The modification Time of the file. Default is +Time.now+.
|
110
|
+
#
|
111
|
+
# If +data+ is +nil+, a directory will be created. Use an empty String for a normal
|
112
|
+
# empty file.
|
113
|
+
def pack_as_file(entry, data, outputter) # :yields action, name, stats:
|
114
|
+
if outputter.is_a?(Minitar::Output)
|
115
|
+
outputter = outputter.tar
|
116
|
+
end
|
117
|
+
|
118
|
+
stats = {
|
119
|
+
gid: nil,
|
120
|
+
uid: nil,
|
121
|
+
mtime: Time.now,
|
122
|
+
size: data&.size || 0,
|
123
|
+
mode: data ? 0o644 : 0o755
|
124
|
+
}
|
125
|
+
|
126
|
+
if entry.is_a?(Hash)
|
127
|
+
name = entry.delete(:name)
|
128
|
+
entry.each_pair { stats[_1] = _2 unless _2.nil? }
|
129
|
+
else
|
130
|
+
name = entry
|
131
|
+
end
|
132
|
+
|
133
|
+
if data.nil? # Create a directory
|
134
|
+
yield :dir, name, stats if block_given?
|
135
|
+
outputter.mkdir(name, stats)
|
136
|
+
else
|
137
|
+
outputter.add_file_simple(name, stats) do |os|
|
138
|
+
stats[:current] = 0
|
139
|
+
yield :file_start, name, stats if block_given?
|
140
|
+
|
141
|
+
StringIO.open(data, "rb") do |ff|
|
142
|
+
until ff.eof?
|
143
|
+
stats[:currinc] = os.write(ff.read(4096))
|
144
|
+
stats[:current] += stats[:currinc]
|
145
|
+
yield :file_progress, name, stats if block_given?
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
yield :file_done, name, stats if block_given?
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# A convenience method to pack the file provided. +entry+ may either be a filename (in
|
155
|
+
# which case various values for the file (see below) will be obtained from
|
156
|
+
# <tt>File#stat(entry)</tt> or a Hash with the fields:
|
157
|
+
#
|
158
|
+
# <tt>:name</tt>:: The filename to be packed into the archive. Required.
|
159
|
+
# <tt>:mode</tt>:: The mode to be applied.
|
160
|
+
# <tt>:uid</tt>:: The user owner of the file. (Ignored on Windows.)
|
161
|
+
# <tt>:gid</tt>:: The group owner of the file. (Ignored on Windows.)
|
162
|
+
# <tt>:mtime</tt>:: The modification Time of the file.
|
163
|
+
#
|
164
|
+
# During packing, if a block is provided, #pack_file yields an +action+ Symol, the
|
165
|
+
# full name of the file being packed, and a Hash of statistical information, just as
|
166
|
+
# with Minitar::Input#extract_entry.
|
167
|
+
#
|
168
|
+
# The +action+ will be one of:
|
169
|
+
# <tt>:dir</tt>:: The +entry+ is a directory.
|
170
|
+
# <tt>:file_start</tt>:: The +entry+ is a file; the extract of the
|
171
|
+
# file is just beginning.
|
172
|
+
# <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
|
173
|
+
# of the +entry+.
|
174
|
+
# <tt>:file_done</tt>:: Yielded when the +entry+ is completed.
|
175
|
+
#
|
176
|
+
# The +stats+ hash contains the following keys:
|
177
|
+
# <tt>:current</tt>:: The current total number of bytes read in the
|
178
|
+
# +entry+.
|
179
|
+
# <tt>:currinc</tt>:: The current number of bytes read in this read
|
180
|
+
# cycle.
|
181
|
+
# <tt>:name</tt>:: The filename to be packed into the tarchive.
|
182
|
+
# *REQUIRED*.
|
183
|
+
# <tt>:mode</tt>:: The mode to be applied.
|
184
|
+
# <tt>:uid</tt>:: The user owner of the file. (+nil+ on Windows.)
|
185
|
+
# <tt>:gid</tt>:: The group owner of the file. (+nil+ on Windows.)
|
186
|
+
# <tt>:mtime</tt>:: The modification Time of the file.
|
187
|
+
def pack_file(entry, outputter) # :yields action, name, stats:
|
188
|
+
if outputter.is_a?(Minitar::Output)
|
189
|
+
outputter = outputter.tar
|
190
|
+
end
|
191
|
+
|
192
|
+
stats = {}
|
193
|
+
|
194
|
+
if entry.is_a?(Hash)
|
195
|
+
name = entry[:name]
|
196
|
+
entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
|
197
|
+
else
|
198
|
+
name = entry
|
199
|
+
end
|
200
|
+
|
201
|
+
name = name.sub(%r{\./}, "")
|
202
|
+
stat = File.stat(name)
|
203
|
+
stats[:mode] ||= stat.mode
|
204
|
+
stats[:mtime] ||= stat.mtime
|
205
|
+
stats[:size] = stat.size
|
206
|
+
|
207
|
+
if windows?
|
208
|
+
stats[:uid] = nil
|
209
|
+
stats[:gid] = nil
|
210
|
+
else
|
211
|
+
stats[:uid] ||= stat.uid
|
212
|
+
stats[:gid] ||= stat.gid
|
213
|
+
end
|
214
|
+
|
215
|
+
if File.file?(name)
|
216
|
+
outputter.add_file_simple(name, stats) do |os|
|
217
|
+
stats[:current] = 0
|
218
|
+
yield :file_start, name, stats if block_given?
|
219
|
+
File.open(name, "rb") do |ff|
|
220
|
+
until ff.eof?
|
221
|
+
stats[:currinc] = os.write(ff.read(4096))
|
222
|
+
stats[:current] += stats[:currinc]
|
223
|
+
yield :file_progress, name, stats if block_given?
|
224
|
+
end
|
225
|
+
end
|
226
|
+
yield :file_done, name, stats if block_given?
|
227
|
+
end
|
228
|
+
elsif dir?(name)
|
229
|
+
yield :dir, name, stats if block_given?
|
230
|
+
outputter.mkdir(name, stats)
|
231
|
+
else
|
232
|
+
raise "Don't yet know how to pack this type of file."
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# A convenience method to pack files specified by +src+ into +dest+. If
|
237
|
+
# +src+ is an Array, then each file detailed therein will be packed into
|
238
|
+
# the resulting Minitar::Output stream; if +recurse_dirs+ is
|
239
|
+
# true, then directories will be recursed.
|
240
|
+
#
|
241
|
+
# If +src+ is not an Array, it will be treated as the result of Find.find;
|
242
|
+
# all files matching will be packed.
|
243
|
+
def pack(src, dest, recurse_dirs = true, &block)
|
244
|
+
require "find"
|
245
|
+
Minitar::Output.open(dest) do |outp|
|
246
|
+
if src.is_a?(Array)
|
247
|
+
src.each do |entry|
|
248
|
+
if dir?(entry) && recurse_dirs
|
249
|
+
Find.find(entry) do |ee|
|
250
|
+
pack_file(ee, outp, &block)
|
251
|
+
end
|
252
|
+
else
|
253
|
+
pack_file(entry, outp, &block)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
else
|
257
|
+
Find.find(src) do |entry|
|
258
|
+
pack_file(entry, outp, &block)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# A convenience method to unpack files from +src+ into the directory
|
265
|
+
# specified by +dest+. Only those files named explicitly in +files+
|
266
|
+
# will be extracted.
|
267
|
+
def unpack(src, dest, files = [], options = {}, &block)
|
268
|
+
Minitar::Input.open(src) do |inp|
|
269
|
+
if File.exist?(dest) && !dir?(dest)
|
270
|
+
raise "Can't unpack to a non-directory."
|
271
|
+
end
|
272
|
+
|
273
|
+
FileUtils.mkdir_p(dest) unless File.exist?(dest)
|
274
|
+
|
275
|
+
inp.each do |entry|
|
276
|
+
if files.empty? || files.include?(entry.full_name)
|
277
|
+
inp.extract_entry(dest, entry, options, &block)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# Check whether +io+ can seek without errors.
|
284
|
+
def seekable?(io, methods = nil)
|
285
|
+
# The IO class throws an exception at runtime if we try to change
|
286
|
+
# position on a non-regular file.
|
287
|
+
if io.respond_to?(:stat)
|
288
|
+
io.stat.file?
|
289
|
+
else
|
290
|
+
# Duck-type the rest of this.
|
291
|
+
methods ||= [:pos, :pos=, :seek, :rewind]
|
292
|
+
methods = [methods] unless methods.is_a?(Array)
|
293
|
+
methods.all? { |m| io.respond_to?(m) }
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
require "minitar/posix_header"
|
299
|
+
require "minitar/input"
|
300
|
+
require "minitar/output"
|
data/test/minitest_helper.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module TarTestHelpers
|
4
|
-
include Archive::Tar::Minitar::ByteSize
|
5
|
-
|
6
2
|
Field = Struct.new(:name, :offset, :length)
|
7
3
|
def self.Field(name, length)
|
4
|
+
# standard:disable ThreadSafety/InstanceVariableInClassMethod
|
8
5
|
@offset ||= 0
|
9
6
|
field = Field.new(name, @offset, length)
|
10
7
|
@offset += length
|
8
|
+
# standard:enable ThreadSafety/InstanceVariableInClassMethod
|
9
|
+
|
11
10
|
FIELDS[name] = field
|
12
11
|
FIELD_ORDER << name
|
13
12
|
field
|
@@ -51,7 +50,7 @@ module TarTestHelpers
|
|
51
50
|
offset = FIELDS[field].offset
|
52
51
|
length = FIELDS[field].length
|
53
52
|
|
54
|
-
assert_equal
|
53
|
+
assert_equal expected[offset, length], actual[offset, length], message
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
@@ -95,8 +94,8 @@ module TarTestHelpers
|
|
95
94
|
]
|
96
95
|
|
97
96
|
h = arr.join.bytes.to_a.pack("C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155")
|
98
|
-
ret = h + "\0" * (512 - bytesize
|
99
|
-
assert_equal
|
97
|
+
ret = h + "\0" * (512 - h.bytesize)
|
98
|
+
assert_equal 512, ret.bytesize
|
100
99
|
ret
|
101
100
|
end
|
102
101
|
|
@@ -116,7 +115,7 @@ module TarTestHelpers
|
|
116
115
|
end
|
117
116
|
|
118
117
|
def asciiz(str, length)
|
119
|
-
str + "\0" * (length - bytesize
|
118
|
+
str + "\0" * (length - str.bytesize)
|
120
119
|
end
|
121
120
|
|
122
121
|
def sp(s)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "minitar"
|
4
|
+
require "minitest_helper"
|
5
|
+
require "base64"
|
6
|
+
require "zlib"
|
7
|
+
|
8
|
+
class TestIssue46 < Minitest::Test
|
9
|
+
SUPERLONG_TGZ = Base64.decode64(<<~EOS).freeze
|
10
|
+
H4sIAK1+smYAA+3WQQ6CMBAF0K49BScAprYd3XkALoECSiQlQYzXt0IkSKLGBdXE
|
11
|
+
/zbtNF000PkQRmG0SWq7T0p7FPOIHaNUNzrTkWI5zPt1IiYtgmSm8zw4n9q0CQLR
|
12
|
+
1HX7at/lkOeVjwP5FZNcKm14tU63uyyPUP91/e3rCJ75uF/j/Gej+6yXw/fArbnM
|
13
|
+
Z2ZlDKlb/ktNrEQQ+3gA9/xP3aS0z/e5bUXh40B+/Vj+oJ63Xkzff26zoqzmzf13
|
14
|
+
/d/98437n0izQf8DAAAAAAAAAAAAAAAAAHziCqQuXDYAKAAA
|
15
|
+
EOS
|
16
|
+
|
17
|
+
FILETIMES = Time.utc(2004).to_i
|
18
|
+
|
19
|
+
superlong_name = (["0123456789abcde"] * 33).join("/")
|
20
|
+
|
21
|
+
SUPERLONG_CONTENTS = {
|
22
|
+
superlong_name => {size: 496, mode: 0o644},
|
23
|
+
"endfile" => {size: 0, mode: 0o644}
|
24
|
+
}
|
25
|
+
|
26
|
+
def test_each_works
|
27
|
+
reader = Zlib::GzipReader.new(StringIO.new(SUPERLONG_TGZ))
|
28
|
+
|
29
|
+
Minitar::Input.open(reader) do |stream|
|
30
|
+
outer = 0
|
31
|
+
stream.each.with_index do |entry, i|
|
32
|
+
assert_kind_of Minitar::Reader::EntryStream, entry
|
33
|
+
assert SUPERLONG_CONTENTS.key?(entry.name), "File #{entry.name} not defined"
|
34
|
+
|
35
|
+
assert_equal SUPERLONG_CONTENTS[entry.name][:size],
|
36
|
+
entry.size,
|
37
|
+
"File sizes sizes do not match: #{entry.name}"
|
38
|
+
|
39
|
+
assert_modes_equal(SUPERLONG_CONTENTS[entry.name][:mode],
|
40
|
+
entry.mode, entry.name)
|
41
|
+
assert_equal FILETIMES, entry.mtime, "entry.mtime"
|
42
|
+
|
43
|
+
outer += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
assert_equal 2, outer
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "minitar"
|
4
|
+
require "minitest_helper"
|
5
|
+
require "zlib"
|
6
|
+
|
7
|
+
class TestMinitar < Minitest::Test
|
8
|
+
FILE_2004 = Time.utc(2004).to_i
|
9
|
+
|
10
|
+
def test_pack_as_file
|
11
|
+
input = [
|
12
|
+
["path", nil],
|
13
|
+
["test", "test"],
|
14
|
+
["extra/test", "extra/test"],
|
15
|
+
[{name: "empty2004", mtime: FILE_2004, mode: 0o755}, ""]
|
16
|
+
]
|
17
|
+
|
18
|
+
writer = StringIO.new
|
19
|
+
Minitar::Output.open(writer) do |out_stream|
|
20
|
+
input.each do |(name, data)|
|
21
|
+
Minitar.pack_as_file(name, data, out_stream)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
expected = [
|
26
|
+
{name: "path", size: 0, mode: 0o755},
|
27
|
+
{name: "test", size: 4, mode: 0o644, data: "test"},
|
28
|
+
{name: "extra/test", size: 10, mode: 0o0644, data: "extra/test"},
|
29
|
+
{name: "empty2004", size: 0, mode: 0o755, mtime: FILE_2004, nil: true}
|
30
|
+
]
|
31
|
+
|
32
|
+
count = 0
|
33
|
+
reader = StringIO.new(writer.string)
|
34
|
+
Minitar::Input.open(reader) do |stream|
|
35
|
+
stream.each.with_index do |entry, i|
|
36
|
+
assert_kind_of Minitar::Reader::EntryStream, entry
|
37
|
+
|
38
|
+
assert_equal expected[i][:name], entry.name
|
39
|
+
assert_equal expected[i][:size], entry.size
|
40
|
+
assert_equal expected[i][:mode], entry.mode
|
41
|
+
|
42
|
+
if expected[i].key?(:mtime)
|
43
|
+
assert_equal expected[i][:mtime], entry.mtime
|
44
|
+
end
|
45
|
+
|
46
|
+
if expected[i].key?(:data)
|
47
|
+
assert_equal expected[i][:data], entry.read
|
48
|
+
end
|
49
|
+
|
50
|
+
if expected[i].key?(:nil)
|
51
|
+
assert_nil entry.read
|
52
|
+
end
|
53
|
+
|
54
|
+
count += 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
assert_equal expected.size, count
|
59
|
+
end
|
60
|
+
end
|
data/test/test_tar_header.rb
CHANGED
@@ -1,81 +1,79 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require "minitest_helper"
|
4
2
|
|
5
3
|
class TestTarHeader < Minitest::Test
|
6
4
|
def test_arguments_are_checked
|
7
|
-
ph =
|
5
|
+
ph = Minitar::PosixHeader
|
8
6
|
assert_raises(ArgumentError) {
|
9
|
-
ph.new(:
|
7
|
+
ph.new(name: "", size: "", mode: "")
|
10
8
|
}
|
11
9
|
assert_raises(ArgumentError) {
|
12
|
-
ph.new(:
|
10
|
+
ph.new(name: "", size: "", prefix: "")
|
13
11
|
}
|
14
12
|
assert_raises(ArgumentError) {
|
15
|
-
ph.new(:
|
13
|
+
ph.new(name: "", prefix: "", mode: "")
|
16
14
|
}
|
17
15
|
assert_raises(ArgumentError) {
|
18
|
-
ph.new(:
|
16
|
+
ph.new(prefix: "", size: "", mode: "")
|
19
17
|
}
|
20
18
|
end
|
21
19
|
|
22
20
|
def test_basic_headers
|
23
21
|
header = {
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
22
|
+
name: "bla",
|
23
|
+
mode: 0o12345,
|
24
|
+
size: 10,
|
25
|
+
prefix: "",
|
26
|
+
typeflag: "0"
|
29
27
|
}
|
30
28
|
assert_headers_equal(tar_file_header("bla", "", 0o12345, 10),
|
31
|
-
|
29
|
+
Minitar::PosixHeader.new(header).to_s)
|
32
30
|
|
33
31
|
header = {
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
32
|
+
name: "bla",
|
33
|
+
mode: 0o12345,
|
34
|
+
size: 0,
|
35
|
+
prefix: "",
|
36
|
+
typeflag: "5"
|
39
37
|
}
|
40
38
|
assert_headers_equal(tar_dir_header("bla", "", 0o12345),
|
41
|
-
|
39
|
+
Minitar::PosixHeader.new(header).to_s)
|
42
40
|
end
|
43
41
|
|
44
42
|
def test_long_name_works
|
45
43
|
header = {
|
46
|
-
:
|
44
|
+
name: "a" * 100, mode: 0o12345, size: 10, prefix: ""
|
47
45
|
}
|
48
46
|
assert_headers_equal(tar_file_header("a" * 100, "", 0o12345, 10),
|
49
|
-
|
47
|
+
Minitar::PosixHeader.new(header).to_s)
|
50
48
|
header = {
|
51
|
-
:
|
49
|
+
name: "a" * 100, mode: 0o12345, size: 10, prefix: "bb" * 60
|
52
50
|
}
|
53
51
|
assert_headers_equal(tar_file_header("a" * 100, "bb" * 60, 0o12345, 10),
|
54
|
-
|
52
|
+
Minitar::PosixHeader.new(header).to_s)
|
55
53
|
end
|
56
54
|
|
57
55
|
def test_from_stream
|
58
56
|
header = tar_file_header("a" * 100, "", 0o12345, 10)
|
59
57
|
header = StringIO.new(header)
|
60
|
-
h =
|
61
|
-
assert_equal
|
62
|
-
assert_equal
|
63
|
-
assert_equal
|
64
|
-
assert_equal
|
65
|
-
assert_equal
|
58
|
+
h = Minitar::PosixHeader.from_stream(header)
|
59
|
+
assert_equal "a" * 100, h.name
|
60
|
+
assert_equal 0o12345, h.mode
|
61
|
+
assert_equal 10, h.size
|
62
|
+
assert_equal "", h.prefix
|
63
|
+
assert_equal "ustar", h.magic
|
66
64
|
end
|
67
65
|
|
68
66
|
def test_from_stream_with_evil_name
|
69
67
|
header = tar_file_header("a \0" + "\0" * 97, "", 0o12345, 10)
|
70
68
|
header = StringIO.new(header)
|
71
|
-
h =
|
72
|
-
assert_equal
|
69
|
+
h = Minitar::PosixHeader.from_stream header
|
70
|
+
assert_equal "a ", h.name
|
73
71
|
end
|
74
72
|
|
75
73
|
def test_valid_with_valid_header
|
76
74
|
header = tar_file_header("a" * 100, "", 0o12345, 10)
|
77
75
|
header = StringIO.new(header)
|
78
|
-
h =
|
76
|
+
h = Minitar::PosixHeader.from_stream header
|
79
77
|
|
80
78
|
assert(h.valid?)
|
81
79
|
end
|
@@ -85,7 +83,7 @@ class TestTarHeader < Minitest::Test
|
|
85
83
|
io = StringIO.new(header)
|
86
84
|
|
87
85
|
assert_raises(ArgumentError) do
|
88
|
-
|
86
|
+
Minitar::PosixHeader.from_stream(io)
|
89
87
|
end
|
90
88
|
end
|
91
89
|
|
@@ -98,14 +96,14 @@ class TestTarHeader < Minitest::Test
|
|
98
96
|
|
99
97
|
header = update_checksum(header)
|
100
98
|
io = StringIO.new(header)
|
101
|
-
header =
|
99
|
+
header = Minitar::PosixHeader.from_stream(io)
|
102
100
|
|
103
|
-
assert_equal
|
101
|
+
assert_equal 651, header.size
|
104
102
|
end
|
105
103
|
|
106
104
|
def test_valid_with_invalid_header
|
107
105
|
header = StringIO.new("testing")
|
108
|
-
h =
|
106
|
+
h = Minitar::PosixHeader.from_stream header
|
109
107
|
|
110
108
|
refute(h.valid?)
|
111
109
|
end
|