Class | Gem::Package::TarInput |
In: |
lib/rubygems/package/tar_input.rb
|
Parent: | Object |
++
Copyright (C) 2004 Mauricio Julio Fernández Pradier See LICENSE.txt for additional licensing information.
metadata | [R] |
# File lib/rubygems/package/tar_input.rb, line 24 24: def initialize(io, security_policy = nil) 25: @io = io 26: @tarreader = Gem::Package::TarReader.new @io 27: has_meta = false 28: 29: data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil 30: dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil 31: 32: @tarreader.each do |entry| 33: case entry.full_name 34: when "metadata" 35: @metadata = load_gemspec entry.read 36: has_meta = true 37: when "metadata.gz" 38: begin 39: # if we have a security_policy, then pre-read the metadata file 40: # and calculate it's digest 41: sio = nil 42: if security_policy 43: Gem.ensure_ssl_available 44: sio = StringIO.new(entry.read) 45: meta_dgst = dgst_algo.digest(sio.string) 46: sio.rewind 47: end 48: 49: gzis = Zlib::GzipReader.new(sio || entry) 50: # YAML wants an instance of IO 51: @metadata = load_gemspec(gzis) 52: has_meta = true 53: ensure 54: gzis.close unless gzis.nil? 55: end 56: when 'metadata.gz.sig' 57: meta_sig = entry.read 58: when 'data.tar.gz.sig' 59: data_sig = entry.read 60: when 'data.tar.gz' 61: if security_policy 62: Gem.ensure_ssl_available 63: data_dgst = dgst_algo.digest(entry.read) 64: end 65: end 66: end 67: 68: if security_policy then 69: Gem.ensure_ssl_available 70: 71: # map trust policy from string to actual class (or a serialized YAML 72: # file, if that exists) 73: if String === security_policy then 74: if Gem::Security::Policies.key? security_policy then 75: # load one of the pre-defined security policies 76: security_policy = Gem::Security::Policies[security_policy] 77: elsif File.exist? security_policy then 78: # FIXME: this doesn't work yet 79: security_policy = YAML.load File.read(security_policy) 80: else 81: raise Gem::Exception, "Unknown trust policy '#{security_policy}'" 82: end 83: end 84: 85: if data_sig && data_dgst && meta_sig && meta_dgst then 86: # the user has a trust policy, and we have a signed gem 87: # file, so use the trust policy to verify the gem signature 88: 89: begin 90: security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain) 91: rescue Exception => e 92: raise "Couldn't verify data signature: #{e}" 93: end 94: 95: begin 96: security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain) 97: rescue Exception => e 98: raise "Couldn't verify metadata signature: #{e}" 99: end 100: elsif security_policy.only_signed 101: raise Gem::Exception, "Unsigned gem" 102: else 103: # FIXME: should display warning here (trust policy, but 104: # either unsigned or badly signed gem file) 105: end 106: end 107: 108: @tarreader.rewind 109: @fileops = Gem::FileOperations.new 110: 111: raise Gem::Package::FormatError, "No metadata found!" unless has_meta 112: end
# File lib/rubygems/package/tar_input.rb, line 16 16: def self.open(io, security_policy = nil, &block) 17: is = new io, security_policy 18: 19: yield is 20: ensure 21: is.close if is 22: end
# File lib/rubygems/package/tar_input.rb, line 114 114: def close 115: @io.close 116: @tarreader.close 117: end
# File lib/rubygems/package/tar_input.rb, line 119 119: def each(&block) 120: @tarreader.each do |entry| 121: next unless entry.full_name == "data.tar.gz" 122: is = zipped_stream entry 123: 124: begin 125: Gem::Package::TarReader.new is do |inner| 126: inner.each(&block) 127: end 128: ensure 129: is.close if is 130: end 131: end 132: 133: @tarreader.rewind 134: end
# File lib/rubygems/package/tar_input.rb, line 136 136: def extract_entry(destdir, entry, expected_md5sum = nil) 137: if entry.directory? then 138: dest = File.join destdir, entry.full_name 139: 140: if File.directory? dest then 141: @fileops.chmod entry.header.mode, dest, :verbose => false 142: else 143: @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false 144: end 145: 146: fsync_dir dest 147: fsync_dir File.join(dest, "..") 148: 149: return 150: end 151: 152: # it's a file 153: md5 = Digest::MD5.new if expected_md5sum 154: destdir = File.join destdir, File.dirname(entry.full_name) 155: @fileops.mkdir_p destdir, :mode => 0755, :verbose => false 156: destfile = File.join destdir, File.basename(entry.full_name) 157: @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT 158: 159: open destfile, "wb", entry.header.mode do |os| 160: loop do 161: data = entry.read 4096 162: break unless data 163: # HACK shouldn't we check the MD5 before writing to disk? 164: md5 << data if expected_md5sum 165: os.write(data) 166: end 167: 168: os.fsync 169: end 170: 171: @fileops.chmod entry.header.mode, destfile, :verbose => false 172: fsync_dir File.dirname(destfile) 173: fsync_dir File.join(File.dirname(destfile), "..") 174: 175: if expected_md5sum && expected_md5sum != md5.hexdigest then 176: raise Gem::Package::BadCheckSum 177: end 178: end
Attempt to YAML-load a gemspec from the given io parameter. Return nil if it fails.
# File lib/rubygems/package/tar_input.rb, line 182 182: def load_gemspec(io) 183: Gem::Specification.from_yaml io 184: rescue Gem::Exception 185: nil 186: end
Return an IO stream for the zipped entry.
NOTE: Originally this method used two approaches, Return a GZipReader directly, or read the GZipReader into a string and return a StringIO on the string. The string IO approach was used for versions of ZLib before 1.2.1 to avoid buffer errors on windows machines. Then we found that errors happened with 1.2.1 as well, so we changed the condition. Then we discovered errors occurred with versions as late as 1.2.3. At this point (after some benchmarking to show we weren‘t seriously crippling the unpacking speed) we threw our hands in the air and declared that this method would use the String IO approach on all platforms at all times. And that‘s the way it is.
# File lib/rubygems/package/tar_input.rb, line 202 202: def zipped_stream(entry) 203: if defined? Rubinius or defined? Maglev then 204: # these implementations have working Zlib 205: zis = Zlib::GzipReader.new entry 206: dis = zis.read 207: is = StringIO.new(dis) 208: else 209: # This is Jamis Buck's Zlib workaround for some unknown issue 210: entry.read(10) # skip the gzip header 211: zis = Zlib::Inflate.new(-Zlib::MAX_WBITS) 212: is = StringIO.new(zis.inflate(entry.read)) 213: end 214: ensure 215: zis.finish if zis 216: end