immutable: fix test for truncated reads of URI extension block size
authorZooko O'Whielacronx <zooko@zooko.com>
Sat, 3 Jan 2009 18:44:27 +0000 (11:44 -0700)
committerZooko O'Whielacronx <zooko@zooko.com>
Sat, 3 Jan 2009 18:44:27 +0000 (11:44 -0700)
src/allmydata/immutable/layout.py
src/allmydata/test/test_immutable.py

index 1f5de5ab0db3d360570892d0f192595f2e1f54fd..aec80b38403047fe11ad65a475b0bf5e93899eea 100644 (file)
@@ -3,7 +3,7 @@ from zope.interface import implements
 from twisted.internet import defer
 from allmydata.interfaces import IStorageBucketWriter, IStorageBucketReader, \
      FileTooLargeError, HASH_SIZE
-from allmydata.util import mathutil, idlib
+from allmydata.util import log, mathutil, idlib
 from allmydata.util.assertutil import precondition
 from allmydata import storage
 
index dd4da45122d89e8400b8c9c0f8ab8dea1c979a85..82def7d746bd69d010910f4d4cf0804006a5d2b4 100644 (file)
@@ -63,9 +63,9 @@ def _corrupt_segment_size(data):
     sharevernum = struct.unpack(">l", data[0x0c:0x0c+4])[0]
     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
     if sharevernum == 1:
-        return corrupt_field(data, 0x0c+0x04, 4, debug=True)
+        return corrupt_field(data, 0x0c+0x04, 4, debug=False)
     else:
-        return corrupt_field(data, 0x0c+0x04, 8, debug=True)
+        return corrupt_field(data, 0x0c+0x04, 8, debug=False)
 
 def _corrupt_size_of_sharedata(data):
     """ Scramble the file data -- the field showing the size of the data within the share
@@ -94,9 +94,9 @@ def _corrupt_offset_of_ciphertext_hash_tree(data):
     sharevernum = struct.unpack(">l", data[0x0c:0x0c+4])[0]
     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
     if sharevernum == 1:
-        return corrupt_field(data, 0x0c+0x14, 4, debug=True)
+        return corrupt_field(data, 0x0c+0x14, 4, debug=False)
     else:
-        return corrupt_field(data, 0x0c+0x24, 8, debug=True)
+        return corrupt_field(data, 0x0c+0x24, 8, debug=False)
 
 def _corrupt_offset_of_block_hashes(data):
     """ Scramble the file data -- the field showing the offset of the block hash tree within
@@ -128,6 +128,23 @@ def _corrupt_offset_of_uri_extension(data):
     else:
         return corrupt_field(data, 0x0c+0x3c, 8)
 
+def _corrupt_offset_of_uri_extension_to_force_short_read(data, debug=False):
+    """ Scramble the file data -- the field showing the offset of the uri extension will be set
+    to the size of the file minus 3.  This means when the client tries to read the length field
+    from that location it will get a short read -- the result string will be only 3 bytes long,
+    not the 4 or 8 bytes necessary to do a successful struct.unpack."""
+    sharevernum = struct.unpack(">l", data[0x0c:0x0c+4])[0]
+    assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
+    # The "-0x0c" in here is to skip the server-side header in the share file, which the client doesn't see when seeking and reading.
+    if sharevernum == 1:
+        if debug:
+            log.msg("testing: corrupting offset %d, size %d, changing %d to %d (len(data) == %d)" % (0x2c, 4, struct.unpack(">L", data[0x2c:0x2c+4])[0], len(data)-0x0c-3, len(data)))
+        return data[:0x2c] + struct.pack(">L", len(data)-0x0c-3) + data[0x2c+4:]
+    else:
+        if debug:
+            log.msg("testing: corrupting offset %d, size %d, changing %d to %d (len(data) == %d)" % (0x48, 8, struct.unpack(">Q", data[0x48:0x48+8])[0], len(data)-0x0c-3, len(data)))
+        return data[:0x48] + struct.pack(">Q", len(data)-0x0c-3) + data[0x48+8:]
+
 def _corrupt_share_data(data):
     """ Scramble the file data -- the field containing the share data itself will have one
     bit flipped or else will be changed to a random value. """
@@ -341,12 +358,18 @@ class Test(ShareManglingMixin, unittest.TestCase):
         shares[k] = corruptor_func(shares[k])
         self.replace_shares(shares, storage_index=self.uri.storage_index)
 
+    def _corrupt_all_shares(self, unused, corruptor_func):
+        """ All shares on disk will be corrupted by corruptor_func. """
+        shares = self.find_shares()
+        for k in shares.keys():
+            self._corrupt_a_share(unused, corruptor_func, k[1])
+
     def _corrupt_a_random_share(self, unused, corruptor_func):
         """ Exactly one share on disk will be corrupted by corruptor_func. """
         shares = self.find_shares()
         ks = shares.keys()
         k = random.choice(ks)
-        return self._corrupt_a_share(unused, corruptor_func, k)
+        return self._corrupt_a_share(unused, corruptor_func, k[1])
 
     def test_download(self):
         """ Basic download.  (This functionality is more or less already tested by test code in
@@ -646,6 +669,7 @@ class Test(ShareManglingMixin, unittest.TestCase):
             _corrupt_offset_of_block_hashes,
             _corrupt_offset_of_share_hashes,
             _corrupt_offset_of_uri_extension,
+            _corrupt_offset_of_uri_extension_to_force_short_read,
             _corrupt_share_data,
             _corrupt_crypttext_hash_tree,
             _corrupt_block_hashes,