| 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: # Ruby 1.8 doesn't have encoding and YAML is UTF-8
53: args = [sio || entry]
54: args << { :external_encoding => Encoding::UTF_8 } if
55: Object.const_defined?(:Encoding)
56:
57: gzis = Zlib::GzipReader.new(*args)
58:
59: # YAML wants an instance of IO
60: @metadata = load_gemspec(gzis)
61: has_meta = true
62: ensure
63: gzis.close unless gzis.nil?
64: end
65: when 'metadata.gz.sig'
66: meta_sig = entry.read
67: when 'data.tar.gz.sig'
68: data_sig = entry.read
69: when 'data.tar.gz'
70: if security_policy
71: Gem.ensure_ssl_available
72: data_dgst = dgst_algo.digest(entry.read)
73: end
74: end
75: end
76:
77: if security_policy then
78: Gem.ensure_ssl_available
79:
80: # map trust policy from string to actual class (or a serialized YAML
81: # file, if that exists)
82: if String === security_policy then
83: if Gem::Security::Policies.key? security_policy then
84: # load one of the pre-defined security policies
85: security_policy = Gem::Security::Policies[security_policy]
86: elsif File.exist? security_policy then
87: # FIXME: this doesn't work yet
88: security_policy = YAML.load File.read(security_policy)
89: else
90: raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
91: end
92: end
93:
94: if data_sig && data_dgst && meta_sig && meta_dgst then
95: # the user has a trust policy, and we have a signed gem
96: # file, so use the trust policy to verify the gem signature
97:
98: begin
99: security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
100: rescue Exception => e
101: raise "Couldn't verify data signature: #{e}"
102: end
103:
104: begin
105: security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
106: rescue Exception => e
107: raise "Couldn't verify metadata signature: #{e}"
108: end
109: elsif security_policy.only_signed
110: raise Gem::Exception, "Unsigned gem"
111: else
112: # FIXME: should display warning here (trust policy, but
113: # either unsigned or badly signed gem file)
114: end
115: end
116:
117: @tarreader.rewind
118:
119: unless has_meta then
120: path = io.path if io.respond_to? :path
121: error = Gem::Package::FormatError.new 'no metadata found', path
122: raise error
123: end
124: 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 126
126: def close
127: @io.close
128: @tarreader.close
129: end
# File lib/rubygems/package/tar_input.rb, line 131
131: def each(&block)
132: @tarreader.each do |entry|
133: next unless entry.full_name == "data.tar.gz"
134: is = zipped_stream entry
135:
136: begin
137: Gem::Package::TarReader.new is do |inner|
138: inner.each(&block)
139: end
140: ensure
141: is.close if is
142: end
143: end
144:
145: @tarreader.rewind
146: end
# File lib/rubygems/package/tar_input.rb, line 148
148: def extract_entry(destdir, entry, expected_md5sum = nil)
149: if entry.directory? then
150: dest = File.join destdir, entry.full_name
151:
152: if File.directory? dest then
153: FileUtils.chmod entry.header.mode, dest, :verbose => false
154: else
155: FileUtils.mkdir_p dest, :mode => entry.header.mode, :verbose => false
156: end
157:
158: fsync_dir dest
159: fsync_dir File.join(dest, "..")
160:
161: return
162: end
163:
164: # it's a file
165: md5 = Digest::MD5.new if expected_md5sum
166: destdir = File.join destdir, File.dirname(entry.full_name)
167: FileUtils.mkdir_p destdir, :mode => 0755, :verbose => false
168: destfile = File.join destdir, File.basename(entry.full_name)
169: FileUtils.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
170:
171: open destfile, "wb", entry.header.mode do |os|
172: loop do
173: data = entry.read 4096
174: break unless data
175: # HACK shouldn't we check the MD5 before writing to disk?
176: md5 << data if expected_md5sum
177: os.write(data)
178: end
179:
180: os.fsync
181: end
182:
183: FileUtils.chmod entry.header.mode, destfile, :verbose => false
184: fsync_dir File.dirname(destfile)
185: fsync_dir File.join(File.dirname(destfile), "..")
186:
187: if expected_md5sum && expected_md5sum != md5.hexdigest then
188: raise Gem::Package::BadCheckSum
189: end
190: 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 194
194: def load_gemspec(io)
195: Gem::Specification.from_yaml io
196: rescue Gem::Exception
197: nil
198: 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.
Revisited. Here‘s the beginning of the long story. osdir.com/ml/lang.ruby.gems.devel/2007-06/msg00045.html
StringIO wraping has never worked as a workaround by definition. Skipping initial 10 bytes and passing -MAX_WBITS to Zlib::Inflate luckily works as gzip reader, but it only works if the GZip header is 10 bytes long (see below) and it does not check inflated stream consistency (CRC value in the Gzip trailer.)
RubyGems generated Gzip Header: 10 bytes
magic(2) + method(1) + flag(1) + mtime(4) + exflag(1) + os(1) +
orig_name(0) + comment(0)
Ideally, it must return a GZipReader without meaningless buffering. We have lots of CRuby committers around so let‘s fix windows build when we received an error.
# File lib/rubygems/package/tar_input.rb, line 230
230: def zipped_stream(entry)
231: Zlib::GzipReader.new entry
232: end