minitar 0.12.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|