diff options
Diffstat (limited to 'lib/rubygems/package')
-rw-r--r-- | lib/rubygems/package/digest_io.rb | 3 | ||||
-rw-r--r-- | lib/rubygems/package/file_source.rb | 1 | ||||
-rw-r--r-- | lib/rubygems/package/io_source.rb | 1 | ||||
-rw-r--r-- | lib/rubygems/package/old.rb | 5 | ||||
-rw-r--r-- | lib/rubygems/package/source.rb | 1 | ||||
-rw-r--r-- | lib/rubygems/package/tar_header.rb | 107 | ||||
-rw-r--r-- | lib/rubygems/package/tar_reader.rb | 53 | ||||
-rw-r--r-- | lib/rubygems/package/tar_reader/entry.rb | 125 | ||||
-rw-r--r-- | lib/rubygems/package/tar_writer.rb | 41 |
9 files changed, 213 insertions, 124 deletions
diff --git a/lib/rubygems/package/digest_io.rb b/lib/rubygems/package/digest_io.rb index 4736f76d93..f04ab97462 100644 --- a/lib/rubygems/package/digest_io.rb +++ b/lib/rubygems/package/digest_io.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + ## # IO wrapper that creates digests of contents written to the IO it wraps. @@ -35,7 +36,7 @@ class Gem::Package::DigestIO yield digest_io - return digests + digests end ## diff --git a/lib/rubygems/package/file_source.rb b/lib/rubygems/package/file_source.rb index 14c7a9f6d2..d9717e0f2a 100644 --- a/lib/rubygems/package/file_source.rb +++ b/lib/rubygems/package/file_source.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + ## # The primary source of gems is a file on disk, including all usages # internal to rubygems. diff --git a/lib/rubygems/package/io_source.rb b/lib/rubygems/package/io_source.rb index 03d7714524..227835dfce 100644 --- a/lib/rubygems/package/io_source.rb +++ b/lib/rubygems/package/io_source.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + ## # Supports reading and writing gems from/to a generic IO object. This is # useful for other applications built on top of rubygems, such as diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb index 09a02d3ecd..1a13ac3e29 100644 --- a/lib/rubygems/package/old.rb +++ b/lib/rubygems/package/old.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. @@ -69,7 +70,7 @@ class Gem::Package::Old < Gem::Package file_data << line end - file_data = file_data.strip.unpack("m")[0] + file_data = file_data.strip.unpack1("m") file_data = Zlib::Inflate.inflate file_data raise Gem::Package::FormatError, "#{full_name} in #{@gem} is corrupt" if @@ -77,7 +78,7 @@ class Gem::Package::Old < Gem::Package FileUtils.rm_rf destination - FileUtils.mkdir_p File.dirname(destination), :mode => dir_mode && 0755 + FileUtils.mkdir_p File.dirname(destination), mode: dir_mode && 0o755 File.open destination, "wb", file_mode(entry["mode"]) do |out| out.write file_data diff --git a/lib/rubygems/package/source.rb b/lib/rubygems/package/source.rb index 69701e55e9..8c44f8c305 100644 --- a/lib/rubygems/package/source.rb +++ b/lib/rubygems/package/source.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true + class Gem::Package::Source # :nodoc: end diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb index 590a2f0315..dd5e835a1e 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true -#-- + +# rubocop:disable Style/AsciiComments + # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. -#++ + +# rubocop:enable Style/AsciiComments ## #-- @@ -92,39 +95,40 @@ class Gem::Package::TarHeader attr_reader(*FIELDS) - EMPTY_HEADER = ("\0" * 512).freeze # :nodoc: + EMPTY_HEADER = ("\0" * 512).b.freeze # :nodoc: ## # Creates a tar header from IO +stream+ def self.from(stream) header = stream.read 512 - empty = (EMPTY_HEADER == header) + return EMPTY if header == EMPTY_HEADER fields = header.unpack UNPACK_FORMAT - new :name => fields.shift, - :mode => strict_oct(fields.shift), - :uid => oct_or_256based(fields.shift), - :gid => oct_or_256based(fields.shift), - :size => strict_oct(fields.shift), - :mtime => strict_oct(fields.shift), - :checksum => strict_oct(fields.shift), - :typeflag => fields.shift, - :linkname => fields.shift, - :magic => fields.shift, - :version => strict_oct(fields.shift), - :uname => fields.shift, - :gname => fields.shift, - :devmajor => strict_oct(fields.shift), - :devminor => strict_oct(fields.shift), - :prefix => fields.shift, - - :empty => empty + new name: fields.shift, + mode: strict_oct(fields.shift), + uid: oct_or_256based(fields.shift), + gid: oct_or_256based(fields.shift), + size: strict_oct(fields.shift), + mtime: strict_oct(fields.shift), + checksum: strict_oct(fields.shift), + typeflag: fields.shift, + linkname: fields.shift, + magic: fields.shift, + version: strict_oct(fields.shift), + uname: fields.shift, + gname: fields.shift, + devmajor: strict_oct(fields.shift), + devminor: strict_oct(fields.shift), + prefix: fields.shift, + + empty: false end def self.strict_oct(str) - return str.strip.oct if str.strip =~ /\A[0-7]*\z/ + str.strip! + return str.oct if /\A[0-7]*\z/.match?(str) raise ArgumentError, "#{str.inspect} is not an octal string" end @@ -134,7 +138,8 @@ class Gem::Package::TarHeader # \ff flags a negative 256-based number # In case we have a match, parse it as a signed binary value # in big-endian order, except that the high-order bit is ignored. - return str.unpack("N2").last if str =~ /\A[\x80\xff]/n + + return str.unpack1("@4N") if /\A[\x80\xff]/n.match?(str) strict_oct(str) end @@ -146,25 +151,43 @@ class Gem::Package::TarHeader raise ArgumentError, ":name, :size, :prefix and :mode required" end - vals[:uid] ||= 0 - vals[:gid] ||= 0 - vals[:mtime] ||= 0 - vals[:checksum] ||= "" - vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty? - vals[:magic] ||= "ustar" - vals[:version] ||= "00" - vals[:uname] ||= "wheel" - vals[:gname] ||= "wheel" - vals[:devmajor] ||= 0 - vals[:devminor] ||= 0 - - FIELDS.each do |name| - instance_variable_set "@#{name}", vals[name] - end + @checksum = vals[:checksum] || "" + @devmajor = vals[:devmajor] || 0 + @devminor = vals[:devminor] || 0 + @gid = vals[:gid] || 0 + @gname = vals[:gname] || "wheel" + @linkname = vals[:linkname] + @magic = vals[:magic] || "ustar" + @mode = vals[:mode] + @mtime = vals[:mtime] || 0 + @name = vals[:name] + @prefix = vals[:prefix] + @size = vals[:size] + @typeflag = vals[:typeflag] + @typeflag = "0" if @typeflag.nil? || @typeflag.empty? + @uid = vals[:uid] || 0 + @uname = vals[:uname] || "wheel" + @version = vals[:version] || "00" @empty = vals[:empty] end + EMPTY = new({ # :nodoc: + checksum: 0, + gname: "", + linkname: "", + magic: "", + mode: 0, + name: "", + prefix: "", + size: 0, + uname: "", + version: 0, + + empty: true, + }).freeze + private_constant :EMPTY + ## # Is the tar entry empty? @@ -208,7 +231,7 @@ class Gem::Package::TarHeader private def calculate_checksum(header) - header.unpack("C*").inject {|a, b| a + b } + header.sum(0) end def header(checksum = @checksum) @@ -234,10 +257,10 @@ class Gem::Package::TarHeader header = header.pack PACK_FORMAT - header << ("\0" * ((512 - header.size) % 512)) + header.ljust 512, "\0" end def oct(num, len) - "%0#{len}o" % num + format("%0#{len}o", num) end end diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb index cdc3fdc015..25f9b2f945 100644 --- a/lib/rubygems/package/tar_reader.rb +++ b/lib/rubygems/package/tar_reader.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true -#-- + +# rubocop:disable Style/AsciiComments + # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. -#++ + +# rubocop:enable Style/AsciiComments ## # TarReader reads tar files and allows iteration over their items @@ -11,11 +14,6 @@ class Gem::Package::TarReader include Enumerable ## - # Raised if the tar IO is not seekable - - class UnexpectedEOF < StandardError; end - - ## # Creates a new TarReader on +io+ and yields it to the block, if given. def self.new(io) @@ -53,44 +51,23 @@ class Gem::Package::TarReader def each return enum_for __method__ unless block_given? - use_seek = @io.respond_to?(:seek) - until @io.eof? do - header = Gem::Package::TarHeader.from @io - return if header.empty? + begin + header = Gem::Package::TarHeader.from @io + rescue ArgumentError => e + # Specialize only exceptions from Gem::Package::TarHeader.strict_oct + raise e unless e.message.match?(/ is not an octal string$/) + raise Gem::Package::TarInvalidError, e.message + end + return if header.empty? entry = Gem::Package::TarReader::Entry.new header, @io - size = entry.header.size - yield entry - - skip = (512 - (size % 512)) % 512 - pending = size - entry.bytes_read - - if use_seek - begin - # avoid reading if the @io supports seeking - @io.seek pending, IO::SEEK_CUR - pending = 0 - rescue Errno::EINVAL - end - end - - # if seeking isn't supported or failed - while pending > 0 do - bytes_read = @io.read([pending, 4096].min).size - raise UnexpectedEOF if @io.eof? - pending -= bytes_read - end - - @io.read skip # discard trailing zeros - - # make sure nobody can use #read, #getc or #rewind anymore entry.close end end - alias each_entry each + alias_method :each_entry, :each ## # NOTE: Do not call #rewind during #each @@ -115,7 +92,7 @@ class Gem::Package::TarReader return unless found - return yield found + yield found ensure rewind end diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb index 8634381c18..5e9d9af5c6 100644 --- a/lib/rubygems/package/tar_reader/entry.rb +++ b/lib/rubygems/package/tar_reader/entry.rb @@ -1,14 +1,31 @@ # frozen_string_literal: true -#++ + +# rubocop:disable Style/AsciiComments + # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. -#-- + +# rubocop:enable Style/AsciiComments ## # Class for reading entries out of a tar file class Gem::Package::TarReader::Entry ## + # Creates a new tar entry for +header+ that will be read from +io+ + # If a block is given, the entry is yielded and then closed. + + def self.open(header, io, &block) + entry = new header, io + return entry unless block_given? + begin + yield entry + ensure + entry.close + end + end + + ## # Header for this tar entry attr_reader :header @@ -21,6 +38,7 @@ class Gem::Package::TarReader::Entry @header = header @io = io @orig_pos = @io.pos + @end_pos = @orig_pos + @header.size @read = 0 end @@ -39,7 +57,14 @@ class Gem::Package::TarReader::Entry # Closes the tar entry def close + return if closed? + # Seek to the end of the entry if it wasn't fully read + seek(0, IO::SEEK_END) + # discard trailing zeros + skip = (512 - (@header.size % 512)) % 512 + @io.read(skip) @closed = true + nil end ## @@ -77,9 +102,7 @@ class Gem::Package::TarReader::Entry # Read one byte from the tar entry def getc - check_closed - - return nil if @read >= @header.size + return nil if eof? ret = @io.getc @read += 1 if ret @@ -117,36 +140,43 @@ class Gem::Package::TarReader::Entry bytes_read end + ## + # Seek to the position in the tar entry + + def pos=(new_pos) + seek(new_pos, IO::SEEK_SET) + end + def size @header.size end - alias length size + alias_method :length, :size ## - # Reads +len+ bytes from the tar file entry, or the rest of the entry if - # nil - - def read(len = nil) - check_closed + # Reads +maxlen+ bytes from the tar file entry, or the rest of the entry if nil - return nil if @read >= @header.size + def read(maxlen = nil) + if eof? + return maxlen.to_i.zero? ? "" : nil + end - len ||= @header.size - @read - max_read = [len, @header.size - @read].min + max_read = [maxlen, @header.size - @read].compact.min ret = @io.read max_read + if ret.nil? + return maxlen ? nil : "" # IO.read returns nil on EOF with len argument + end @read += ret.size ret end - def readpartial(maxlen = nil, outbuf = "".b) - check_closed - - raise EOFError if @read >= @header.size + def readpartial(maxlen, outbuf = "".b) + if eof? && maxlen > 0 + raise EOFError, "end of file reached" + end - maxlen ||= @header.size - @read max_read = [maxlen, @header.size - @read].min @io.readpartial(max_read, outbuf) @@ -156,12 +186,63 @@ class Gem::Package::TarReader::Entry end ## + # Seeks to +offset+ bytes into the tar file entry + # +whence+ can be IO::SEEK_SET, IO::SEEK_CUR, or IO::SEEK_END + + def seek(offset, whence = IO::SEEK_SET) + check_closed + + new_pos = + case whence + when IO::SEEK_SET then @orig_pos + offset + when IO::SEEK_CUR then @io.pos + offset + when IO::SEEK_END then @end_pos + offset + else + raise ArgumentError, "invalid whence" + end + + if new_pos < @orig_pos + new_pos = @orig_pos + elsif new_pos > @end_pos + new_pos = @end_pos + end + + pending = new_pos - @io.pos + + return 0 if pending == 0 + + if @io.respond_to?(:seek) + begin + # avoid reading if the @io supports seeking + @io.seek new_pos, IO::SEEK_SET + pending = 0 + rescue Errno::EINVAL + end + end + + # if seeking isn't supported or failed + # negative seek requires that we rewind and read + if pending < 0 + @io.rewind + pending = new_pos + end + + while pending > 0 do + size_read = @io.read([pending, 4096].min)&.size + raise(EOFError, "end of file reached") if size_read.nil? + pending -= size_read + end + + @read = @io.pos - @orig_pos + + 0 + end + + ## # Rewinds to the beginning of the tar file entry def rewind check_closed - - @io.pos = @orig_pos - @read = 0 + seek(0, IO::SEEK_SET) end end diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb index db5242c5e4..b24bdb63e7 100644 --- a/lib/rubygems/package/tar_writer.rb +++ b/lib/rubygems/package/tar_writer.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true -#-- + +# rubocop:disable Style/AsciiComments + # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. -#++ + +# rubocop:enable Style/AsciiComments ## # Allows writing of tar files @@ -113,9 +116,9 @@ class Gem::Package::TarWriter final_pos = @io.pos @io.pos = init_pos - header = Gem::Package::TarHeader.new :name => name, :mode => mode, - :size => size, :prefix => prefix, - :mtime => Gem.source_date_epoch + header = Gem::Package::TarHeader.new name: name, mode: mode, + size: size, prefix: prefix, + mtime: Gem.source_date_epoch @io.write header @io.pos = final_pos @@ -189,7 +192,7 @@ class Gem::Package::TarWriter if signer.key signature = signer.sign signature_digest.digest - add_file_simple "#{name}.sig", 0444, signature.length do |io| + add_file_simple "#{name}.sig", 0o444, signature.length do |io| io.write signature end end @@ -206,9 +209,9 @@ class Gem::Package::TarWriter name, prefix = split_name name - header = Gem::Package::TarHeader.new(:name => name, :mode => mode, - :size => size, :prefix => prefix, - :mtime => Gem.source_date_epoch).to_s + header = Gem::Package::TarHeader.new(name: name, mode: mode, + size: size, prefix: prefix, + mtime: Gem.source_date_epoch).to_s @io.write header os = BoundedStream.new @io, size @@ -232,11 +235,11 @@ class Gem::Package::TarWriter name, prefix = split_name name - header = Gem::Package::TarHeader.new(:name => name, :mode => mode, - :size => 0, :typeflag => "2", - :linkname => target, - :prefix => prefix, - :mtime => Gem.source_date_epoch).to_s + header = Gem::Package::TarHeader.new(name: name, mode: mode, + size: 0, typeflag: "2", + linkname: target, + prefix: prefix, + mtime: Gem.source_date_epoch).to_s @io.write header @@ -286,10 +289,10 @@ class Gem::Package::TarWriter name, prefix = split_name(name) - header = Gem::Package::TarHeader.new :name => name, :mode => mode, - :typeflag => "5", :size => 0, - :prefix => prefix, - :mtime => Gem.source_date_epoch + header = Gem::Package::TarHeader.new name: name, mode: mode, + typeflag: "5", size: 0, + prefix: prefix, + mtime: Gem.source_date_epoch @io.write header @@ -323,6 +326,6 @@ class Gem::Package::TarWriter end end - return name, prefix + [name, prefix] end end |