minitar 0.9 → 0.12
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/Contributing.md +48 -47
- data/History.md +158 -127
- data/Manifest.txt +1 -0
- data/README.rdoc +1 -6
- data/Rakefile +53 -33
- data/lib/archive/tar/minitar/input.rb +18 -18
- data/lib/archive/tar/minitar/output.rb +6 -6
- data/lib/archive/tar/minitar/posix_header.rb +36 -34
- data/lib/archive/tar/minitar/reader.rb +47 -33
- data/lib/archive/tar/minitar/writer.rb +64 -35
- data/lib/archive/tar/minitar.rb +45 -44
- data/lib/archive-tar-minitar.rb +1 -1
- data/lib/minitar.rb +1 -1
- data/support/hoe/deprecated_gem.rb +64 -0
- data/test/minitest_helper.rb +7 -6
- data/test/support/tar_test_helpers.rb +45 -41
- data/test/test_tar_header.rb +30 -31
- data/test/test_tar_input.rb +31 -31
- data/test/test_tar_output.rb +12 -12
- data/test/test_tar_reader.rb +23 -23
- data/test/test_tar_writer.rb +119 -69
- metadata +70 -31
@@ -1,6 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
require
|
3
|
+
require "archive/tar/minitar/reader"
|
4
4
|
|
5
5
|
module Archive::Tar::Minitar
|
6
6
|
# Wraps a Archive::Tar::Minitar::Reader with convenience methods and wrapped
|
@@ -51,7 +51,7 @@ module Archive::Tar::Minitar
|
|
51
51
|
def self.each_entry(input)
|
52
52
|
return to_enum(__method__, input) unless block_given?
|
53
53
|
|
54
|
-
open(input) do |stream|
|
54
|
+
Input.open(input) do |stream|
|
55
55
|
stream.each do |entry|
|
56
56
|
yield entry
|
57
57
|
end
|
@@ -71,10 +71,10 @@ module Archive::Tar::Minitar
|
|
71
71
|
# Archive::Tar::Minitar::Input.new(path) -> input
|
72
72
|
def initialize(input)
|
73
73
|
@io = if input.respond_to?(:read)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
input
|
75
|
+
else
|
76
|
+
::Kernel.open(input, "rb")
|
77
|
+
end
|
78
78
|
|
79
79
|
unless Archive::Tar::Minitar.seekable?(@io, :rewind)
|
80
80
|
raise Archive::Tar::Minitar::NonSeekableStream
|
@@ -96,7 +96,7 @@ module Archive::Tar::Minitar
|
|
96
96
|
ensure
|
97
97
|
@tar.rewind
|
98
98
|
end
|
99
|
-
|
99
|
+
alias_method :each, :each_entry
|
100
100
|
|
101
101
|
# Extracts the current +entry+ to +destdir+. If a block is provided, it
|
102
102
|
# yields an +action+ Symbol, the full name of the file being extracted
|
@@ -119,9 +119,9 @@ module Archive::Tar::Minitar
|
|
119
119
|
# Reader::EntryStream, with all methods thereof.
|
120
120
|
def extract_entry(destdir, entry, options = {}, &block) # :yields action, name, stats:
|
121
121
|
stats = {
|
122
|
-
:current
|
123
|
-
:currinc
|
124
|
-
:entry
|
122
|
+
:current => 0,
|
123
|
+
:currinc => 0,
|
124
|
+
:entry => entry
|
125
125
|
}
|
126
126
|
|
127
127
|
# extract_entry is not vulnerable to prefix '/' vulnerabilities, but it
|
@@ -133,10 +133,10 @@ module Archive::Tar::Minitar
|
|
133
133
|
# leave +destdir+.
|
134
134
|
#
|
135
135
|
# However, squeeze consecutive '/' characters together.
|
136
|
-
full_name = entry.full_name.squeeze(
|
136
|
+
full_name = entry.full_name.squeeze("/")
|
137
137
|
|
138
|
-
if
|
139
|
-
raise SecureRelativePathError,
|
138
|
+
if /\.{2}(?:\/|\z)/.match?(full_name)
|
139
|
+
raise SecureRelativePathError, "Path contains '..'"
|
140
140
|
end
|
141
141
|
|
142
142
|
if entry.directory?
|
@@ -164,7 +164,7 @@ module Archive::Tar::Minitar
|
|
164
164
|
|
165
165
|
def fsync_dir(dirname)
|
166
166
|
# make sure this hits the disc
|
167
|
-
dir = open(dirname,
|
167
|
+
dir = IO.open(dirname, "rb")
|
168
168
|
dir.fsync
|
169
169
|
rescue # ignore IOError if it's an unpatched (old) Ruby
|
170
170
|
nil
|
@@ -184,7 +184,7 @@ module Archive::Tar::Minitar
|
|
184
184
|
nil
|
185
185
|
end
|
186
186
|
else
|
187
|
-
File.unlink(dest.chomp(
|
187
|
+
File.unlink(dest.chomp("/")) if File.symlink?(dest.chomp("/"))
|
188
188
|
|
189
189
|
FileUtils.mkdir_p(dest, :mode => entry.mode)
|
190
190
|
FileUtils.chmod(entry.mode, dest)
|
@@ -192,7 +192,7 @@ module Archive::Tar::Minitar
|
|
192
192
|
|
193
193
|
if options.fetch(:fsync, true)
|
194
194
|
fsync_dir(dest)
|
195
|
-
fsync_dir(File.join(dest,
|
195
|
+
fsync_dir(File.join(dest, ".."))
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
@@ -211,7 +211,7 @@ module Archive::Tar::Minitar
|
|
211
211
|
|
212
212
|
yield :file_start, full_name, stats if block_given?
|
213
213
|
|
214
|
-
File.open(destfile,
|
214
|
+
File.open(destfile, "wb", entry.mode) do |os|
|
215
215
|
loop do
|
216
216
|
data = entry.read(4096)
|
217
217
|
break unless data
|
@@ -234,7 +234,7 @@ module Archive::Tar::Minitar
|
|
234
234
|
yield :dir_fsync, full_name, stats if block_given?
|
235
235
|
|
236
236
|
fsync_dir(File.dirname(destfile))
|
237
|
-
fsync_dir(File.join(File.dirname(destfile),
|
237
|
+
fsync_dir(File.join(File.dirname(destfile), ".."))
|
238
238
|
end
|
239
239
|
|
240
240
|
yield :file_done, full_name, stats if block_given?
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
require
|
3
|
+
require "archive/tar/minitar/writer"
|
4
4
|
|
5
5
|
module Archive::Tar::Minitar
|
6
6
|
# Wraps a Archive::Tar::Minitar::Writer with convenience methods and wrapped
|
@@ -40,7 +40,7 @@ module Archive::Tar::Minitar
|
|
40
40
|
def self.tar(output)
|
41
41
|
return to_enum(__method__, output) unless block_given?
|
42
42
|
|
43
|
-
open(output) do |stream|
|
43
|
+
Output.open(output) do |stream|
|
44
44
|
yield stream.tar
|
45
45
|
end
|
46
46
|
end
|
@@ -55,10 +55,10 @@ module Archive::Tar::Minitar
|
|
55
55
|
# Archive::Tar::Minitar::Output.new(path) -> output
|
56
56
|
def initialize(output)
|
57
57
|
@io = if output.respond_to?(:write)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
output
|
59
|
+
else
|
60
|
+
::Kernel.open(output, "wb")
|
61
|
+
end
|
62
62
|
@tar = Archive::Tar::Minitar::Writer.new(@io)
|
63
63
|
end
|
64
64
|
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
##
|
4
4
|
module Archive; end
|
5
|
+
|
5
6
|
##
|
6
7
|
module Archive::Tar; end
|
8
|
+
|
7
9
|
##
|
8
10
|
module Archive::Tar::Minitar; end
|
9
11
|
|
@@ -36,12 +38,12 @@ module Archive::Tar::Minitar; end
|
|
36
38
|
# unrecognized typeflag value as a regular file."
|
37
39
|
class Archive::Tar::Minitar::PosixHeader
|
38
40
|
BLOCK_SIZE = 512
|
39
|
-
MAGIC_BYTES =
|
41
|
+
MAGIC_BYTES = "ustar".freeze
|
40
42
|
|
41
|
-
GNU_EXT_LONG_LINK =
|
43
|
+
GNU_EXT_LONG_LINK = "././@LongLink"
|
42
44
|
|
43
45
|
# Fields that must be set in a POSIX tar(1) header.
|
44
|
-
REQUIRED_FIELDS = [
|
46
|
+
REQUIRED_FIELDS = [:name, :size, :prefix, :mode].freeze
|
45
47
|
# Fields that may be set in a POSIX tar(1) header.
|
46
48
|
OPTIONAL_FIELDS = [
|
47
49
|
:uid, :gid, :mtime, :checksum, :typeflag, :linkname, :magic, :version,
|
@@ -60,9 +62,9 @@ class Archive::Tar::Minitar::PosixHeader
|
|
60
62
|
attr_accessor :name
|
61
63
|
|
62
64
|
# The pack format passed to Array#pack for encoding a header.
|
63
|
-
HEADER_PACK_FORMAT
|
65
|
+
HEADER_PACK_FORMAT = "a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155".freeze
|
64
66
|
# The unpack format passed to String#unpack for decoding a header.
|
65
|
-
HEADER_UNPACK_FORMAT
|
67
|
+
HEADER_UNPACK_FORMAT = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155".freeze
|
66
68
|
|
67
69
|
class << self
|
68
70
|
# Creates a new PosixHeader from a data stream.
|
@@ -79,23 +81,23 @@ class Archive::Tar::Minitar::PosixHeader
|
|
79
81
|
|
80
82
|
# Creates a new PosixHeader from a BLOCK_SIZE-byte data buffer.
|
81
83
|
def from_data(data)
|
82
|
-
fields
|
83
|
-
name
|
84
|
-
mode
|
85
|
-
uid
|
86
|
-
gid
|
87
|
-
size
|
88
|
-
mtime
|
89
|
-
checksum
|
90
|
-
typeflag
|
91
|
-
linkname
|
92
|
-
magic
|
93
|
-
version
|
94
|
-
uname
|
95
|
-
gname
|
96
|
-
devmajor
|
97
|
-
devminor
|
98
|
-
prefix
|
84
|
+
fields = data.unpack(HEADER_UNPACK_FORMAT)
|
85
|
+
name = fields.shift
|
86
|
+
mode = fields.shift.oct
|
87
|
+
uid = fields.shift.oct
|
88
|
+
gid = fields.shift.oct
|
89
|
+
size = strict_oct(fields.shift)
|
90
|
+
mtime = fields.shift.oct
|
91
|
+
checksum = fields.shift.oct
|
92
|
+
typeflag = fields.shift
|
93
|
+
linkname = fields.shift
|
94
|
+
magic = fields.shift
|
95
|
+
version = fields.shift.oct
|
96
|
+
uname = fields.shift
|
97
|
+
gname = fields.shift
|
98
|
+
devmajor = fields.shift.oct
|
99
|
+
devminor = fields.shift.oct
|
100
|
+
prefix = fields.shift
|
99
101
|
|
100
102
|
empty = !data.each_byte.any?(&:nonzero?)
|
101
103
|
|
@@ -123,7 +125,7 @@ class Archive::Tar::Minitar::PosixHeader
|
|
123
125
|
private
|
124
126
|
|
125
127
|
def strict_oct(string)
|
126
|
-
return string.oct if
|
128
|
+
return string.oct if /\A[0-7 ]*\z/.match?(string)
|
127
129
|
raise ArgumentError, "#{string.inspect} is not a valid octal string"
|
128
130
|
end
|
129
131
|
end
|
@@ -136,13 +138,13 @@ class Archive::Tar::Minitar::PosixHeader
|
|
136
138
|
end
|
137
139
|
|
138
140
|
v[:mtime] = v[:mtime].to_i
|
139
|
-
v[:checksum] ||=
|
140
|
-
v[:typeflag] ||=
|
141
|
-
v[:magic]
|
142
|
-
v[:version]
|
141
|
+
v[:checksum] ||= ""
|
142
|
+
v[:typeflag] ||= "0"
|
143
|
+
v[:magic] ||= MAGIC_BYTES
|
144
|
+
v[:version] ||= "00"
|
143
145
|
|
144
146
|
FIELDS.each do |f|
|
145
|
-
instance_variable_set("@#{f}", v[f])
|
147
|
+
instance_variable_set(:"@#{f}", v[f])
|
146
148
|
end
|
147
149
|
|
148
150
|
@empty = v[:empty]
|
@@ -161,7 +163,7 @@ class Archive::Tar::Minitar::PosixHeader
|
|
161
163
|
# Returns +true+ if the header is a long name special header which indicates
|
162
164
|
# that the next block of data is the filename.
|
163
165
|
def long_name?
|
164
|
-
typeflag ==
|
166
|
+
typeflag == "L" && name == GNU_EXT_LONG_LINK
|
165
167
|
end
|
166
168
|
|
167
169
|
# A string representation of the header.
|
@@ -169,11 +171,11 @@ class Archive::Tar::Minitar::PosixHeader
|
|
169
171
|
update_checksum
|
170
172
|
header(@checksum)
|
171
173
|
end
|
172
|
-
|
174
|
+
alias_method :to_str, :to_s
|
173
175
|
|
174
176
|
# Update the checksum field.
|
175
177
|
def update_checksum
|
176
|
-
hh = header(
|
178
|
+
hh = header(" " * 8)
|
177
179
|
@checksum = oct(calculate_checksum(hh), 6)
|
178
180
|
end
|
179
181
|
|
@@ -190,13 +192,13 @@ class Archive::Tar::Minitar::PosixHeader
|
|
190
192
|
end
|
191
193
|
|
192
194
|
def calculate_checksum(hdr)
|
193
|
-
hdr.unpack(
|
195
|
+
hdr.unpack("C*").inject { |a, e| a + e }
|
194
196
|
end
|
195
197
|
|
196
198
|
def header(chksum)
|
197
199
|
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
|
198
|
-
|
199
|
-
|
200
|
+
oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
|
201
|
+
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
|
200
202
|
str = arr.pack(HEADER_PACK_FORMAT)
|
201
203
|
str + "\0" * ((BLOCK_SIZE - bytesize(str)) % BLOCK_SIZE)
|
202
204
|
end
|
@@ -11,14 +11,21 @@ module Archive::Tar::Minitar
|
|
11
11
|
# This marks the EntryStream closed for reading without closing the
|
12
12
|
# actual data stream.
|
13
13
|
module InvalidEntryStream
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
def read(*)
|
15
|
+
raise ClosedStream
|
16
|
+
end
|
17
|
+
|
18
|
+
def getc
|
19
|
+
raise ClosedStream
|
20
|
+
end
|
21
|
+
|
22
|
+
def rewind
|
23
|
+
raise ClosedStream
|
24
|
+
end
|
25
|
+
|
26
|
+
def closed?
|
27
|
+
true
|
28
|
+
end
|
22
29
|
end
|
23
30
|
|
24
31
|
# EntryStreams are pseudo-streams on top of the main data stream.
|
@@ -30,24 +37,24 @@ module Archive::Tar::Minitar
|
|
30
37
|
end
|
31
38
|
|
32
39
|
def initialize(header, io)
|
33
|
-
@io
|
34
|
-
@name
|
35
|
-
@mode
|
36
|
-
@uid
|
37
|
-
@gid
|
38
|
-
@size
|
39
|
-
@mtime
|
40
|
+
@io = io
|
41
|
+
@name = header.name
|
42
|
+
@mode = header.mode
|
43
|
+
@uid = header.uid
|
44
|
+
@gid = header.gid
|
45
|
+
@size = header.size
|
46
|
+
@mtime = header.mtime
|
40
47
|
@checksum = header.checksum
|
41
48
|
@typeflag = header.typeflag
|
42
49
|
@linkname = header.linkname
|
43
|
-
@magic
|
44
|
-
@version
|
45
|
-
@uname
|
46
|
-
@gname
|
50
|
+
@magic = header.magic
|
51
|
+
@version = header.version
|
52
|
+
@uname = header.uname
|
53
|
+
@gname = header.gname
|
47
54
|
@devmajor = header.devmajor
|
48
55
|
@devminor = header.devminor
|
49
|
-
@prefix
|
50
|
-
@read
|
56
|
+
@prefix = header.prefix
|
57
|
+
@read = 0
|
51
58
|
@orig_pos =
|
52
59
|
if Archive::Tar::Minitar.seekable?(@io)
|
53
60
|
@io.pos
|
@@ -79,25 +86,25 @@ module Archive::Tar::Minitar
|
|
79
86
|
# Returns +true+ if the entry represents a directory.
|
80
87
|
def directory?
|
81
88
|
case @typeflag
|
82
|
-
when
|
89
|
+
when "5"
|
83
90
|
true
|
84
|
-
when
|
91
|
+
when "0", "\0"
|
85
92
|
# If the name ends with a slash, treat it as a directory.
|
86
93
|
# This is what other major tar implementations do for
|
87
94
|
# interoperability and compatibility with older tar variants
|
88
95
|
# and some new ones.
|
89
|
-
@name.end_with?(
|
96
|
+
@name.end_with?("/")
|
90
97
|
else
|
91
98
|
false
|
92
99
|
end
|
93
100
|
end
|
94
|
-
|
101
|
+
alias_method :directory, :directory?
|
95
102
|
|
96
103
|
# Returns +true+ if the entry represents a plain file.
|
97
104
|
def file?
|
98
|
-
(@typeflag ==
|
105
|
+
(@typeflag == "0" || @typeflag == "\0") && !@name.end_with?("/")
|
99
106
|
end
|
100
|
-
|
107
|
+
alias_method :file, :file?
|
101
108
|
|
102
109
|
# Returns +true+ if the current read pointer is at the end of the
|
103
110
|
# EntryStream data.
|
@@ -125,7 +132,7 @@ module Archive::Tar::Minitar
|
|
125
132
|
|
126
133
|
# Returns the full and proper name of the entry.
|
127
134
|
def full_name
|
128
|
-
if @prefix !=
|
135
|
+
if @prefix != ""
|
129
136
|
File.join(@prefix, @name)
|
130
137
|
else
|
131
138
|
@name
|
@@ -185,7 +192,7 @@ module Archive::Tar::Minitar
|
|
185
192
|
def self.each_entry(io)
|
186
193
|
return to_enum(__method__, io) unless block_given?
|
187
194
|
|
188
|
-
open(io) do |reader|
|
195
|
+
Input.open(io) do |reader|
|
189
196
|
reader.each_entry do |entry|
|
190
197
|
yield entry
|
191
198
|
end
|
@@ -195,7 +202,11 @@ module Archive::Tar::Minitar
|
|
195
202
|
# Creates and returns a new Reader object.
|
196
203
|
def initialize(io)
|
197
204
|
@io = io
|
198
|
-
@init_pos =
|
205
|
+
@init_pos = begin
|
206
|
+
io.pos
|
207
|
+
rescue
|
208
|
+
nil
|
209
|
+
end
|
199
210
|
end
|
200
211
|
|
201
212
|
# Resets the read pointer to the beginning of data stream. Do not call
|
@@ -229,14 +240,17 @@ module Archive::Tar::Minitar
|
|
229
240
|
raise Archive::Tar::Minitar::InvalidTarStream if header.size < 0
|
230
241
|
|
231
242
|
if header.long_name?
|
232
|
-
|
243
|
+
name_block = (header.size / 512.0).ceil * 512
|
244
|
+
|
245
|
+
name = @io.read(name_block).rstrip
|
233
246
|
header = PosixHeader.from_stream(@io)
|
247
|
+
|
234
248
|
return if header.empty?
|
235
249
|
header.name = name
|
236
250
|
end
|
237
251
|
|
238
252
|
entry = EntryStream.new(header, @io)
|
239
|
-
size
|
253
|
+
size = entry.size
|
240
254
|
|
241
255
|
yield entry
|
242
256
|
|
@@ -259,7 +273,7 @@ module Archive::Tar::Minitar
|
|
259
273
|
entry.close
|
260
274
|
end
|
261
275
|
end
|
262
|
-
|
276
|
+
alias_method :each, :each_entry
|
263
277
|
|
264
278
|
# Returns false if the reader is open (it never closes).
|
265
279
|
def closed?
|
@@ -30,8 +30,8 @@ module Archive::Tar::Minitar
|
|
30
30
|
def self.const_missing(c)
|
31
31
|
case c
|
32
32
|
when :FileOverflow
|
33
|
-
warn
|
34
|
-
|
33
|
+
warn "Writer::BoundedWriteStream::FileOverflow has been renamed " \
|
34
|
+
"to Writer::WriteBoundaryOverflow"
|
35
35
|
const_set :FileOverflow,
|
36
36
|
Archive::Tar::Minitar::Writer::WriteBoundaryOverflow
|
37
37
|
else
|
@@ -46,9 +46,9 @@ module Archive::Tar::Minitar
|
|
46
46
|
attr_reader :written
|
47
47
|
|
48
48
|
def initialize(io, limit)
|
49
|
-
@io
|
50
|
-
@limit
|
51
|
-
@written
|
49
|
+
@io = io
|
50
|
+
@limit = limit
|
51
|
+
@written = 0
|
52
52
|
end
|
53
53
|
|
54
54
|
def write(data)
|
@@ -65,7 +65,7 @@ module Archive::Tar::Minitar
|
|
65
65
|
def self.const_missing(c)
|
66
66
|
case c
|
67
67
|
when :BoundedStream
|
68
|
-
warn
|
68
|
+
warn "BoundedStream has been renamed to BoundedWriteStream"
|
69
69
|
const_set(:BoundedStream, BoundedWriteStream)
|
70
70
|
else
|
71
71
|
super
|
@@ -101,7 +101,7 @@ module Archive::Tar::Minitar
|
|
101
101
|
|
102
102
|
# Creates and returns a new Writer object.
|
103
103
|
def initialize(io)
|
104
|
-
@io
|
104
|
+
@io = io
|
105
105
|
@closed = false
|
106
106
|
end
|
107
107
|
|
@@ -139,10 +139,10 @@ module Archive::Tar::Minitar
|
|
139
139
|
raise ClosedStream if @closed
|
140
140
|
|
141
141
|
header = {
|
142
|
-
:mode
|
143
|
-
:mtime
|
144
|
-
:gid
|
145
|
-
:uid
|
142
|
+
:mode => opts.fetch(:mode, 0o644),
|
143
|
+
:mtime => opts.fetch(:mtime, nil),
|
144
|
+
:gid => opts.fetch(:gid, nil),
|
145
|
+
:uid => opts.fetch(:uid, nil)
|
146
146
|
}
|
147
147
|
|
148
148
|
data = opts.fetch(:data, nil)
|
@@ -151,12 +151,12 @@ module Archive::Tar::Minitar
|
|
151
151
|
if block_given?
|
152
152
|
if data
|
153
153
|
raise ArgumentError,
|
154
|
-
|
154
|
+
"Too much data (opts[:data] and block_given?)."
|
155
155
|
end
|
156
156
|
|
157
|
-
raise ArgumentError,
|
157
|
+
raise ArgumentError, "No size provided" unless size
|
158
158
|
else
|
159
|
-
raise ArgumentError,
|
159
|
+
raise ArgumentError, "No data provided" unless data
|
160
160
|
|
161
161
|
bytes = bytesize(data)
|
162
162
|
size = bytes if size.nil? || size < bytes
|
@@ -164,7 +164,8 @@ module Archive::Tar::Minitar
|
|
164
164
|
|
165
165
|
header[:size] = size
|
166
166
|
|
167
|
-
|
167
|
+
short_name, prefix, needs_long_name = split_name(name)
|
168
|
+
write_header(header, name, short_name, prefix, needs_long_name)
|
168
169
|
|
169
170
|
os = BoundedWriteStream.new(@io, size)
|
170
171
|
if block_given?
|
@@ -211,12 +212,15 @@ module Archive::Tar::Minitar
|
|
211
212
|
raise Archive::Tar::Minitar::NonSeekableStream
|
212
213
|
end
|
213
214
|
|
215
|
+
short_name, prefix, needs_long_name = split_name(name)
|
216
|
+
|
217
|
+
data_offset = needs_long_name ? 3 * 512 : 512
|
214
218
|
init_pos = @io.pos
|
215
|
-
@io.write("\0" *
|
219
|
+
@io.write("\0" * data_offset) # placeholder for the header
|
216
220
|
|
217
221
|
yield WriteOnlyStream.new(@io), opts
|
218
222
|
|
219
|
-
size
|
223
|
+
size = @io.pos - (init_pos + data_offset)
|
220
224
|
remainder = (512 - (size % 512)) % 512
|
221
225
|
@io.write("\0" * remainder)
|
222
226
|
|
@@ -227,9 +231,11 @@ module Archive::Tar::Minitar
|
|
227
231
|
:mtime => opts[:mtime],
|
228
232
|
:size => size,
|
229
233
|
:gid => opts[:gid],
|
230
|
-
:uid => opts[:uid]
|
234
|
+
:uid => opts[:uid]
|
231
235
|
}
|
232
|
-
|
236
|
+
|
237
|
+
write_header(header, name, short_name, prefix, needs_long_name)
|
238
|
+
|
233
239
|
@io.pos = final_pos
|
234
240
|
end
|
235
241
|
|
@@ -239,13 +245,38 @@ module Archive::Tar::Minitar
|
|
239
245
|
|
240
246
|
header = {
|
241
247
|
:mode => opts[:mode],
|
242
|
-
:typeflag =>
|
248
|
+
:typeflag => "5",
|
249
|
+
:size => 0,
|
250
|
+
:gid => opts[:gid],
|
251
|
+
:uid => opts[:uid],
|
252
|
+
:mtime => opts[:mtime]
|
253
|
+
}
|
254
|
+
|
255
|
+
short_name, prefix, needs_long_name = split_name(name)
|
256
|
+
write_header(header, name, short_name, prefix, needs_long_name)
|
257
|
+
|
258
|
+
nil
|
259
|
+
end
|
260
|
+
|
261
|
+
# Creates a symbolic link entry in the tar.
|
262
|
+
def symlink(name, link_target, opts = {})
|
263
|
+
raise ClosedStream if @closed
|
264
|
+
|
265
|
+
raise FileNameTooLong if link_target.size > 100
|
266
|
+
|
267
|
+
name, prefix = split_name(name)
|
268
|
+
header = {
|
269
|
+
:name => name,
|
270
|
+
:mode => opts[:mode],
|
271
|
+
:typeflag => "2",
|
243
272
|
:size => 0,
|
273
|
+
:linkname => link_target,
|
244
274
|
:gid => opts[:gid],
|
245
275
|
:uid => opts[:uid],
|
246
276
|
:mtime => opts[:mtime],
|
277
|
+
:prefix => prefix
|
247
278
|
}
|
248
|
-
|
279
|
+
@io.write(PosixHeader.new(header))
|
249
280
|
nil
|
250
281
|
end
|
251
282
|
|
@@ -271,47 +302,45 @@ module Archive::Tar::Minitar
|
|
271
302
|
|
272
303
|
private
|
273
304
|
|
274
|
-
def write_header(long_name,
|
275
|
-
short_name, prefix, needs_long_name = split_name(long_name)
|
276
|
-
|
305
|
+
def write_header(header, long_name, short_name, prefix, needs_long_name)
|
277
306
|
if needs_long_name
|
278
307
|
long_name_header = {
|
279
|
-
:prefix =>
|
308
|
+
:prefix => "",
|
280
309
|
:name => PosixHeader::GNU_EXT_LONG_LINK,
|
281
|
-
:typeflag =>
|
282
|
-
:size => long_name.length,
|
283
|
-
:mode => 0
|
310
|
+
:typeflag => "L",
|
311
|
+
:size => long_name.length + 1,
|
312
|
+
:mode => 0
|
284
313
|
}
|
285
314
|
@io.write(PosixHeader.new(long_name_header))
|
286
315
|
@io.write(long_name)
|
287
316
|
@io.write("\0" * (512 - (long_name.length % 512)))
|
288
317
|
end
|
289
318
|
|
290
|
-
new_header = header.merge({
|
319
|
+
new_header = header.merge({:name => short_name, :prefix => prefix})
|
291
320
|
@io.write(PosixHeader.new(new_header))
|
292
321
|
end
|
293
322
|
|
294
323
|
def split_name(name)
|
295
324
|
if bytesize(name) <= 100
|
296
|
-
prefix =
|
325
|
+
prefix = ""
|
297
326
|
else
|
298
|
-
parts = name.split(
|
327
|
+
parts = name.split("/")
|
299
328
|
newname = parts.pop
|
300
329
|
|
301
|
-
nxt =
|
330
|
+
nxt = ""
|
302
331
|
|
303
332
|
loop do
|
304
|
-
nxt = parts.pop ||
|
333
|
+
nxt = parts.pop || ""
|
305
334
|
break if bytesize(newname) + 1 + bytesize(nxt) >= 100
|
306
335
|
newname = "#{nxt}/#{newname}"
|
307
336
|
end
|
308
337
|
|
309
|
-
prefix = (parts + [nxt]).join(
|
338
|
+
prefix = (parts + [nxt]).join("/")
|
310
339
|
|
311
340
|
name = newname
|
312
341
|
end
|
313
342
|
|
314
|
-
[
|
343
|
+
[name, prefix, bytesize(name) > 100 || bytesize(prefix) > 155]
|
315
344
|
end
|
316
345
|
end
|
317
346
|
end
|