Class Gem::Package::TarInput
In: lib/rubygems/package/tar_input.rb
Parent: Object

Methods

close   each   extract_entry   load_gemspec   new   open   zipped_stream  

Included Modules

Gem::Package::FSyncDir Enumerable

Attributes

metadata  [R] 

Public Class methods

[Source]

     # 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

[Source]

    # 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

Public Instance methods

[Source]

     # File lib/rubygems/package/tar_input.rb, line 121
121:   def close
122:     @io.close
123:     @tarreader.close
124:   end

[Source]

     # 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

[Source]

     # 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.

[Source]

     # 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.

[Source]

     # 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

[Validate]