| 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