~abentley/simplestreams/fix-read-signed-no-check

« back to all changes in this revision

Viewing changes to simplestreams/contentsource.py

  • Committer: Scott Moser
  • Date: 2015-09-22 20:28:53 UTC
  • mfrom: (398.1.21 trunk.lp1487004)
  • Revision ID: smoser@ubuntu.com-20150922202853-hxqmj6bpif5n62v9
provide insert_item with a contentsource that does checksumming

Previously, insert_item would receive a 'contentsource' (essentially
a file like object to be read as a stream).  The user of that object
needed to calculate checksums and verify the data they read.

Now, instead the contentsource will do checksumming as read() 
operations are done, and will raise a checksum error in any failure
case.

Thus to use this, the user now simply has to loop over reads
and catch the exception.

stream data is now expected to have valid checksums and size on
all items with a path.  If the user is using a stream source that
does not have either size or checksum information, they have a few
options:
 a.) [legacy/SRU only] set environment variable
     SS_MISSING_ITEM_CHECKSUM_BEHAVIOR can be set to
        silent: behave exactly as before.  No checksumming is done,
                no warnings are emitted.  The consumer of the
                contentsource must check checksums.
        warn:   log messages at WARN level (same as default/unset)
        fail:   the new behavior. raise an InvalidChecksum exception.

 b.) instantiate the BasicMirrorWriter with config checksumming_reader=False
     the default for that config setting is True, meaning that you
     will get a reader that checksums content as it goes and 
     raises exception on bad data.

 c.) fix their source to have a sha256sum and a size


The 'sstream-mirror' program now has a '--no-checksumming-reader' flag
that does 'b' for this mirror.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
import os
21
21
import sys
22
22
 
 
23
from . import checksum_util
 
24
 
23
25
if sys.version_info > (3, 0):
24
26
    import urllib.parse as urlparse
25
27
else:
239
241
        super(MemoryContentSource, self).__init__(fd=fd, url=url)
240
242
 
241
243
 
 
244
class ChecksummingContentSource(ContentSource):
 
245
    def __init__(self, csrc, checksums, size=None):
 
246
        self.cs = csrc
 
247
        self.bytes_read = 0
 
248
        self.checksummer = None
 
249
        self.size = size
 
250
 
 
251
        try:
 
252
            csummer = checksum_util.SafeCheckSummer(checksums)
 
253
        except ValueError as e:
 
254
            raise checksum_util.invalid_checksum_for_reader(self, msg=str(e))
 
255
 
 
256
        self._set_checksummer(csummer)
 
257
 
 
258
        try:
 
259
            self.size = int(size)
 
260
        except TypeError:
 
261
            self.size = size
 
262
            raise checksum_util.invalid_checksum_for_reader(self)
 
263
 
 
264
    def resume(self, offset, checksummer):
 
265
        self.cs.set_start_pos(offset)
 
266
        self._set_checksummer(checksummer)
 
267
        self.bytes_read = offset
 
268
 
 
269
    @property
 
270
    def algorithm(self):
 
271
        return self.checksummer.algorithm
 
272
 
 
273
    def _set_checksummer(self, checksummer):
 
274
        if checksummer.algorithm not in checksum_util.CHECKSUMS:
 
275
            raise ValueError("algorithm %s is not valid (%s)" %
 
276
                             (checksummer.algorithm, checksum_util.CHECKSUMS))
 
277
        self.checksummer = checksummer
 
278
 
 
279
    def check(self):
 
280
        return self.bytes_read == self.size and self.checksummer.check()
 
281
 
 
282
    def read(self, size=-1):
 
283
        buf = self.cs.read(size)
 
284
        buflen = len(buf)
 
285
        self.checksummer.update(buf)
 
286
        self.bytes_read += buflen
 
287
 
 
288
        # read size was different size than expected.
 
289
        # if its not the end, something wrong
 
290
        if buflen != size and self.size != self.bytes_read:
 
291
            raise checksum_util.invalid_checksum_for_reader(self)
 
292
 
 
293
        if self.bytes_read == self.size and not self.check():
 
294
            raise checksum_util.invalid_checksum_for_reader(self)
 
295
        return buf
 
296
 
 
297
    def open(self):
 
298
        return self.cs.open()
 
299
 
 
300
    def close(self):
 
301
        return self.cs.close()
 
302
 
 
303
    @property
 
304
    def url(self):
 
305
        return self.cs.url
 
306
 
 
307
 
242
308
class UrlReader(object):
243
309
    def read(self, size=-1):
244
310
        raise NotImplementedError()