class Minitar::PosixHeader
Implements the POSIX tar header as a Ruby class. The structure of the POSIX tar header is:
struct tarfile_entry_posix { // pack unpack char name; // ASCII (+ Z unless filled) a100 Z100 char mode; // 0 padded, octal, null a8 A8 char uid; // 0 padded, octal, null a8 A8 char gid; // 0 padded, octal, null a8 A8 char size; // 0 padded, octal, null a12 A12 char mtime; // 0 padded, octal, null a12 A12 char checksum; // 0 padded, octal, null, space a8 A8 char typeflag; // see below a a char linkname; // ASCII + (Z unless filled) a100 Z100 char magic; // “ustar0” a6 A6 char version; // “00” a2 A2 char uname; // ASCIIZ a32 Z32 char gname; // ASCIIZ a32 Z32 char devmajor; // 0 padded, octal, null a8 A8 char devminor; // 0 padded, octal, null a8 A8 char prefix; // ASCII (+ Z unless filled) a155 Z155 };
The typeflag is one of several known values. POSIX indicates that “A POSIX-compliant implementation must treat any unrecognized typeflag value as a regular file.”
Constants
- BLOCK_SIZE
- FIELDS
-
All fields available in a POSIX tar(1) header.
- GNU_EXT_LONG_LINK
- HEADER_PACK_FORMAT
-
The pack format passed to Array#pack for encoding a header.
- HEADER_UNPACK_FORMAT
-
The unpack format passed to String#unpack for decoding a header.
- MAGIC_BYTES
- OPTIONAL_FIELDS
-
Fields that may be set in a POSIX tar(1) header.
- REQUIRED_FIELDS
-
Fields that must be set in a POSIX tar(1) header.
Attributes
The checksum of the file. Stored as an octal integer. Calculated before encoding the header as a string.
The major device ID. Not currently used.
The minor device ID. Not currently used.
The Unix owner group ID of the file. Stored as an octal integer.
The group name of the Unix owner of the file.
The target of the symbolic link.
Always “ustar0”.
The Unix file mode of the file. Stored as an octal integer. Required.
The modification time of the file in epoch seconds. Stored as an octal integer.
The name of the file. Required.
By default, limited to 100 bytes, but may be up to BLOCK_SIZE bytes if using the GNU long name tar extension.
The prefix of the file; the path before name. Limited to 155 bytes. Required.
The size of the file. Required.
The type of record in the file.
0:: Regular file. NULL should be treated as a synonym, for compatibility purposes. 1:: Hard link. 2:: Symbolic link. 3:: Character device node. 4:: Block device node. 5:: Directory. 6:: FIFO node. 7:: Reserved. L:: GNU extension for long filenames when name is ././@LongLink.
The Unix owner user ID of the file. Stored as an octal integer.
The user name of the Unix owner of the file.
Always “00”
Public Class Methods
Source
# File lib/minitar/posix_header.rb, line 213 def calculate_checksum(hdr) hdr.unpack("C*").inject(:+) end
Source
# File lib/minitar/posix_header.rb, line 157 def empty? = @empty # Indicates if the header has a valid magic value. def valid? = empty? || @magic == MAGIC_BYTES # Returns +true+ if the header is a long name special header which indicates # that the next block of data is the filename. def long_name? = typeflag == "L" && name == GNU_EXT_LONG_LINK # Returns +true+ if the header is a PAX extended header which contains # metadata for the next file entry. def pax_header? = typeflag == "x" # Sets the +name+ to the +value+ provided and clears +prefix+. # # Used by Minitar::Reader#each_entry to set the long name when processing GNU long # filename extensions. # # The +value+ must be the complete name, including leading directory components. def long_name=(value) valid_name!(value) @prefix = "" @name = value end # A string representation of the header. def to_s update_checksum header(@checksum) end alias_method :to_str, :to_s # TODO: In Minitar 2, PosixHeader#to_str will be removed. # Update the checksum field. def update_checksum hh = header(" " * 8) @checksum = oct(calculate_checksum(hh), 6) end private def valid_name!(value) return if value.is_a?(String) && !value.empty? raise ArgumentError, "Field name must be a non-empty string" end def oct(num, len) if num.nil? "\0" * (len + 1) else "%0#{len}o" % num end end def calculate_checksum(hdr) hdr.unpack("C*").inject(:+) end def header(chksum) arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11), oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version, uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix] str = arr.pack(HEADER_PACK_FORMAT) str + "\0" * ((BLOCK_SIZE - str.bytesize) % BLOCK_SIZE) end ## # :attr_accessor: name # The name of the file. Required. # # By default, limited to 100 bytes, but may be up to BLOCK_SIZE bytes if using the # GNU long name tar extension. ## # :attr_accessor: size # The size of the file. Required. ## # :attr_reader: prefix # The prefix of the file; the path before #name. Limited to 155 bytes. # Required. ## # :attr_reader: mode # The Unix file mode of the file. Stored as an octal integer. Required. ## # :attr_reader: uid # The Unix owner user ID of the file. Stored as an octal integer. ## # :attr_reader: uname # The user name of the Unix owner of the file. ## # :attr_reader: gid # The Unix owner group ID of the file. Stored as an octal integer. ## # :attr_reader: gname # The group name of the Unix owner of the file. ## # :attr_reader: mtime # The modification time of the file in epoch seconds. Stored as an # octal integer. ## # :attr_reader: checksum # The checksum of the file. Stored as an octal integer. Calculated # before encoding the header as a string. ## # :attr_reader: typeflag # The type of record in the file. # # +0+:: Regular file. NULL should be treated as a synonym, for compatibility # purposes. # +1+:: Hard link. # +2+:: Symbolic link. # +3+:: Character device node. # +4+:: Block device node. # +5+:: Directory. # +6+:: FIFO node. # +7+:: Reserved. # +L+:: GNU extension for long filenames when #name is <tt>././@LongLink</tt>. ## # :attr_reader: linkname # The target of the symbolic link. ## # :attr_reader: magic # Always "ustar\0". ## # :attr_reader: version # Always "00" ## # :attr_reader: devmajor # The major device ID. Not currently used. ## # :attr_reader: devminor # The minor device ID. Not currently used.
Indicates if the header was an empty header.
Source
# File lib/minitar/posix_header.rb, line 68 def from_data(data) fields = data.unpack(HEADER_UNPACK_FORMAT) name = fields.shift mode = fields.shift.oct uid = fields.shift.oct gid = fields.shift.oct size = parse_numeric_field(fields.shift) mtime = fields.shift.oct checksum = fields.shift.oct typeflag = fields.shift linkname = fields.shift magic = fields.shift version = fields.shift.oct uname = fields.shift gname = fields.shift devmajor = fields.shift.oct devminor = fields.shift.oct prefix = fields.shift empty = !data.each_byte.any?(&:nonzero?) new( name: name, mode: mode, uid: uid, gid: gid, size: size, mtime: mtime, checksum: checksum, typeflag: typeflag, magic: magic, version: version, uname: uname, gname: gname, devmajor: devmajor, devminor: devminor, prefix: prefix, empty: empty, linkname: linkname ) end
Creates a new PosixHeader from a BLOCK_SIZE-byte data buffer.
Source
# File lib/minitar/posix_header.rb, line 65 def from_stream(stream) = from_data(stream.read(BLOCK_SIZE)) # Creates a new PosixHeader from a BLOCK_SIZE-byte data buffer. def from_data(data) fields = data.unpack(HEADER_UNPACK_FORMAT) name = fields.shift mode = fields.shift.oct uid = fields.shift.oct gid = fields.shift.oct size = parse_numeric_field(fields.shift) mtime = fields.shift.oct checksum = fields.shift.oct typeflag = fields.shift linkname = fields.shift magic = fields.shift version = fields.shift.oct uname = fields.shift gname = fields.shift devmajor = fields.shift.oct devminor = fields.shift.oct prefix = fields.shift empty = !data.each_byte.any?(&:nonzero?) new( name: name, mode: mode, uid: uid, gid: gid, size: size, mtime: mtime, checksum: checksum, typeflag: typeflag, magic: magic, version: version, uname: uname, gname: gname, devmajor: devmajor, devminor: devminor, prefix: prefix, empty: empty, linkname: linkname ) end private def parse_numeric_field(string) return string.oct if /\A[0-7 \0]*\z/.match?(string) # \0 appears as a padding return parse_base256(string) if string.bytes.first == 0x80 || string.bytes.first == 0xff raise ArgumentError, "#{string.inspect} is not a valid numeric field" end def parse_base256(string) # https://www.gnu.org/software/tar/manual/html_node/Extensions.html bytes = string.bytes case bytes.first when 0x80 # Positive number: *non-leading* bytes, number in big-endian order bytes[1..].inject(0) { |r, byte| (r << 8) | byte } when 0xff # Negative number: *all* bytes, two's complement in big-endian order result = bytes.inject(0) { |r, byte| (r << 8) | byte } bit_length = bytes.size * 8 result - (1 << bit_length) else raise ArgumentError, "Invalid binary field format" end end end
Creates a new PosixHeader from a data stream.
Source
# File lib/minitar/posix_header.rb, line 217 def header(chksum) arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11), oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version, uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix] str = arr.pack(HEADER_PACK_FORMAT) str + "\0" * ((BLOCK_SIZE - str.bytesize) % BLOCK_SIZE) end
Source
# File lib/minitar/posix_header.rb, line 136 def initialize(v) REQUIRED_FIELDS.each do |f| raise ArgumentError, "Field #{f} is required." unless v.key?(f) end v[:mtime] = v[:mtime].to_i v[:checksum] ||= "" v[:typeflag] ||= "0" v[:magic] ||= MAGIC_BYTES v[:version] ||= "00" FIELDS.each do |f| instance_variable_set(:"@#{f}", v[f]) end @empty = v[:empty] valid_name!(v[:name]) unless v[:empty] end
Creates a new PosixHeader. A PosixHeader cannot be created unless name, size, prefix, and mode are provided.
Source
# File lib/minitar/posix_header.rb, line 176 def long_name=(value) valid_name!(value) @prefix = "" @name = value end
Sets the name to the value provided and clears prefix.
Used by Minitar::Reader#each_entry to set the long name when processing GNU long filename extensions.
The value must be the complete name, including leading directory components.
Source
# File lib/minitar/posix_header.rb, line 164 def long_name? = typeflag == "L" && name == GNU_EXT_LONG_LINK # Returns +true+ if the header is a PAX extended header which contains # metadata for the next file entry. def pax_header? = typeflag == "x" # Sets the +name+ to the +value+ provided and clears +prefix+. # # Used by Minitar::Reader#each_entry to set the long name when processing GNU long # filename extensions. # # The +value+ must be the complete name, including leading directory components. def long_name=(value) valid_name!(value) @prefix = "" @name = value end # A string representation of the header. def to_s update_checksum header(@checksum) end alias_method :to_str, :to_s # TODO: In Minitar 2, PosixHeader#to_str will be removed. # Update the checksum field. def update_checksum hh = header(" " * 8) @checksum = oct(calculate_checksum(hh), 6) end private def valid_name!(value) return if value.is_a?(String) && !value.empty? raise ArgumentError, "Field name must be a non-empty string" end def oct(num, len) if num.nil? "\0" * (len + 1) else "%0#{len}o" % num end end def calculate_checksum(hdr) hdr.unpack("C*").inject(:+) end def header(chksum) arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11), oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version, uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix] str = arr.pack(HEADER_PACK_FORMAT) str + "\0" * ((BLOCK_SIZE - str.bytesize) % BLOCK_SIZE) end ## # :attr_accessor: name # The name of the file. Required. # # By default, limited to 100 bytes, but may be up to BLOCK_SIZE bytes if using the # GNU long name tar extension. ## # :attr_accessor: size # The size of the file. Required. ## # :attr_reader: prefix # The prefix of the file; the path before #name. Limited to 155 bytes. # Required. ## # :attr_reader: mode # The Unix file mode of the file. Stored as an octal integer. Required. ## # :attr_reader: uid # The Unix owner user ID of the file. Stored as an octal integer. ## # :attr_reader: uname # The user name of the Unix owner of the file. ## # :attr_reader: gid # The Unix owner group ID of the file. Stored as an octal integer. ## # :attr_reader: gname # The group name of the Unix owner of the file. ## # :attr_reader: mtime # The modification time of the file in epoch seconds. Stored as an # octal integer. ## # :attr_reader: checksum # The checksum of the file. Stored as an octal integer. Calculated # before encoding the header as a string. ## # :attr_reader: typeflag # The type of record in the file. # # +0+:: Regular file. NULL should be treated as a synonym, for compatibility # purposes. # +1+:: Hard link. # +2+:: Symbolic link. # +3+:: Character device node. # +4+:: Block device node. # +5+:: Directory. # +6+:: FIFO node. # +7+:: Reserved. # +L+:: GNU extension for long filenames when #name is <tt>././@LongLink</tt>. ## # :attr_reader: linkname # The target of the symbolic link. ## # :attr_reader: magic # Always "ustar\0". ## # :attr_reader: version # Always "00" ## # :attr_reader: devmajor # The major device ID. Not currently used. ## # :attr_reader: devminor # The minor device ID. Not currently used. end end
Returns true if the header is a long name special header which indicates that the next block of data is the filename.
Source
# File lib/minitar/posix_header.rb, line 205 def oct(num, len) if num.nil? "\0" * (len + 1) else "%0#{len}o" % num end end
Source
# File lib/minitar/posix_header.rb, line 118 def parse_base256(string) # https://www.gnu.org/software/tar/manual/html_node/Extensions.html bytes = string.bytes case bytes.first when 0x80 # Positive number: *non-leading* bytes, number in big-endian order bytes[1..].inject(0) { |r, byte| (r << 8) | byte } when 0xff # Negative number: *all* bytes, two's complement in big-endian order result = bytes.inject(0) { |r, byte| (r << 8) | byte } bit_length = bytes.size * 8 result - (1 << bit_length) else raise ArgumentError, "Invalid binary field format" end end
Source
# File lib/minitar/posix_header.rb, line 112 def parse_numeric_field(string) return string.oct if /\A[0-7 \0]*\z/.match?(string) # \0 appears as a padding return parse_base256(string) if string.bytes.first == 0x80 || string.bytes.first == 0xff raise ArgumentError, "#{string.inspect} is not a valid numeric field" end
Source
# File lib/minitar/posix_header.rb, line 168 def pax_header? = typeflag == "x" # Sets the +name+ to the +value+ provided and clears +prefix+. # # Used by Minitar::Reader#each_entry to set the long name when processing GNU long # filename extensions. # # The +value+ must be the complete name, including leading directory components. def long_name=(value) valid_name!(value) @prefix = "" @name = value end # A string representation of the header. def to_s update_checksum header(@checksum) end alias_method :to_str, :to_s # TODO: In Minitar 2, PosixHeader#to_str will be removed. # Update the checksum field. def update_checksum hh = header(" " * 8) @checksum = oct(calculate_checksum(hh), 6) end private def valid_name!(value) return if value.is_a?(String) && !value.empty? raise ArgumentError, "Field name must be a non-empty string" end def oct(num, len) if num.nil? "\0" * (len + 1) else "%0#{len}o" % num end end def calculate_checksum(hdr) hdr.unpack("C*").inject(:+) end def header(chksum) arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11), oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version, uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix] str = arr.pack(HEADER_PACK_FORMAT) str + "\0" * ((BLOCK_SIZE - str.bytesize) % BLOCK_SIZE) end ## # :attr_accessor: name # The name of the file. Required. # # By default, limited to 100 bytes, but may be up to BLOCK_SIZE bytes if using the # GNU long name tar extension. ## # :attr_accessor: size # The size of the file. Required. ## # :attr_reader: prefix # The prefix of the file; the path before #name. Limited to 155 bytes. # Required. ## # :attr_reader: mode # The Unix file mode of the file. Stored as an octal integer. Required. ## # :attr_reader: uid # The Unix owner user ID of the file. Stored as an octal integer. ## # :attr_reader: uname # The user name of the Unix owner of the file. ## # :attr_reader: gid # The Unix owner group ID of the file. Stored as an octal integer. ## # :attr_reader: gname # The group name of the Unix owner of the file. ## # :attr_reader: mtime # The modification time of the file in epoch seconds. Stored as an # octal integer. ## # :attr_reader: checksum # The checksum of the file. Stored as an octal integer. Calculated # before encoding the header as a string. ## # :attr_reader: typeflag # The type of record in the file. # # +0+:: Regular file. NULL should be treated as a synonym, for compatibility # purposes. # +1+:: Hard link. # +2+:: Symbolic link. # +3+:: Character device node. # +4+:: Block device node. # +5+:: Directory. # +6+:: FIFO node. # +7+:: Reserved. # +L+:: GNU extension for long filenames when #name is <tt>././@LongLink</tt>. ## # :attr_reader: linkname # The target of the symbolic link. ## # :attr_reader: magic # Always "ustar\0". ## # :attr_reader: version # Always "00" ## # :attr_reader: devmajor # The major device ID. Not currently used. ## # :attr_reader: devminor # The minor device ID. Not currently used. end
Returns true if the header is a PAX extended header which contains metadata for the next file entry.
Source
# File lib/minitar/posix_header.rb, line 184 def to_s update_checksum header(@checksum) end
A string representation of the header.
Source
# File lib/minitar/posix_header.rb, line 193 def update_checksum hh = header(" " * 8) @checksum = oct(calculate_checksum(hh), 6) end
Update the checksum field.
Source
# File lib/minitar/posix_header.rb, line 160 def valid? = empty? || @magic == MAGIC_BYTES # Returns +true+ if the header is a long name special header which indicates # that the next block of data is the filename. def long_name? = typeflag == "L" && name == GNU_EXT_LONG_LINK # Returns +true+ if the header is a PAX extended header which contains # metadata for the next file entry. def pax_header? = typeflag == "x" # Sets the +name+ to the +value+ provided and clears +prefix+. # # Used by Minitar::Reader#each_entry to set the long name when processing GNU long # filename extensions. # # The +value+ must be the complete name, including leading directory components. def long_name=(value) valid_name!(value) @prefix = "" @name = value end # A string representation of the header. def to_s update_checksum header(@checksum) end alias_method :to_str, :to_s # TODO: In Minitar 2, PosixHeader#to_str will be removed. # Update the checksum field. def update_checksum hh = header(" " * 8) @checksum = oct(calculate_checksum(hh), 6) end private def valid_name!(value) return if value.is_a?(String) && !value.empty? raise ArgumentError, "Field name must be a non-empty string" end def oct(num, len) if num.nil? "\0" * (len + 1) else "%0#{len}o" % num end end def calculate_checksum(hdr) hdr.unpack("C*").inject(:+) end def header(chksum) arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11), oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version, uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix] str = arr.pack(HEADER_PACK_FORMAT) str + "\0" * ((BLOCK_SIZE - str.bytesize) % BLOCK_SIZE) end ## # :attr_accessor: name # The name of the file. Required. # # By default, limited to 100 bytes, but may be up to BLOCK_SIZE bytes if using the # GNU long name tar extension. ## # :attr_accessor: size # The size of the file. Required. ## # :attr_reader: prefix # The prefix of the file; the path before #name. Limited to 155 bytes. # Required. ## # :attr_reader: mode # The Unix file mode of the file. Stored as an octal integer. Required. ## # :attr_reader: uid # The Unix owner user ID of the file. Stored as an octal integer. ## # :attr_reader: uname # The user name of the Unix owner of the file. ## # :attr_reader: gid # The Unix owner group ID of the file. Stored as an octal integer. ## # :attr_reader: gname # The group name of the Unix owner of the file. ## # :attr_reader: mtime # The modification time of the file in epoch seconds. Stored as an # octal integer. ## # :attr_reader: checksum # The checksum of the file. Stored as an octal integer. Calculated # before encoding the header as a string. ## # :attr_reader: typeflag # The type of record in the file. # # +0+:: Regular file. NULL should be treated as a synonym, for compatibility # purposes. # +1+:: Hard link. # +2+:: Symbolic link. # +3+:: Character device node. # +4+:: Block device node. # +5+:: Directory. # +6+:: FIFO node. # +7+:: Reserved. # +L+:: GNU extension for long filenames when #name is <tt>././@LongLink</tt>. ## # :attr_reader: linkname # The target of the symbolic link. ## # :attr_reader: magic # Always "ustar\0". ## # :attr_reader: version # Always "00" ## # :attr_reader: devmajor # The major device ID. Not currently used. ## # :attr_reader: devminor # The minor device ID. Not currently used. end
Indicates if the header has a valid magic value.
Source
# File lib/minitar/posix_header.rb, line 200 def valid_name!(value) return if value.is_a?(String) && !value.empty? raise ArgumentError, "Field name must be a non-empty string" end
Public Instance Methods
Source
# File lib/minitar/posix_header.rb, line 51 def name=(value) valid_name!(value) @name = value end
The name of the file. Required.
By default, limited to 100 bytes, but may be up to BLOCK_SIZE bytes if using the GNU long name tar extension.