class Archive::Tar::Reader
Public Class Methods
new(stream, options = {})
click to toggle source
# File lib/archive/tar/reader.rb, line 32 def initialize(stream, options = {}) options = { compression: :auto, tmpdir: "/tmp", block_size: 2 ** 19, read_limit: 2 ** 19, cache: true, cache_size: 16, max_cache_size: 2 ** 19, generate_index: true, use_normalized_paths: true, }.merge(options) if stream.is_a? String stream = File.new(stream) end @options = options @stream = _generate_compressed_stream(stream, options[:compression]) @cache_candidates = [] @cache = {} build_index if options[:generate_index] end
Public Instance Methods
[](file)
click to toggle source
# File lib/archive/tar/reader.rb, line 76 def [](file) stat(file, false) end
build_index()
click to toggle source
Build new index of files
# File lib/archive/tar/reader.rb, line 157 def build_index new_index = {} @stream.rewind until @stream.eof? raw_header = @stream.read(512) break if raw_header == "\0" * 512 header = Archive::Tar::Format::unpack_header(raw_header) if @options[:use_normalized_paths] header[:path] = normalize_path(header[:path]) end unless header[:type] == :pax_global_header new_index[header[:path]] = [ header, @stream.tell ] end @stream.seek(header[:blocks] * 512, IO::SEEK_CUR) end @index = new_index end
each(&block)
click to toggle source
# File lib/archive/tar/reader.rb, line 88 def each(&block) @index.each do |key, value| block.call(*(value)) end end
entry?(file)
click to toggle source
# File lib/archive/tar/reader.rb, line 84 def entry?(file) has_entry? file end
extract(source, dest, options = {})
click to toggle source
Extract file from /source/ to /dest/
# File lib/archive/tar/reader.rb, line 134 def extract(source, dest, options = {}) options = { recursive: true, preserve: false, override: false }.merge(options) unless @index.key? source raise NoSuchEntryError.new(source) end header, offset = @index[source] _extract(header, offset, dest, options) if header[:type] == :directory && options[:recursive] @index.each_key do |entry| if entry[0, source.length] == source && entry != source extract(entry, File.join(dest, entry.sub(source, "")), options) end end end end
extract_all(dest, options = {})
click to toggle source
Extract all files to /dest/ directory
# File lib/archive/tar/reader.rb, line 111 def extract_all(dest, options = {}) options = { :preserve => false, :override => false }.merge(options) unless File::exists? dest FileUtils::mkdir_p dest end unless File.directory? dest raise "No such directory: #{dest}" end @index.each_key do |entry| ndest = File.join(dest, entry) header, offset = @index[entry] _extract(header, offset, ndest, options) end end
has_entry?(file)
click to toggle source
# File lib/archive/tar/reader.rb, line 80 def has_entry?(file) @index.key? file end
index()
click to toggle source
# File lib/archive/tar/reader.rb, line 61 def index @index end
read(name, no_cache = false)
click to toggle source
# File lib/archive/tar/reader.rb, line 94 def read(name, no_cache = false) header, offset = stat(name) if @options[:cache] && header[:size] <= @options[:max_cache_size] && !no_cache @cache_candidates << name rebuild_cache end if @cache.key? name && !no_cache return @cache[name] end @stream.seek(offset) @stream.read(header[:size]) end
stat(file, exception = true)
click to toggle source
# File lib/archive/tar/reader.rb, line 65 def stat(file, exception = true) if @options[:use_normalized_paths] result = @index[normalize_path(file)] else result = @index[file] end raise NoSuchEntryError.new(file) if result == nil && exception result end
stream()
click to toggle source
# File lib/archive/tar/reader.rb, line 57 def stream @stream end
Protected Instance Methods
_extract(header, offset, dest, options)
click to toggle source
# File lib/archive/tar/reader.rb, line 237 def _extract(header, offset, dest, options) if !options[:override] && File::exists?(dest) return end case header[:type] when :normal export_to_file(offset, header[:size], header[:path], dest) when :directory if !File.exists? dest Dir.mkdir(dest) end when :symbolic File.symlink(header[:dest], dest) when :link if header[:dest][0] == "/" FileUtils.touch(header[:dest]) else FileUtils.touch(realpath("#{dest}/../#{header[:dest]}")) end File.link(header[:dest], dest) when :block system("mknod '#{dest}' b #{header[:major]} #{header[:minor]}") when :character system("mknod '#{dest}' c #{header[:major]} #{header[:minor]}") when :fifo system("mknod '#{dest}' p #{header[:major]} #{header[:minor]}") end if options[:preserve] File.chmod(header[:mode], dest) File.chown(header[:uid], header[:gid], dest) end end
export_to_file(offset, length, source, destination)
click to toggle source
# File lib/archive/tar/reader.rb, line 208 def export_to_file(offset, length, source, destination) destination = File.new(destination, "w+b") if destination.is_a?(String) if @options[:max_cache_size] >= length && @options[:cache] @cache_candidates << source rebuild_cache end if @cache.key? source destination.write(@cache[source]) return true end @stream.seek(offset) if length <= @options[:read_limit] destination.write(@stream.read(length)) return true end i = 0 while i < length destination.write(@stream.read(@options[:block_size])) i += @options[:block_size] end true end
rebuild_cache()
click to toggle source
# File lib/archive/tar/reader.rb, line 182 def rebuild_cache return nil unless @options[:cache] cache_count = {} @cache_candidates.each do |candidate| cache_count[candidate] = 0 unless cache_count.key? candidate cache_count[candidate] += 1 end cache_count_sorted = cache_count.sort do |pair_1, pair_2| (pair_1[1] <=> pair_2[1]) * -1 end puts cache_count_sorted @cache = {} i = 0 cache_count_sorted.each do |tupel| if i >= @options[:cache_size] break end @cache[tupel[0]] = read(tupel[0], true) i += 1 end end
Private Instance Methods
_detect_compression(filename)
click to toggle source
# File lib/archive/tar/reader.rb, line 274 def _detect_compression(filename) return :none unless filename.include? "." case filename.slice(Range.new(filename.rindex(".") + 1, -1)) when "gz", "tgz" return :gzip when "bz2", "tbz", "tb2" return :bzip2 when "xz", "txz" return :xz when "lz", "lzma", "tlz" return :lzma end return :none end
_generate_compressed_stream(stream, compression)
click to toggle source
# File lib/archive/tar/reader.rb, line 291 def _generate_compressed_stream(stream, compression) if compression == :auto if stream.is_a? File compression = _detect_compression(stream.path) else compression = :none end end programs = { gzip: "gzip -d -c -f", bzip2: "bzip2 -d -c -f", lzma: "lzma -d -c -f", xz: "xz -d -c -f" } unless programs.key? compression return stream end io = IO::popen("/usr/bin/env #{programs[compression]}", "a+b") new_file = File.open("#{@options[:tmpdir]}/#{rand(500512)}", "w+b") Thread.new do until stream.eof? io.write(stream.read(@options[:block_size])) end io.close_write end until io.eof? new_file.write(io.read(@options[:block_size])) end new_file end