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