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 25
 25:   def initialize(io, security_policy = nil)
 26:     @io = io
 27:     @tarreader = Gem::Package::TarReader.new @io
 28:     has_meta = false
 29: 
 30:     data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
 31:     dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
 32: 
 33:     @tarreader.each do |entry|
 34:       case entry.full_name
 35:       when "metadata"
 36:         @metadata = load_gemspec entry.read
 37:         has_meta = true
 38:       when "metadata.gz"
 39:         begin
 40:           # if we have a security_policy, then pre-read the metadata file
 41:           # and calculate it's digest
 42:           sio = nil
 43:           if security_policy
 44:             Gem.ensure_ssl_available
 45:             sio = StringIO.new(entry.read)
 46:             meta_dgst = dgst_algo.digest(sio.string)
 47:             sio.rewind
 48:           end
 49: 
 50:           gzis = Zlib::GzipReader.new(sio || entry)
 51:           # YAML wants an instance of IO
 52:           @metadata = load_gemspec(gzis)
 53:           has_meta = true
 54:         ensure
 55:           gzis.close unless gzis.nil?
 56:         end
 57:       when 'metadata.gz.sig'
 58:         meta_sig = entry.read
 59:       when 'data.tar.gz.sig'
 60:         data_sig = entry.read
 61:       when 'data.tar.gz'
 62:         if security_policy
 63:           Gem.ensure_ssl_available
 64:           data_dgst = dgst_algo.digest(entry.read)
 65:         end
 66:       end
 67:     end
 68: 
 69:     if security_policy then
 70:       Gem.ensure_ssl_available
 71: 
 72:       # map trust policy from string to actual class (or a serialized YAML
 73:       # file, if that exists)
 74:       if String === security_policy then
 75:         if Gem::Security::Policy.key? security_policy then
 76:           # load one of the pre-defined security policies
 77:           security_policy = Gem::Security::Policy[security_policy]
 78:         elsif File.exist? security_policy then
 79:           # FIXME: this doesn't work yet
 80:           security_policy = YAML.load File.read(security_policy)
 81:         else
 82:           raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
 83:         end
 84:       end
 85: 
 86:       if data_sig && data_dgst && meta_sig && meta_dgst then
 87:         # the user has a trust policy, and we have a signed gem
 88:         # file, so use the trust policy to verify the gem signature
 89: 
 90:         begin
 91:           security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
 92:         rescue Exception => e
 93:           raise "Couldn't verify data signature: #{e}"
 94:         end
 95: 
 96:         begin
 97:           security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
 98:         rescue Exception => e
 99:           raise "Couldn't verify metadata signature: #{e}"
100:         end
101:       elsif security_policy.only_signed
102:         raise Gem::Exception, "Unsigned gem"
103:       else
104:         # FIXME: should display warning here (trust policy, but
105:         # either unsigned or badly signed gem file)
106:       end
107:     end
108: 
109:     @tarreader.rewind
110:     @fileops = Gem::FileOperations.new
111: 
112:     raise Gem::Package::FormatError, "No metadata found!" unless has_meta
113:   end

[Source]

    # File lib/rubygems/package/tar_input.rb, line 17
17:   def self.open(io, security_policy = nil,  &block)
18:     is = new io, security_policy
19: 
20:     yield is
21:   ensure
22:     is.close if is
23:   end

Public Instance methods

[Source]

     # File lib/rubygems/package/tar_input.rb, line 115
115:   def close
116:     @io.close
117:     @tarreader.close
118:   end

[Source]

     # File lib/rubygems/package/tar_input.rb, line 120
120:   def each(&block)
121:     @tarreader.each do |entry|
122:       next unless entry.full_name == "data.tar.gz"
123:       is = zipped_stream entry
124: 
125:       begin
126:         Gem::Package::TarReader.new is do |inner|
127:           inner.each(&block)
128:         end
129:       ensure
130:         is.close if is
131:       end
132:     end
133: 
134:     @tarreader.rewind
135:   end

[Source]

     # File lib/rubygems/package/tar_input.rb, line 137
137:   def extract_entry(destdir, entry, expected_md5sum = nil)
138:     if entry.directory? then
139:       dest = File.join(destdir, entry.full_name)
140: 
141:       if File.dir? dest then
142:         @fileops.chmod entry.header.mode, dest, :verbose=>false
143:       else
144:         @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false
145:       end
146: 
147:       fsync_dir dest
148:       fsync_dir File.join(dest, "..")
149: 
150:       return
151:     end
152: 
153:     # it's a file
154:     md5 = Digest::MD5.new if expected_md5sum
155:     destdir = File.join destdir, File.dirname(entry.full_name)
156:     @fileops.mkdir_p destdir, :mode => 0755, :verbose => false
157:     destfile = File.join destdir, File.basename(entry.full_name)
158:     @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
159: 
160:     open destfile, "wb", entry.header.mode do |os|
161:       loop do
162:         data = entry.read 4096
163:         break unless data
164:         # HACK shouldn't we check the MD5 before writing to disk?
165:         md5 << data if expected_md5sum
166:         os.write(data)
167:       end
168: 
169:       os.fsync
170:     end
171: 
172:     @fileops.chmod entry.header.mode, destfile, :verbose => false
173:     fsync_dir File.dirname(destfile)
174:     fsync_dir File.join(File.dirname(destfile), "..")
175: 
176:     if expected_md5sum && expected_md5sum != md5.hexdigest then
177:       raise Gem::Package::BadCheckSum
178:     end
179:   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 183
183:   def load_gemspec(io)
184:     Gem::Specification.from_yaml io
185:   rescue Gem::Exception
186:     nil
187:   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 203
203:   def zipped_stream(entry)
204:     if defined? Rubinius then
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

[Validate]