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
@@ -1,9 +1,7 @@
|
|
1
|
-
|
1
|
+
require "minitar/writer"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
module Archive::Tar::Minitar
|
6
|
-
# Wraps a Archive::Tar::Minitar::Writer with convenience methods and wrapped
|
3
|
+
class Minitar
|
4
|
+
# Wraps a Minitar::Writer with convenience methods and wrapped
|
7
5
|
# stream management. If the stream provided to Output does not support random
|
8
6
|
# access, only Writer#add_file_simple and Writer#mkdir are guaranteed to
|
9
7
|
# work.
|
@@ -15,8 +13,8 @@ module Archive::Tar::Minitar
|
|
15
13
|
# instance, +Output.open+ returns the value of the block.
|
16
14
|
#
|
17
15
|
# call-seq:
|
18
|
-
#
|
19
|
-
#
|
16
|
+
# Minitar::Output.open(io) -> output
|
17
|
+
# Minitar::Output.open(io) { |output| block } -> obj
|
20
18
|
def self.open(output)
|
21
19
|
stream = new(output)
|
22
20
|
return stream unless block_given?
|
@@ -35,8 +33,8 @@ module Archive::Tar::Minitar
|
|
35
33
|
# will be created with the same behaviour.
|
36
34
|
#
|
37
35
|
# call-seq:
|
38
|
-
#
|
39
|
-
#
|
36
|
+
# Minitar::Output.tar(io) -> enumerator
|
37
|
+
# Minitar::Output.tar(io) { |tar| block } -> obj
|
40
38
|
def self.tar(output)
|
41
39
|
return to_enum(__method__, output) unless block_given?
|
42
40
|
|
@@ -51,15 +49,15 @@ module Archive::Tar::Minitar
|
|
51
49
|
# object wrapped will be closed.
|
52
50
|
#
|
53
51
|
# call-seq:
|
54
|
-
#
|
55
|
-
#
|
52
|
+
# Minitar::Output.new(io) -> output
|
53
|
+
# Minitar::Output.new(path) -> output
|
56
54
|
def initialize(output)
|
57
55
|
@io = if output.respond_to?(:write)
|
58
56
|
output
|
59
57
|
else
|
60
58
|
::Kernel.open(output, "wb")
|
61
59
|
end
|
62
|
-
@tar =
|
60
|
+
@tar = Minitar::Writer.new(@io)
|
63
61
|
end
|
64
62
|
|
65
63
|
# Returns the Writer object for direct access.
|
@@ -0,0 +1,267 @@
|
|
1
|
+
class Minitar
|
2
|
+
# Implements the POSIX tar header as a Ruby class. The structure of
|
3
|
+
# the POSIX tar header is:
|
4
|
+
#
|
5
|
+
# struct tarfile_entry_posix
|
6
|
+
# { // pack/unpack
|
7
|
+
# char name[100]; // ASCII (+ Z unless filled) a100/Z100
|
8
|
+
# char mode[8]; // 0 padded, octal, null a8 /A8
|
9
|
+
# char uid[8]; // 0 padded, octal, null a8 /A8
|
10
|
+
# char gid[8]; // 0 padded, octal, null a8 /A8
|
11
|
+
# char size[12]; // 0 padded, octal, null a12 /A12
|
12
|
+
# char mtime[12]; // 0 padded, octal, null a12 /A12
|
13
|
+
# char checksum[8]; // 0 padded, octal, null, space a8 /A8
|
14
|
+
# char typeflag[1]; // see below a /a
|
15
|
+
# char linkname[100]; // ASCII + (Z unless filled) a100/Z100
|
16
|
+
# char magic[6]; // "ustar\0" a6 /A6
|
17
|
+
# char version[2]; // "00" a2 /A2
|
18
|
+
# char uname[32]; // ASCIIZ a32 /Z32
|
19
|
+
# char gname[32]; // ASCIIZ a32 /Z32
|
20
|
+
# char devmajor[8]; // 0 padded, octal, null a8 /A8
|
21
|
+
# char devminor[8]; // 0 padded, octal, null a8 /A8
|
22
|
+
# char prefix[155]; // ASCII (+ Z unless filled) a155/Z155
|
23
|
+
# };
|
24
|
+
#
|
25
|
+
# The #typeflag is one of several known values.
|
26
|
+
#
|
27
|
+
# POSIX indicates that "A POSIX-compliant implementation must treat any
|
28
|
+
# unrecognized typeflag value as a regular file."
|
29
|
+
class PosixHeader
|
30
|
+
BLOCK_SIZE = 512
|
31
|
+
MAGIC_BYTES = "ustar".freeze
|
32
|
+
|
33
|
+
GNU_EXT_LONG_LINK = "././@LongLink"
|
34
|
+
|
35
|
+
# Fields that must be set in a POSIX tar(1) header.
|
36
|
+
REQUIRED_FIELDS = [:name, :size, :prefix, :mode].freeze
|
37
|
+
# Fields that may be set in a POSIX tar(1) header.
|
38
|
+
OPTIONAL_FIELDS = [
|
39
|
+
:uid, :gid, :mtime, :checksum, :typeflag, :linkname, :magic, :version,
|
40
|
+
:uname, :gname, :devmajor, :devminor
|
41
|
+
].freeze
|
42
|
+
|
43
|
+
# All fields available in a POSIX tar(1) header.
|
44
|
+
FIELDS = (REQUIRED_FIELDS + OPTIONAL_FIELDS).freeze
|
45
|
+
|
46
|
+
FIELDS.each do |f|
|
47
|
+
attr_reader f.to_sym unless f.to_sym == :name
|
48
|
+
end
|
49
|
+
|
50
|
+
# The name of the file. By default, limited to 100 bytes. Required. May be
|
51
|
+
# longer (up to BLOCK_SIZE bytes) if using the GNU long name tar extension.
|
52
|
+
attr_accessor :name
|
53
|
+
|
54
|
+
# The pack format passed to Array#pack for encoding a header.
|
55
|
+
HEADER_PACK_FORMAT = "a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155".freeze
|
56
|
+
# The unpack format passed to String#unpack for decoding a header.
|
57
|
+
HEADER_UNPACK_FORMAT = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155".freeze
|
58
|
+
|
59
|
+
class << self
|
60
|
+
# Creates a new PosixHeader from a data stream.
|
61
|
+
def from_stream(stream)
|
62
|
+
from_data(stream.read(BLOCK_SIZE))
|
63
|
+
end
|
64
|
+
|
65
|
+
# Creates a new PosixHeader from a data stream. Deprecated; use
|
66
|
+
# PosixHeader.from_stream instead.
|
67
|
+
def new_from_stream(stream)
|
68
|
+
warn "#{__method__} has been deprecated; use from_stream instead."
|
69
|
+
from_stream(stream)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates a new PosixHeader from a BLOCK_SIZE-byte data buffer.
|
73
|
+
def from_data(data)
|
74
|
+
fields = data.unpack(HEADER_UNPACK_FORMAT)
|
75
|
+
name = fields.shift
|
76
|
+
mode = fields.shift.oct
|
77
|
+
uid = fields.shift.oct
|
78
|
+
gid = fields.shift.oct
|
79
|
+
size = strict_oct(fields.shift)
|
80
|
+
mtime = fields.shift.oct
|
81
|
+
checksum = fields.shift.oct
|
82
|
+
typeflag = fields.shift
|
83
|
+
linkname = fields.shift
|
84
|
+
magic = fields.shift
|
85
|
+
version = fields.shift.oct
|
86
|
+
uname = fields.shift
|
87
|
+
gname = fields.shift
|
88
|
+
devmajor = fields.shift.oct
|
89
|
+
devminor = fields.shift.oct
|
90
|
+
prefix = fields.shift
|
91
|
+
|
92
|
+
empty = !data.each_byte.any?(&:nonzero?)
|
93
|
+
|
94
|
+
new(
|
95
|
+
name: name,
|
96
|
+
mode: mode,
|
97
|
+
uid: uid,
|
98
|
+
gid: gid,
|
99
|
+
size: size,
|
100
|
+
mtime: mtime,
|
101
|
+
checksum: checksum,
|
102
|
+
typeflag: typeflag,
|
103
|
+
magic: magic,
|
104
|
+
version: version,
|
105
|
+
uname: uname,
|
106
|
+
gname: gname,
|
107
|
+
devmajor: devmajor,
|
108
|
+
devminor: devminor,
|
109
|
+
prefix: prefix,
|
110
|
+
empty: empty,
|
111
|
+
linkname: linkname
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def strict_oct(string)
|
118
|
+
return string.oct if /\A[0-7 ]*\z/.match?(string)
|
119
|
+
raise ArgumentError, "#{string.inspect} is not a valid octal string"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Creates a new PosixHeader. A PosixHeader cannot be created unless
|
124
|
+
# +name+, +size+, +prefix+, and +mode+ are provided.
|
125
|
+
def initialize(v)
|
126
|
+
REQUIRED_FIELDS.each do |f|
|
127
|
+
raise ArgumentError, "Field #{f} is required." unless v.key?(f)
|
128
|
+
end
|
129
|
+
|
130
|
+
v[:mtime] = v[:mtime].to_i
|
131
|
+
v[:checksum] ||= ""
|
132
|
+
v[:typeflag] ||= "0"
|
133
|
+
v[:magic] ||= MAGIC_BYTES
|
134
|
+
v[:version] ||= "00"
|
135
|
+
|
136
|
+
FIELDS.each do |f|
|
137
|
+
instance_variable_set(:"@#{f}", v[f])
|
138
|
+
end
|
139
|
+
|
140
|
+
@empty = v[:empty]
|
141
|
+
end
|
142
|
+
|
143
|
+
# Indicates if the header was an empty header.
|
144
|
+
def empty?
|
145
|
+
@empty
|
146
|
+
end
|
147
|
+
|
148
|
+
# Indicates if the header has a valid magic value.
|
149
|
+
def valid?
|
150
|
+
empty? || @magic == MAGIC_BYTES
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns +true+ if the header is a long name special header which indicates
|
154
|
+
# that the next block of data is the filename.
|
155
|
+
def long_name?
|
156
|
+
typeflag == "L" && name == GNU_EXT_LONG_LINK
|
157
|
+
end
|
158
|
+
|
159
|
+
# A string representation of the header.
|
160
|
+
def to_s
|
161
|
+
update_checksum
|
162
|
+
header(@checksum)
|
163
|
+
end
|
164
|
+
alias_method :to_str, :to_s
|
165
|
+
|
166
|
+
# Update the checksum field.
|
167
|
+
def update_checksum
|
168
|
+
hh = header(" " * 8)
|
169
|
+
@checksum = oct(calculate_checksum(hh), 6)
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def oct(num, len)
|
175
|
+
if num.nil?
|
176
|
+
"\0" * (len + 1)
|
177
|
+
else
|
178
|
+
"%0#{len}o" % num
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def calculate_checksum(hdr)
|
183
|
+
hdr.unpack("C*").inject { |a, e| a + e }
|
184
|
+
end
|
185
|
+
|
186
|
+
def header(chksum)
|
187
|
+
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
|
188
|
+
oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
|
189
|
+
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
|
190
|
+
str = arr.pack(HEADER_PACK_FORMAT)
|
191
|
+
str + "\0" * ((BLOCK_SIZE - str.bytesize) % BLOCK_SIZE)
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# :attr_reader: size
|
196
|
+
# The size of the file. Required.
|
197
|
+
|
198
|
+
##
|
199
|
+
# :attr_reader: prefix
|
200
|
+
# The prefix of the file; the path before #name. Limited to 155 bytes.
|
201
|
+
# Required.
|
202
|
+
|
203
|
+
##
|
204
|
+
# :attr_reader: mode
|
205
|
+
# The Unix file mode of the file. Stored as an octal integer. Required.
|
206
|
+
|
207
|
+
##
|
208
|
+
# :attr_reader: uid
|
209
|
+
# The Unix owner user ID of the file. Stored as an octal integer.
|
210
|
+
|
211
|
+
##
|
212
|
+
# :attr_reader: uname
|
213
|
+
# The user name of the Unix owner of the file.
|
214
|
+
|
215
|
+
##
|
216
|
+
# :attr_reader: gid
|
217
|
+
# The Unix owner group ID of the file. Stored as an octal integer.
|
218
|
+
|
219
|
+
##
|
220
|
+
# :attr_reader: gname
|
221
|
+
# The group name of the Unix owner of the file.
|
222
|
+
|
223
|
+
##
|
224
|
+
# :attr_reader: mtime
|
225
|
+
# The modification time of the file in epoch seconds. Stored as an
|
226
|
+
# octal integer.
|
227
|
+
|
228
|
+
##
|
229
|
+
# :attr_reader: checksum
|
230
|
+
# The checksum of the file. Stored as an octal integer. Calculated
|
231
|
+
# before encoding the header as a string.
|
232
|
+
|
233
|
+
##
|
234
|
+
# :attr_reader: typeflag
|
235
|
+
# The type of record in the file.
|
236
|
+
#
|
237
|
+
# +0+:: Regular file. NULL should be treated as a synonym, for compatibility
|
238
|
+
# purposes.
|
239
|
+
# +1+:: Hard link.
|
240
|
+
# +2+:: Symbolic link.
|
241
|
+
# +3+:: Character device node.
|
242
|
+
# +4+:: Block device node.
|
243
|
+
# +5+:: Directory.
|
244
|
+
# +6+:: FIFO node.
|
245
|
+
# +7+:: Reserved.
|
246
|
+
|
247
|
+
##
|
248
|
+
# :attr_reader: linkname
|
249
|
+
# The name of the link stored. Not currently used.
|
250
|
+
|
251
|
+
##
|
252
|
+
# :attr_reader: magic
|
253
|
+
# Always "ustar\0".
|
254
|
+
|
255
|
+
##
|
256
|
+
# :attr_reader: version
|
257
|
+
# Always "00"
|
258
|
+
|
259
|
+
##
|
260
|
+
# :attr_reader: devmajor
|
261
|
+
# The major device ID. Not currently used.
|
262
|
+
|
263
|
+
##
|
264
|
+
# :attr_reader: devminor
|
265
|
+
# The minor device ID. Not currently used.
|
266
|
+
end
|
267
|
+
end
|
@@ -1,12 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Archive::Tar::Minitar
|
1
|
+
class Minitar
|
4
2
|
# The class that reads a tar format archive from a data stream. The data
|
5
3
|
# stream may be sequential or random access, but certain features only work
|
6
4
|
# with random access data streams.
|
7
5
|
class Reader
|
8
6
|
include Enumerable
|
9
|
-
include Archive::Tar::Minitar::ByteSize
|
10
7
|
|
11
8
|
# This marks the EntryStream closed for reading without closing the
|
12
9
|
# actual data stream.
|
@@ -30,9 +27,7 @@ module Archive::Tar::Minitar
|
|
30
27
|
|
31
28
|
# EntryStreams are pseudo-streams on top of the main data stream.
|
32
29
|
class EntryStream
|
33
|
-
|
34
|
-
|
35
|
-
Archive::Tar::Minitar::PosixHeader::FIELDS.each do |field|
|
30
|
+
Minitar::PosixHeader::FIELDS.each do |field|
|
36
31
|
attr_reader field.to_sym
|
37
32
|
end
|
38
33
|
|
@@ -56,7 +51,7 @@ module Archive::Tar::Minitar
|
|
56
51
|
@prefix = header.prefix
|
57
52
|
@read = 0
|
58
53
|
@orig_pos =
|
59
|
-
if
|
54
|
+
if Minitar.seekable?(@io)
|
60
55
|
@io.pos
|
61
56
|
else
|
62
57
|
0
|
@@ -70,7 +65,7 @@ module Archive::Tar::Minitar
|
|
70
65
|
len ||= @size - @read
|
71
66
|
max_read = [len, @size - @read].min
|
72
67
|
ret = @io.read(max_read)
|
73
|
-
@read += bytesize
|
68
|
+
@read += ret.bytesize
|
74
69
|
ret
|
75
70
|
end
|
76
71
|
|
@@ -119,8 +114,8 @@ module Archive::Tar::Minitar
|
|
119
114
|
|
120
115
|
# Sets the current read pointer to the beginning of the EntryStream.
|
121
116
|
def rewind
|
122
|
-
unless
|
123
|
-
raise
|
117
|
+
unless Minitar.seekable?(@io, :pos=)
|
118
|
+
raise Minitar::NonSeekableStream
|
124
119
|
end
|
125
120
|
@io.pos = @orig_pos
|
126
121
|
@read = 0
|
@@ -177,7 +172,7 @@ module Archive::Tar::Minitar
|
|
177
172
|
# Iterates over each entry in the provided input. This wraps the common
|
178
173
|
# pattern of:
|
179
174
|
#
|
180
|
-
#
|
175
|
+
# Minitar::Input.open(io) do |i|
|
181
176
|
# inp.each do |entry|
|
182
177
|
# # ...
|
183
178
|
# end
|
@@ -187,8 +182,8 @@ module Archive::Tar::Minitar
|
|
187
182
|
# behaviour.
|
188
183
|
#
|
189
184
|
# call-seq:
|
190
|
-
#
|
191
|
-
#
|
185
|
+
# Minitar::Reader.each_entry(io) -> enumerator
|
186
|
+
# Minitar::Reader.each_entry(io) { |entry| block } -> obj
|
192
187
|
def self.each_entry(io)
|
193
188
|
return to_enum(__method__, io) unless block_given?
|
194
189
|
|
@@ -214,13 +209,13 @@ module Archive::Tar::Minitar
|
|
214
209
|
# random access data streams that respond to #rewind and #pos.
|
215
210
|
def rewind
|
216
211
|
if @init_pos.zero?
|
217
|
-
unless
|
218
|
-
raise
|
212
|
+
unless Minitar.seekable?(@io, :rewind)
|
213
|
+
raise Minitar::NonSeekableStream
|
219
214
|
end
|
220
215
|
@io.rewind
|
221
216
|
else
|
222
|
-
unless
|
223
|
-
raise
|
217
|
+
unless Minitar.seekable?(@io, :pos=)
|
218
|
+
raise Minitar::NonSeekableStream
|
224
219
|
end
|
225
220
|
@io.pos = @init_pos
|
226
221
|
end
|
@@ -233,11 +228,11 @@ module Archive::Tar::Minitar
|
|
233
228
|
loop do
|
234
229
|
return if @io.eof?
|
235
230
|
|
236
|
-
header =
|
237
|
-
raise
|
231
|
+
header = Minitar::PosixHeader.from_stream(@io)
|
232
|
+
raise Minitar::InvalidTarStream unless header.valid?
|
238
233
|
return if header.empty?
|
239
234
|
|
240
|
-
raise
|
235
|
+
raise Minitar::InvalidTarStream if header.size < 0
|
241
236
|
|
242
237
|
if header.long_name?
|
243
238
|
name_block = (header.size / 512.0).ceil * 512
|
@@ -256,13 +251,13 @@ module Archive::Tar::Minitar
|
|
256
251
|
|
257
252
|
skip = (512 - (size % 512)) % 512
|
258
253
|
|
259
|
-
if
|
254
|
+
if Minitar.seekable?(@io, :seek)
|
260
255
|
# avoid reading...
|
261
256
|
@io.seek(size - entry.bytes_read, IO::SEEK_CUR)
|
262
257
|
else
|
263
258
|
pending = size - entry.bytes_read
|
264
259
|
while pending > 0
|
265
|
-
bread =
|
260
|
+
bread = @io.read([pending, 4096].min).bytesize
|
266
261
|
raise UnexpectedEOF if @io.eof?
|
267
262
|
pending -= bread
|
268
263
|
end
|
@@ -1,10 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Archive::Tar::Minitar
|
1
|
+
class Minitar
|
4
2
|
# The class that writes a tar format archive to a data stream.
|
5
3
|
class Writer
|
6
|
-
include Archive::Tar::Minitar::ByteSize
|
7
|
-
|
8
4
|
# The exception raised when the user attempts to write more data to a
|
9
5
|
# BoundedWriteStream than has been allocated.
|
10
6
|
WriteBoundaryOverflow = Class.new(StandardError)
|
@@ -25,15 +21,13 @@ module Archive::Tar::Minitar
|
|
25
21
|
|
26
22
|
# A WriteOnlyStream that also has a size limit.
|
27
23
|
class BoundedWriteStream < WriteOnlyStream
|
28
|
-
include Archive::Tar::Minitar::ByteSize
|
29
|
-
|
30
24
|
def self.const_missing(c)
|
31
25
|
case c
|
32
26
|
when :FileOverflow
|
33
27
|
warn "Writer::BoundedWriteStream::FileOverflow has been renamed " \
|
34
28
|
"to Writer::WriteBoundaryOverflow"
|
35
29
|
const_set :FileOverflow,
|
36
|
-
|
30
|
+
Minitar::Writer::WriteBoundaryOverflow
|
37
31
|
else
|
38
32
|
super
|
39
33
|
end
|
@@ -52,7 +46,7 @@ module Archive::Tar::Minitar
|
|
52
46
|
end
|
53
47
|
|
54
48
|
def write(data)
|
55
|
-
size = bytesize
|
49
|
+
size = data.bytesize
|
56
50
|
raise WriteBoundaryOverflow if (size + @written) > @limit
|
57
51
|
@io.write(data)
|
58
52
|
@written += size
|
@@ -79,11 +73,11 @@ module Archive::Tar::Minitar
|
|
79
73
|
# of the block.
|
80
74
|
#
|
81
75
|
# call-seq:
|
82
|
-
# w =
|
76
|
+
# w = Minitar::Writer.open(STDOUT)
|
83
77
|
# w.add_file_simple('foo.txt', :size => 3)
|
84
78
|
# w.close
|
85
79
|
#
|
86
|
-
#
|
80
|
+
# Minitar::Writer.open(STDOUT) do |w|
|
87
81
|
# w.add_file_simple('foo.txt', :size => 3)
|
88
82
|
# end
|
89
83
|
def self.open(io) # :yields Writer:
|
@@ -139,10 +133,10 @@ module Archive::Tar::Minitar
|
|
139
133
|
raise ClosedStream if @closed
|
140
134
|
|
141
135
|
header = {
|
142
|
-
:
|
143
|
-
:
|
144
|
-
:
|
145
|
-
:
|
136
|
+
mode: opts.fetch(:mode, 0o644),
|
137
|
+
mtime: opts.fetch(:mtime, nil),
|
138
|
+
gid: opts.fetch(:gid, nil),
|
139
|
+
uid: opts.fetch(:uid, nil)
|
146
140
|
}
|
147
141
|
|
148
142
|
data = opts.fetch(:data, nil)
|
@@ -158,7 +152,7 @@ module Archive::Tar::Minitar
|
|
158
152
|
else
|
159
153
|
raise ArgumentError, "No data provided" unless data
|
160
154
|
|
161
|
-
bytes = bytesize
|
155
|
+
bytes = data.bytesize
|
162
156
|
size = bytes if size.nil? || size < bytes
|
163
157
|
end
|
164
158
|
|
@@ -203,13 +197,13 @@ module Archive::Tar::Minitar
|
|
203
197
|
# #add_file_simple must be used.
|
204
198
|
#
|
205
199
|
# +opts+ may be modified during the writing of the file to the stream.
|
206
|
-
def add_file(name, opts = {}, &
|
200
|
+
def add_file(name, opts = {}, &) # :yields WriteOnlyStream, +opts+:
|
207
201
|
raise ClosedStream if @closed
|
208
202
|
|
209
|
-
return add_file_simple(name, opts, &
|
203
|
+
return add_file_simple(name, opts, &) if opts[:data]
|
210
204
|
|
211
|
-
unless
|
212
|
-
raise
|
205
|
+
unless Minitar.seekable?(@io)
|
206
|
+
raise Minitar::NonSeekableStream
|
213
207
|
end
|
214
208
|
|
215
209
|
short_name, prefix, needs_long_name = split_name(name)
|
@@ -227,11 +221,11 @@ module Archive::Tar::Minitar
|
|
227
221
|
final_pos, @io.pos = @io.pos, init_pos
|
228
222
|
|
229
223
|
header = {
|
230
|
-
:
|
231
|
-
:
|
232
|
-
:
|
233
|
-
:
|
234
|
-
:
|
224
|
+
mode: opts[:mode],
|
225
|
+
mtime: opts[:mtime],
|
226
|
+
size: size,
|
227
|
+
gid: opts[:gid],
|
228
|
+
uid: opts[:uid]
|
235
229
|
}
|
236
230
|
|
237
231
|
write_header(header, name, short_name, prefix, needs_long_name)
|
@@ -244,12 +238,12 @@ module Archive::Tar::Minitar
|
|
244
238
|
raise ClosedStream if @closed
|
245
239
|
|
246
240
|
header = {
|
247
|
-
:
|
248
|
-
:
|
249
|
-
:
|
250
|
-
:
|
251
|
-
:
|
252
|
-
:
|
241
|
+
mode: opts[:mode],
|
242
|
+
typeflag: "5",
|
243
|
+
size: 0,
|
244
|
+
gid: opts[:gid],
|
245
|
+
uid: opts[:uid],
|
246
|
+
mtime: opts[:mtime]
|
253
247
|
}
|
254
248
|
|
255
249
|
short_name, prefix, needs_long_name = split_name(name)
|
@@ -266,15 +260,15 @@ module Archive::Tar::Minitar
|
|
266
260
|
|
267
261
|
name, prefix = split_name(name)
|
268
262
|
header = {
|
269
|
-
:
|
270
|
-
:
|
271
|
-
:
|
272
|
-
:
|
273
|
-
:
|
274
|
-
:
|
275
|
-
:
|
276
|
-
:
|
277
|
-
:
|
263
|
+
name: name,
|
264
|
+
mode: opts[:mode],
|
265
|
+
typeflag: "2",
|
266
|
+
size: 0,
|
267
|
+
linkname: link_target,
|
268
|
+
gid: opts[:gid],
|
269
|
+
uid: opts[:uid],
|
270
|
+
mtime: opts[:mtime],
|
271
|
+
prefix: prefix
|
278
272
|
}
|
279
273
|
@io.write(PosixHeader.new(header))
|
280
274
|
nil
|
@@ -305,23 +299,23 @@ module Archive::Tar::Minitar
|
|
305
299
|
def write_header(header, long_name, short_name, prefix, needs_long_name)
|
306
300
|
if needs_long_name
|
307
301
|
long_name_header = {
|
308
|
-
:
|
309
|
-
:
|
310
|
-
:
|
311
|
-
:
|
312
|
-
:
|
302
|
+
prefix: "",
|
303
|
+
name: PosixHeader::GNU_EXT_LONG_LINK,
|
304
|
+
typeflag: "L",
|
305
|
+
size: long_name.length + 1,
|
306
|
+
mode: 0
|
313
307
|
}
|
314
308
|
@io.write(PosixHeader.new(long_name_header))
|
315
309
|
@io.write(long_name)
|
316
310
|
@io.write("\0" * (512 - (long_name.length % 512)))
|
317
311
|
end
|
318
312
|
|
319
|
-
new_header = header.merge({:
|
313
|
+
new_header = header.merge({name: short_name, prefix: prefix})
|
320
314
|
@io.write(PosixHeader.new(new_header))
|
321
315
|
end
|
322
316
|
|
323
317
|
def split_name(name)
|
324
|
-
if bytesize
|
318
|
+
if name.bytesize <= 100
|
325
319
|
prefix = ""
|
326
320
|
else
|
327
321
|
parts = name.split("/")
|
@@ -331,7 +325,7 @@ module Archive::Tar::Minitar
|
|
331
325
|
|
332
326
|
loop do
|
333
327
|
nxt = parts.pop || ""
|
334
|
-
break if bytesize
|
328
|
+
break if newname.bytesize + 1 + nxt.bytesize >= 100
|
335
329
|
newname = "#{nxt}/#{newname}"
|
336
330
|
end
|
337
331
|
|
@@ -340,7 +334,7 @@ module Archive::Tar::Minitar
|
|
340
334
|
name = newname
|
341
335
|
end
|
342
336
|
|
343
|
-
[name, prefix, bytesize
|
337
|
+
[name, prefix, name.bytesize > 100 || prefix.bytesize > 155]
|
344
338
|
end
|
345
339
|
end
|
346
340
|
end
|