| Class | Gem::Package::TarInput |
| In: |
lib/rubygems/package/tar_input.rb
|
| Parent: | Object |
| metadata | [R] |
# 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
# 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
# File lib/rubygems/package/tar_input.rb, line 115
115: def close
116: @io.close
117: @tarreader.close
118: end
# 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
# 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.
# 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.
# 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