minitar 0.5.4 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Code-of-Conduct.md +74 -0
- data/Contributing.md +84 -0
- data/History.md +107 -0
- data/Licence.md +15 -0
- data/Manifest.txt +24 -0
- data/README.rdoc +81 -0
- data/Rakefile +46 -107
- data/docs/bsdl.txt +19 -0
- data/docs/ruby.txt +56 -0
- data/lib/archive-tar-minitar.rb +3 -0
- data/lib/archive/tar/minitar.rb +197 -889
- data/lib/archive/tar/minitar/input.rb +212 -0
- data/lib/archive/tar/minitar/output.rb +69 -0
- data/lib/archive/tar/minitar/posix_header.rb +259 -0
- data/lib/archive/tar/minitar/reader.rb +237 -0
- data/lib/archive/tar/minitar/writer.rb +297 -0
- data/lib/minitar.rb +12 -0
- data/test/minitest_helper.rb +11 -0
- data/test/support/tar_test_helpers.rb +119 -0
- data/test/test_tar_header.rb +74 -0
- data/test/test_tar_input.rb +167 -0
- data/test/test_tar_output.rb +53 -0
- data/test/test_tar_reader.rb +148 -0
- data/test/test_tar_writer.rb +190 -0
- metadata +232 -43
- data/ChangeLog +0 -17
- data/Install +0 -6
- data/README +0 -68
- data/bin/minitar +0 -27
- data/lib/archive/tar/minitar/command.rb +0 -814
- data/tests/tc_tar.rb +0 -629
- data/tests/testall.rb +0 -10
@@ -0,0 +1,212 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'archive/tar/minitar/reader'
|
4
|
+
|
5
|
+
module Archive::Tar::Minitar
|
6
|
+
# Wraps a Archive::Tar::Minitar::Reader with convenience methods and wrapped
|
7
|
+
# stream management; Input only works with data streams that can be rewound.
|
8
|
+
class Input
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# With no associated block, +Input.open+ is a synonym for +Input.new+. If
|
12
|
+
# the optional code block is given, it will be given the new Input as an
|
13
|
+
# argument and the Input object will automatically be closed when the block
|
14
|
+
# terminates (this also closes the wrapped stream object). In this
|
15
|
+
# instance, +Input.open+ returns the value of the block.
|
16
|
+
#
|
17
|
+
# call-seq:
|
18
|
+
# Archive::Tar::Minitar::Input.open(io) -> input
|
19
|
+
# Archive::Tar::Minitar::Input.open(io) { |input| block } -> obj
|
20
|
+
def self.open(input)
|
21
|
+
stream = new(input)
|
22
|
+
return stream unless block_given?
|
23
|
+
yield stream
|
24
|
+
ensure
|
25
|
+
stream.close
|
26
|
+
end
|
27
|
+
|
28
|
+
# Iterates over each entry in the provided input. This wraps the common
|
29
|
+
# pattern of:
|
30
|
+
#
|
31
|
+
# Archive::Tar::Minitar::Input.open(io) do |i|
|
32
|
+
# inp.each do |entry|
|
33
|
+
# # ...
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# If a block is not provided, an enumerator will be created with the same
|
38
|
+
# behaviour.
|
39
|
+
#
|
40
|
+
# call-seq:
|
41
|
+
# Archive::Tar::Minitar::Input.each_entry(io) -> enumerator
|
42
|
+
# Archive::Tar::Minitar::Input.each_entry(io) { |entry| block } -> obj
|
43
|
+
def self.each_entry(input)
|
44
|
+
return to_enum(__method__, input) unless block_given?
|
45
|
+
|
46
|
+
open(input) do |stream|
|
47
|
+
stream.each do |entry|
|
48
|
+
yield entry
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Creates a new Input object. If +input+ is a stream object that responds
|
54
|
+
# to #read, then it will simply be wrapped. Otherwise, one will be created
|
55
|
+
# and opened using Kernel#open. When Input#close is called, the stream
|
56
|
+
# object wrapped will be closed.
|
57
|
+
#
|
58
|
+
# An exception will be raised if the stream that is wrapped does not
|
59
|
+
# support rewinding.
|
60
|
+
#
|
61
|
+
# call-seq:
|
62
|
+
# Archive::Tar::Minitar::Input.new(io) -> input
|
63
|
+
# Archive::Tar::Minitar::Input.new(path) -> input
|
64
|
+
def initialize(input)
|
65
|
+
@io = if input.respond_to?(:read)
|
66
|
+
input
|
67
|
+
else
|
68
|
+
::Kernel.open(input, 'rb')
|
69
|
+
end
|
70
|
+
|
71
|
+
unless Archive::Tar::Minitar.seekable?(@io, :rewind)
|
72
|
+
raise Archive::Tar::Minitar::NonSeekableStream
|
73
|
+
end
|
74
|
+
|
75
|
+
@tar = Reader.new(@io)
|
76
|
+
end
|
77
|
+
|
78
|
+
# When provided a block, iterates through each entry in the archive. When
|
79
|
+
# finished, rewinds to the beginning of the stream.
|
80
|
+
#
|
81
|
+
# If not provided a block, creates an enumerator with the same semantics.
|
82
|
+
def each_entry
|
83
|
+
return to_enum unless block_given?
|
84
|
+
|
85
|
+
@tar.each do |entry|
|
86
|
+
yield entry
|
87
|
+
end
|
88
|
+
ensure
|
89
|
+
@tar.rewind
|
90
|
+
end
|
91
|
+
alias each each_entry
|
92
|
+
|
93
|
+
# Extracts the current +entry+ to +destdir+. If a block is provided, it
|
94
|
+
# yields an +action+ Symbol, the full name of the file being extracted
|
95
|
+
# (+name+), and a Hash of statistical information (+stats+).
|
96
|
+
#
|
97
|
+
# The +action+ will be one of:
|
98
|
+
# <tt>:dir</tt>:: The +entry+ is a directory.
|
99
|
+
# <tt>:file_start</tt>:: The +entry+ is a file; the extract of the
|
100
|
+
# file is just beginning.
|
101
|
+
# <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
|
102
|
+
# of the +entry+.
|
103
|
+
# <tt>:file_done</tt>:: Yielded when the +entry+ is completed.
|
104
|
+
#
|
105
|
+
# The +stats+ hash contains the following keys:
|
106
|
+
# <tt>:current</tt>:: The current total number of bytes read in the
|
107
|
+
# +entry+.
|
108
|
+
# <tt>:currinc</tt>:: The current number of bytes read in this read
|
109
|
+
# cycle.
|
110
|
+
# <tt>:entry</tt>:: The entry being extracted; this is a
|
111
|
+
# Reader::EntryStream, with all methods thereof.
|
112
|
+
def extract_entry(destdir, entry) # :yields action, name, stats:
|
113
|
+
stats = {
|
114
|
+
:current => 0,
|
115
|
+
:currinc => 0,
|
116
|
+
:entry => entry
|
117
|
+
}
|
118
|
+
|
119
|
+
# extract_entry is not vulnerable to prefix '/' vulnerabilities, but it
|
120
|
+
# is vulnerable to relative path directories. This code will break this
|
121
|
+
# vulnerability. For this version, we are breaking relative paths HARD by
|
122
|
+
# throwing an exception.
|
123
|
+
#
|
124
|
+
# Future versions may permit relative paths as long as the file does not
|
125
|
+
# leave +destdir+.
|
126
|
+
#
|
127
|
+
# However, squeeze consecutive '/' characters together.
|
128
|
+
full_name = entry.full_name.squeeze('/')
|
129
|
+
|
130
|
+
if full_name =~ /\.{2}(?:\/|\z)/
|
131
|
+
raise SecureRelativePathError, %q(Path contains '..')
|
132
|
+
end
|
133
|
+
|
134
|
+
if entry.directory?
|
135
|
+
dest = File.join(destdir, full_name)
|
136
|
+
|
137
|
+
yield :dir, full_name, stats if block_given?
|
138
|
+
|
139
|
+
if Archive::Tar::Minitar.dir?(dest)
|
140
|
+
begin
|
141
|
+
FileUtils.chmod(entry.mode, dest)
|
142
|
+
rescue
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
else
|
146
|
+
File.unlink(dest.chomp('/')) if File.symlink?(dest.chomp('/'))
|
147
|
+
|
148
|
+
FileUtils.mkdir_p(dest, :mode => entry.mode)
|
149
|
+
FileUtils.chmod(entry.mode, dest)
|
150
|
+
end
|
151
|
+
|
152
|
+
fsync_dir(dest)
|
153
|
+
fsync_dir(File.join(dest, '..'))
|
154
|
+
return
|
155
|
+
else # it's a file
|
156
|
+
destdir = File.join(destdir, File.dirname(full_name))
|
157
|
+
FileUtils.mkdir_p(destdir, :mode => 0o755)
|
158
|
+
|
159
|
+
destfile = File.join(destdir, File.basename(full_name))
|
160
|
+
|
161
|
+
File.unlink(destfile) if File.symlink?(destfile)
|
162
|
+
|
163
|
+
# Errno::ENOENT
|
164
|
+
# rubocop:disable Style/RescueModifier
|
165
|
+
FileUtils.chmod(0o600, destfile) rescue nil
|
166
|
+
# rubocop:enable Style/RescueModifier
|
167
|
+
|
168
|
+
yield :file_start, full_name, stats if block_given?
|
169
|
+
|
170
|
+
File.open(destfile, 'wb', entry.mode) do |os|
|
171
|
+
loop do
|
172
|
+
data = entry.read(4096)
|
173
|
+
break unless data
|
174
|
+
|
175
|
+
stats[:currinc] = os.write(data)
|
176
|
+
stats[:current] += stats[:currinc]
|
177
|
+
|
178
|
+
yield :file_progress, full_name, stats if block_given?
|
179
|
+
end
|
180
|
+
os.fsync
|
181
|
+
end
|
182
|
+
|
183
|
+
FileUtils.chmod(entry.mode, destfile)
|
184
|
+
fsync_dir(File.dirname(destfile))
|
185
|
+
fsync_dir(File.join(File.dirname(destfile), '..'))
|
186
|
+
|
187
|
+
yield :file_done, full_name, stats if block_given?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Returns the Reader object for direct access.
|
192
|
+
attr_reader :tar
|
193
|
+
|
194
|
+
# Closes both the Reader object and the wrapped data stream.
|
195
|
+
def close
|
196
|
+
@io.close
|
197
|
+
@tar.close
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def fsync_dir(dirname)
|
203
|
+
# make sure this hits the disc
|
204
|
+
dir = open(dirname, 'rb')
|
205
|
+
dir.fsync
|
206
|
+
rescue # ignore IOError if it's an unpatched (old) Ruby
|
207
|
+
nil
|
208
|
+
ensure
|
209
|
+
dir.close if dir rescue nil # rubocop:disable Style/RescueModifier
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'archive/tar/minitar/writer'
|
4
|
+
|
5
|
+
module Archive::Tar::Minitar
|
6
|
+
# Wraps a Archive::Tar::Minitar::Writer with convenience methods and wrapped
|
7
|
+
# stream management. If the stream provided to Output does not support random
|
8
|
+
# access, only Writer#add_file_simple and Writer#mkdir are guaranteed to
|
9
|
+
# work.
|
10
|
+
class Output
|
11
|
+
# With no associated block, +Output.open+ is a synonym for +Output.new+. If
|
12
|
+
# the optional code block is given, it will be given the new Output as an
|
13
|
+
# argument and the Output object will automatically be closed when the
|
14
|
+
# block terminates (this also closes the wrapped stream object). In this
|
15
|
+
# instance, +Output.open+ returns the value of the block.
|
16
|
+
#
|
17
|
+
# call-seq:
|
18
|
+
# Archive::Tar::Minitar::Output.open(io) -> output
|
19
|
+
# Archive::Tar::Minitar::Output.open(io) { |output| block } -> obj
|
20
|
+
def self.open(output)
|
21
|
+
stream = new(output)
|
22
|
+
return stream unless block_given?
|
23
|
+
yield stream
|
24
|
+
ensure
|
25
|
+
stream.close
|
26
|
+
end
|
27
|
+
|
28
|
+
# Output.tar is a wrapper for Output.open that yields the owned tar object
|
29
|
+
# instead of the Output object. If a block is not provided, an enumerator
|
30
|
+
# will be created with the same behaviour.
|
31
|
+
#
|
32
|
+
# call-seq:
|
33
|
+
# Archive::Tar::Minitar::Output.tar(io) -> enumerator
|
34
|
+
# Archive::Tar::Minitar::Output.tar(io) { |tar| block } -> obj
|
35
|
+
def self.tar(output)
|
36
|
+
return to_enum(__method__, output) unless block_given?
|
37
|
+
|
38
|
+
open(output) do |stream|
|
39
|
+
yield stream.tar
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates a new Output object. If +output+ is a stream object that responds
|
44
|
+
# to #write, then it will simply be wrapped. Otherwise, one will be created
|
45
|
+
# and opened using Kernel#open. When Output#close is called, the stream
|
46
|
+
# object wrapped will be closed.
|
47
|
+
#
|
48
|
+
# call-seq:
|
49
|
+
# Archive::Tar::Minitar::Output.new(io) -> output
|
50
|
+
# Archive::Tar::Minitar::Output.new(path) -> output
|
51
|
+
def initialize(output)
|
52
|
+
@io = if output.respond_to?(:write)
|
53
|
+
output
|
54
|
+
else
|
55
|
+
::Kernel.open(output, 'wb')
|
56
|
+
end
|
57
|
+
@tar = Archive::Tar::Minitar::Writer.new(@io)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the Writer object for direct access.
|
61
|
+
attr_reader :tar
|
62
|
+
|
63
|
+
# Closes the Writer object and the wrapped data stream.
|
64
|
+
def close
|
65
|
+
@tar.close
|
66
|
+
@io.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
module Archive; end
|
5
|
+
##
|
6
|
+
module Archive::Tar; end
|
7
|
+
##
|
8
|
+
module Archive::Tar::Minitar; end
|
9
|
+
|
10
|
+
# Implements the POSIX tar header as a Ruby class. The structure of
|
11
|
+
# the POSIX tar header is:
|
12
|
+
#
|
13
|
+
# struct tarfile_entry_posix
|
14
|
+
# { // pack/unpack
|
15
|
+
# char name[100]; // ASCII (+ Z unless filled) a100/Z100
|
16
|
+
# char mode[8]; // 0 padded, octal, null a8 /A8
|
17
|
+
# char uid[8]; // 0 padded, octal, null a8 /A8
|
18
|
+
# char gid[8]; // 0 padded, octal, null a8 /A8
|
19
|
+
# char size[12]; // 0 padded, octal, null a12 /A12
|
20
|
+
# char mtime[12]; // 0 padded, octal, null a12 /A12
|
21
|
+
# char checksum[8]; // 0 padded, octal, null, space a8 /A8
|
22
|
+
# char typeflag[1]; // see below a /a
|
23
|
+
# char linkname[100]; // ASCII + (Z unless filled) a100/Z100
|
24
|
+
# char magic[6]; // "ustar\0" a6 /A6
|
25
|
+
# char version[2]; // "00" a2 /A2
|
26
|
+
# char uname[32]; // ASCIIZ a32 /Z32
|
27
|
+
# char gname[32]; // ASCIIZ a32 /Z32
|
28
|
+
# char devmajor[8]; // 0 padded, octal, null a8 /A8
|
29
|
+
# char devminor[8]; // 0 padded, octal, null a8 /A8
|
30
|
+
# char prefix[155]; // ASCII (+ Z unless filled) a155/Z155
|
31
|
+
# };
|
32
|
+
#
|
33
|
+
# The #typeflag is one of several known values.
|
34
|
+
#
|
35
|
+
# POSIX indicates that "A POSIX-compliant implementation must treat any
|
36
|
+
# unrecognized typeflag value as a regular file."
|
37
|
+
class Archive::Tar::Minitar::PosixHeader
|
38
|
+
BLOCK_SIZE = 512
|
39
|
+
|
40
|
+
# Fields that must be set in a POSIX tar(1) header.
|
41
|
+
REQUIRED_FIELDS = [ :name, :size, :prefix, :mode ].freeze
|
42
|
+
# Fields that may be set in a POSIX tar(1) header.
|
43
|
+
OPTIONAL_FIELDS = [
|
44
|
+
:uid, :gid, :mtime, :checksum, :typeflag, :linkname, :magic, :version,
|
45
|
+
:uname, :gname, :devmajor, :devminor
|
46
|
+
].freeze
|
47
|
+
|
48
|
+
# All fields available in a POSIX tar(1) header.
|
49
|
+
FIELDS = (REQUIRED_FIELDS + OPTIONAL_FIELDS).freeze
|
50
|
+
|
51
|
+
FIELDS.each do |f|
|
52
|
+
attr_reader f.to_sym unless f.to_sym == :name
|
53
|
+
end
|
54
|
+
|
55
|
+
# The name of the file. By default, limited to 100 bytes. Required. May be
|
56
|
+
# longer (up to BLOCK_SIZE bytes) if using the GNU long name tar extension.
|
57
|
+
attr_accessor :name
|
58
|
+
|
59
|
+
# The pack format passed to Array#pack for encoding a header.
|
60
|
+
HEADER_PACK_FORMAT = 'a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155'.freeze
|
61
|
+
# The unpack format passed to String#unpack for decoding a header.
|
62
|
+
HEADER_UNPACK_FORMAT = 'Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155'.freeze
|
63
|
+
|
64
|
+
class << self
|
65
|
+
# Creates a new PosixHeader from a data stream.
|
66
|
+
def from_stream(stream)
|
67
|
+
from_data(stream.read(BLOCK_SIZE))
|
68
|
+
end
|
69
|
+
|
70
|
+
# Creates a new PosixHeader from a data stream. Deprecated; use
|
71
|
+
# PosixHeader.from_stream instead.
|
72
|
+
def new_from_stream(stream)
|
73
|
+
warn "#{__method__} has been deprecated; use from_stream instead."
|
74
|
+
from_stream(stream)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Creates a new PosixHeader from a BLOCK_SIZE-byte data buffer.
|
78
|
+
def from_data(data)
|
79
|
+
fields = data.unpack(HEADER_UNPACK_FORMAT)
|
80
|
+
name = fields.shift
|
81
|
+
mode = fields.shift.oct
|
82
|
+
uid = fields.shift.oct
|
83
|
+
gid = fields.shift.oct
|
84
|
+
size = fields.shift.oct
|
85
|
+
mtime = fields.shift.oct
|
86
|
+
checksum = fields.shift.oct
|
87
|
+
typeflag = fields.shift
|
88
|
+
linkname = fields.shift
|
89
|
+
magic = fields.shift
|
90
|
+
version = fields.shift.oct
|
91
|
+
uname = fields.shift
|
92
|
+
gname = fields.shift
|
93
|
+
devmajor = fields.shift.oct
|
94
|
+
devminor = fields.shift.oct
|
95
|
+
prefix = fields.shift
|
96
|
+
|
97
|
+
empty = !data.each_byte.any?(&:nonzero?)
|
98
|
+
|
99
|
+
new(
|
100
|
+
:name => name,
|
101
|
+
:mode => mode,
|
102
|
+
:uid => uid,
|
103
|
+
:gid => gid,
|
104
|
+
:size => size,
|
105
|
+
:mtime => mtime,
|
106
|
+
:checksum => checksum,
|
107
|
+
:typeflag => typeflag,
|
108
|
+
:magic => magic,
|
109
|
+
:version => version,
|
110
|
+
:uname => uname,
|
111
|
+
:gname => gname,
|
112
|
+
:devmajor => devmajor,
|
113
|
+
:devminor => devminor,
|
114
|
+
:prefix => prefix,
|
115
|
+
:empty => empty,
|
116
|
+
:linkname => linkname
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Creates a new PosixHeader. A PosixHeader cannot be created unless
|
122
|
+
# +name+, +size+, +prefix+, and +mode+ are provided.
|
123
|
+
def initialize(v)
|
124
|
+
REQUIRED_FIELDS.each do |f|
|
125
|
+
raise ArgumentError, "Field #{f} is required." unless v.key?(f)
|
126
|
+
end
|
127
|
+
|
128
|
+
v[:mtime] = v[:mtime].to_i
|
129
|
+
v[:checksum] ||= ''
|
130
|
+
v[:typeflag] ||= '0'
|
131
|
+
v[:magic] ||= 'ustar'
|
132
|
+
v[:version] ||= '00'
|
133
|
+
|
134
|
+
FIELDS.each do |f|
|
135
|
+
instance_variable_set("@#{f}", v[f])
|
136
|
+
end
|
137
|
+
|
138
|
+
@empty = v[:empty]
|
139
|
+
end
|
140
|
+
|
141
|
+
# Indicates if the header was an empty header.
|
142
|
+
def empty?
|
143
|
+
@empty
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns +true+ if the header is a long name special header which indicates
|
147
|
+
# that the next block of data is the filename.
|
148
|
+
def long_name?
|
149
|
+
typeflag == 'L' && name == '././@LongLink'
|
150
|
+
end
|
151
|
+
|
152
|
+
# A string representation of the header.
|
153
|
+
def to_s
|
154
|
+
update_checksum
|
155
|
+
header(@checksum)
|
156
|
+
end
|
157
|
+
alias to_str to_s
|
158
|
+
|
159
|
+
# Update the checksum field.
|
160
|
+
def update_checksum
|
161
|
+
hh = header(' ' * 8)
|
162
|
+
@checksum = oct(calculate_checksum(hh), 6)
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def oct(num, len)
|
168
|
+
if num.nil?
|
169
|
+
"\0" * (len + 1)
|
170
|
+
else
|
171
|
+
"%0#{len}o" % num
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def calculate_checksum(hdr)
|
176
|
+
hdr.unpack('C*').inject { |a, e| a + e }
|
177
|
+
end
|
178
|
+
|
179
|
+
def header(chksum)
|
180
|
+
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
|
181
|
+
oct(mtime, 11), chksum, ' ', typeflag, linkname, magic, version,
|
182
|
+
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
|
183
|
+
str = arr.pack(HEADER_PACK_FORMAT)
|
184
|
+
str + "\0" * ((BLOCK_SIZE - str.size) % BLOCK_SIZE)
|
185
|
+
end
|
186
|
+
|
187
|
+
##
|
188
|
+
# :attr_reader: size
|
189
|
+
# The size of the file. Required.
|
190
|
+
|
191
|
+
##
|
192
|
+
# :attr_reader: prefix
|
193
|
+
# The prefix of the file; the path before #name. Limited to 155 bytes.
|
194
|
+
# Required.
|
195
|
+
|
196
|
+
##
|
197
|
+
# :attr_reader: mode
|
198
|
+
# The Unix file mode of the file. Stored as an octal integer. Required.
|
199
|
+
|
200
|
+
##
|
201
|
+
# :attr_reader: uid
|
202
|
+
# The Unix owner user ID of the file. Stored as an octal integer.
|
203
|
+
|
204
|
+
##
|
205
|
+
# :attr_reader: uname
|
206
|
+
# The user name of the Unix owner of the file.
|
207
|
+
|
208
|
+
##
|
209
|
+
# :attr_reader: gid
|
210
|
+
# The Unix owner group ID of the file. Stored as an octal integer.
|
211
|
+
|
212
|
+
##
|
213
|
+
# :attr_reader: gname
|
214
|
+
# The group name of the Unix owner of the file.
|
215
|
+
|
216
|
+
##
|
217
|
+
# :attr_reader: mtime
|
218
|
+
# The modification time of the file in epoch seconds. Stored as an
|
219
|
+
# octal integer.
|
220
|
+
|
221
|
+
##
|
222
|
+
# :attr_reader: checksum
|
223
|
+
# The checksum of the file. Stored as an octal integer. Calculated
|
224
|
+
# before encoding the header as a string.
|
225
|
+
|
226
|
+
##
|
227
|
+
# :attr_reader: typeflag
|
228
|
+
# The type of record in the file.
|
229
|
+
#
|
230
|
+
# +0+:: Regular file. NULL should be treated as a synonym, for compatibility
|
231
|
+
# purposes.
|
232
|
+
# +1+:: Hard link.
|
233
|
+
# +2+:: Symbolic link.
|
234
|
+
# +3+:: Character device node.
|
235
|
+
# +4+:: Block device node.
|
236
|
+
# +5+:: Directory.
|
237
|
+
# +6+:: FIFO node.
|
238
|
+
# +7+:: Reserved.
|
239
|
+
|
240
|
+
##
|
241
|
+
# :attr_reader: linkname
|
242
|
+
# The name of the link stored. Not currently used.
|
243
|
+
|
244
|
+
##
|
245
|
+
# :attr_reader: magic
|
246
|
+
# Always "ustar\0".
|
247
|
+
|
248
|
+
##
|
249
|
+
# :attr_reader: version
|
250
|
+
# Always "00"
|
251
|
+
|
252
|
+
##
|
253
|
+
# :attr_reader: devmajor
|
254
|
+
# The major device ID. Not currently used.
|
255
|
+
|
256
|
+
##
|
257
|
+
# :attr_reader: devminor
|
258
|
+
# The minor device ID. Not currently used.
|
259
|
+
end
|