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 23 23: def initialize(io, security_policy = nil) 24: @io = io 25: @tarreader = Gem::Package::TarReader.new @io 26: has_meta = false 27: 28: data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil 29: dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil 30: 31: @tarreader.each do |entry| 32: case entry.full_name 33: when "metadata" 34: @metadata = load_gemspec entry.read 35: has_meta = true 36: when "metadata.gz" 37: begin 38: # if we have a security_policy, then pre-read the metadata file 39: # and calculate it's digest 40: sio = nil 41: if security_policy 42: Gem.ensure_ssl_available 43: sio = StringIO.new(entry.read) 44: meta_dgst = dgst_algo.digest(sio.string) 45: sio.rewind 46: end 47: 48: gzis = Zlib::GzipReader.new(sio || entry) 49: # YAML wants an instance of IO 50: @metadata = load_gemspec(gzis) 51: has_meta = true 52: ensure 53: gzis.close unless gzis.nil? 54: end 55: when 'metadata.gz.sig' 56: meta_sig = entry.read 57: when 'data.tar.gz.sig' 58: data_sig = entry.read 59: when 'data.tar.gz' 60: if security_policy 61: Gem.ensure_ssl_available 62: data_dgst = dgst_algo.digest(entry.read) 63: end 64: end 65: end 66: 67: if security_policy then 68: Gem.ensure_ssl_available 69: 70: # map trust policy from string to actual class (or a serialized YAML 71: # file, if that exists) 72: if String === security_policy then 73: if Gem::Security::Policies.key? security_policy then 74: # load one of the pre-defined security policies 75: security_policy = Gem::Security::Policies[security_policy] 76: elsif File.exist? security_policy then 77: # FIXME: this doesn't work yet 78: security_policy = YAML.load File.read(security_policy) 79: else 80: raise Gem::Exception, "Unknown trust policy '#{security_policy}'" 81: end 82: end 83: 84: if data_sig && data_dgst && meta_sig && meta_dgst then 85: # the user has a trust policy, and we have a signed gem 86: # file, so use the trust policy to verify the gem signature 87: 88: begin 89: security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain) 90: rescue Exception => e 91: raise "Couldn't verify data signature: #{e}" 92: end 93: 94: begin 95: security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain) 96: rescue Exception => e 97: raise "Couldn't verify metadata signature: #{e}" 98: end 99: elsif security_policy.only_signed 100: raise Gem::Exception, "Unsigned gem" 101: else 102: # FIXME: should display warning here (trust policy, but 103: # either unsigned or badly signed gem file) 104: end 105: end 106: 107: @tarreader.rewind 108: @fileops = Gem::FileOperations.new 109: 110: raise Gem::Package::FormatError, "No metadata found!" unless has_meta 111: end
# File lib/rubygems/package/tar_input.rb, line 15 15: def self.open(io, security_policy = nil, &block) 16: is = new io, security_policy 17: 18: yield is 19: ensure 20: is.close if is 21: end
# File lib/rubygems/package/tar_input.rb, line 113 113: def close 114: @io.close 115: @tarreader.close 116: end
# File lib/rubygems/package/tar_input.rb, line 118 118: def each(&block) 119: @tarreader.each do |entry| 120: next unless entry.full_name == "data.tar.gz" 121: is = zipped_stream entry 122: 123: begin 124: Gem::Package::TarReader.new is do |inner| 125: inner.each(&block) 126: end 127: ensure 128: is.close if is 129: end 130: end 131: 132: @tarreader.rewind 133: end
# File lib/rubygems/package/tar_input.rb, line 135 135: def extract_entry(destdir, entry, expected_md5sum = nil) 136: if entry.directory? then 137: dest = File.join destdir, entry.full_name 138: 139: if File.directory? dest then 140: @fileops.chmod entry.header.mode, dest, :verbose => false 141: else 142: @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false 143: end 144: 145: fsync_dir dest 146: fsync_dir File.join(dest, "..") 147: 148: return 149: end 150: 151: # it's a file 152: md5 = Digest::MD5.new if expected_md5sum 153: destdir = File.join destdir, File.dirname(entry.full_name) 154: @fileops.mkdir_p destdir, :mode => 0755, :verbose => false 155: destfile = File.join destdir, File.basename(entry.full_name) 156: @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT 157: 158: open destfile, "wb", entry.header.mode do |os| 159: loop do 160: data = entry.read 4096 161: break unless data 162: # HACK shouldn't we check the MD5 before writing to disk? 163: md5 << data if expected_md5sum 164: os.write(data) 165: end 166: 167: os.fsync 168: end 169: 170: @fileops.chmod entry.header.mode, destfile, :verbose => false 171: fsync_dir File.dirname(destfile) 172: fsync_dir File.join(File.dirname(destfile), "..") 173: 174: if expected_md5sum && expected_md5sum != md5.hexdigest then 175: raise Gem::Package::BadCheckSum 176: end 177: 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 181 181: def load_gemspec(io) 182: Gem::Specification.from_yaml io 183: rescue Gem::Exception 184: nil 185: 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 201 201: def zipped_stream(entry) 202: if defined? Rubinius then 203: zis = Zlib::GzipReader.new entry 204: dis = zis.read 205: is = StringIO.new(dis) 206: else 207: # This is Jamis Buck's Zlib workaround for some unknown issue 208: entry.read(10) # skip the gzip header 209: zis = Zlib::Inflate.new(-Zlib::MAX_WBITS) 210: is = StringIO.new(zis.inflate(entry.read)) 211: end 212: ensure 213: zis.finish if zis 214: end