Commit 2a65e10b authored by Tim Peters's avatar Tim Peters

Forward port from ZODB 3.2.

Collector 1581:  fspack can blow up when .fs is corrupted

Repaired three places where fspack referenced an undefined global while
*trying* to raise CorruptedError.  Added new checkCorruptionInPack()
test to verify the correct exception gets raised.
parent c61bc53b
...@@ -28,6 +28,15 @@ trying to determine the object's class path. This was confusing. The code ...@@ -28,6 +28,15 @@ trying to determine the object's class path. This was confusing. The code
has been changed to obtain the class path from the object's pickle, without has been changed to obtain the class path from the object's pickle, without
trying to import application modules or classes. trying to import application modules or classes.
FileStorage
-----------
Collector 1581: When an attempt to pack a corrupted ``Data.fs`` file was
made, it was possible for the pack routine to die with a reference to an
undefined global while it was trying to raise ``CorruptedError``. It
raises ``CorruptedError``, as it always intended, in these cases now.
Install Install
------- -------
......
...@@ -264,7 +264,7 @@ class GC(FileStorageFormatter): ...@@ -264,7 +264,7 @@ class GC(FileStorageFormatter):
if tlen != th.tlen: if tlen != th.tlen:
self.fail(pos, "redundant transaction length does not " self.fail(pos, "redundant transaction length does not "
"match initial transaction length: %d != %d", "match initial transaction length: %d != %d",
u64(s), th.tlen) tlen, th.tlen)
pos += 8 pos += 8
self.packpos = pos self.packpos = pos
...@@ -359,7 +359,7 @@ class GC(FileStorageFormatter): ...@@ -359,7 +359,7 @@ class GC(FileStorageFormatter):
if tlen != th.tlen: if tlen != th.tlen:
self.fail(pos, "redundant transaction length does not " self.fail(pos, "redundant transaction length does not "
"match initial transaction length: %d != %d", "match initial transaction length: %d != %d",
u64(s), th.tlen) tlen, th.tlen)
pos += 8 pos += 8
for pos in extra_roots: for pos in extra_roots:
...@@ -553,7 +553,7 @@ class FileStoragePacker(FileStorageFormatter): ...@@ -553,7 +553,7 @@ class FileStoragePacker(FileStorageFormatter):
if tlen != th.tlen: if tlen != th.tlen:
self.fail(pos, "redundant transaction length does not " self.fail(pos, "redundant transaction length does not "
"match initial transaction length: %d != %d", "match initial transaction length: %d != %d",
u64(s), th.tlen) tlen, th.tlen)
pos += 8 pos += 8
return pos, new_pos return pos, new_pos
......
...@@ -167,6 +167,55 @@ class FileStorageTests( ...@@ -167,6 +167,55 @@ class FileStorageTests(
self.failUnless(self._storage._records_before_save > 20) self.failUnless(self._storage._records_before_save > 20)
def checkCorruptionInPack(self):
# This sets up a corrupt .fs file, with a redundant transaction
# length mismatch. The implementation of pack in many releases of
# ZODB blew up if the .fs file had such damage: it detected the
# damage, but the code to raise CorruptedError referenced an undefined
# global.
import time
from ZODB.DB import DB
from ZODB.utils import U64, p64
from ZODB.FileStorage.format import CorruptedError
db = DB(self._storage)
conn = db.open()
conn.root()['xyz'] = 1
get_transaction().commit()
# Ensure it's all on disk.
db.close()
self._storage.close()
# Reopen before damaging.
self.open()
# Open .fs directly, and damage content.
f = open('FileStorageTests.fs', 'r+b')
f.seek(0, 2)
pos2 = f.tell() - 8
f.seek(pos2)
tlen2 = U64(f.read(8)) # length-8 of the last transaction
pos1 = pos2 - tlen2 + 8 # skip over the tid at the start
f.seek(pos1)
tlen1 = U64(f.read(8)) # should be redundant length-8
self.assertEqual(tlen1, tlen2) # verify that it is redundant
# Now damage the second copy.
f.seek(pos2)
f.write(p64(tlen2 - 1))
f.close()
# Try to pack. This used to yield
# NameError: global name 's' is not defined
try:
self._storage.pack(time.time(), None)
except CorruptedError, detail:
self.assert_("redundant transaction length does not match "
"initial transaction length" in str(detail))
else:
self.fail("expected CorruptedError")
class FileStorageRecoveryTest( class FileStorageRecoveryTest(
StorageTestBase.StorageTestBase, StorageTestBase.StorageTestBase,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment