class CPIO::ASCIIReader

Constants

FIELD_ORDER
FIELD_SIZES
HEADER_LENGTH
HEADER_PACK

Public Class Methods

new(io) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 71
def initialize(io)
  @io = io
end

Public Instance Methods

each(&block) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 81
def each(&block)
  while true
    entry = read
    break if entry.nil?
    # The CPIO format has the end-of-stream marker as a file called "TRAILER!!!"
    break if entry.name == "TRAILER!!!"
    block.call(entry, entry.file)
    verify_correct_read(entry) unless entry.directory?
  end
end

Private Instance Methods

io() click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 77
def io
  @io
end
padding_file(entry) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 158
def padding_file(entry)
  (-(HEADER_LENGTH + entry.filesize + 2) % 4)
end
padding_name(entry) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 153
def padding_name(entry)
  # name padding is padding up to a multiple of 4 after header+namesize
  -(HEADER_LENGTH + entry.namesize) % 4
end
read() click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 108
def read
  entry = CPIOEntry.new
  header = io.read(HEADER_LENGTH)
  return nil if header.nil?
  FIELD_ORDER.zip(header.unpack(HEADER_PACK)).each do |field, value|
    entry.send("#{field}=", value.to_i(16))
  end

  entry.validate
  entry.mtime = Time.at(entry.mtime)
  read_name(entry, @io)
  read_file(entry, @io)
  entry
end
read_file(entry, io) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 134
def read_file(entry, io)
  if entry.directory?
    entry.file = nil
    #read_file_padding(entry, io)
    nil
  else
    entry.file = BoundedIO.new(io, entry.filesize) do
      read_file_padding(entry, io)
    end
  end
end
read_file_padding(entry, io) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 146
def read_file_padding(entry, io)
  padding_data = io.read(padding_file(entry))
  if padding_data != ("\0" * padding_data.bytesize)
    raise ArgumentError, "Corrupt CPIO or bug? File null padding was #{padding_file(entry)} bytes: #{padding_data.inspect}"
  end
end
read_name(entry, io) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 123
def read_name(entry, io)
  entry.name = io.read(entry.namesize - 1) # - 1 for null terminator
  nul = io.read(1)
  raise ArgumentError, "Corrupt CPIO or bug? Name null terminator was not null: #{nul.inspect}" if nul != "\0"
  padding_data = io.read(padding_name(entry))
  # Padding should be all null bytes
  if padding_data != ("\0" * padding_data.bytesize)
    raise ArgumentError, "Corrupt CPIO or bug? Name null padding was #{padding_name(entry)} bytes: #{padding_data.inspect}"
  end
end
verify_correct_read(entry) click to toggle source
# File lib/excavate/extractors/cpio/cpio.rb, line 92
def verify_correct_read(entry)
  # Read and throw away the whole file if not read at all.
  entry.file.tap do |file|
    if file.nil? ||  file.remaining == 0
      # All OK! :)
    elsif file.remaining == file.length
      file.read(16384) while !file.eof?
    else
      # The file was only partially read? This should be an error by the
      # user.
      consumed = file.length - file.remaining
      raise BadState, "Only #{consumed} bytes were read of the #{file.length} byte file: #{entry.name}"
    end
  end
end