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