Browse Source

Merge pull request #1454 from ThomasWaldmann/enfore-max-object-size-put

repo: do not put objects that we won't get, fixes #1451
TW 9 years ago
parent
commit
4c51bed665
3 changed files with 32 additions and 5 deletions
  1. 16 4
      borg/repository.py
  2. 8 1
      borg/testsuite/repository.py
  3. 8 0
      docs/changes.rst

+ 16 - 4
borg/repository.py

@@ -712,9 +712,14 @@ class LoggedIO:
             key = None
             key = None
         else:
         else:
             raise TypeError("_read called with unsupported format")
             raise TypeError("_read called with unsupported format")
-        if size > MAX_OBJECT_SIZE or size < fmt.size:
-            raise IntegrityError('Invalid segment entry size [segment {}, offset {}]'.format(
-                segment, offset))
+        if size > MAX_OBJECT_SIZE:
+            # if you get this on an archive made with borg < 1.0.7 and millions of files and
+            # you need to restore it, you can disable this check by using "if False:" above.
+            raise IntegrityError('Invalid segment entry size {} - too big [segment {}, offset {}]'.format(
+                size, segment, offset))
+        if size < fmt.size:
+            raise IntegrityError('Invalid segment entry size {} - too small [segment {}, offset {}]'.format(
+                size, segment, offset))
         length = size - fmt.size
         length = size - fmt.size
         data = fd.read(length)
         data = fd.read(length)
         if len(data) != length:
         if len(data) != length:
@@ -731,8 +736,12 @@ class LoggedIO:
         return size, tag, key, data
         return size, tag, key, data
 
 
     def write_put(self, id, data, raise_full=False):
     def write_put(self, id, data, raise_full=False):
+        data_size = len(data)
+        if data_size > MAX_DATA_SIZE:
+            # this would push the segment entry size beyond MAX_OBJECT_SIZE.
+            raise IntegrityError('More than allowed put data [{} > {}]'.format(data_size, MAX_DATA_SIZE))
         fd = self.get_write_fd(raise_full=raise_full)
         fd = self.get_write_fd(raise_full=raise_full)
-        size = len(data) + self.put_header_fmt.size
+        size = data_size + self.put_header_fmt.size
         offset = self.offset
         offset = self.offset
         header = self.header_no_crc_fmt.pack(size, TAG_PUT)
         header = self.header_no_crc_fmt.pack(size, TAG_PUT)
         crc = self.crc_fmt.pack(crc32(data, crc32(id, crc32(header))) & 0xffffffff)
         crc = self.crc_fmt.pack(crc32(data, crc32(id, crc32(header))) & 0xffffffff)
@@ -771,3 +780,6 @@ class LoggedIO:
             self._write_fd.close()
             self._write_fd.close()
             sync_dir(os.path.dirname(self._write_fd.name))
             sync_dir(os.path.dirname(self._write_fd.name))
             self._write_fd = None
             self._write_fd = None
+
+
+MAX_DATA_SIZE = MAX_OBJECT_SIZE - LoggedIO.put_header_fmt.size

+ 8 - 1
borg/testsuite/repository.py

@@ -8,7 +8,7 @@ from ..hashindex import NSIndex
 from ..helpers import Location, IntegrityError
 from ..helpers import Location, IntegrityError
 from ..locking import Lock, LockFailed
 from ..locking import Lock, LockFailed
 from ..remote import RemoteRepository, InvalidRPCMethod
 from ..remote import RemoteRepository, InvalidRPCMethod
-from ..repository import Repository, LoggedIO, TAG_COMMIT
+from ..repository import Repository, LoggedIO, TAG_COMMIT, MAX_DATA_SIZE
 from . import BaseTestCase
 from . import BaseTestCase
 
 
 
 
@@ -128,6 +128,13 @@ class RepositoryTestCase(RepositoryTestCaseBase):
         self.assert_equal(second_half, all[50:])
         self.assert_equal(second_half, all[50:])
         self.assert_equal(len(self.repository.list(limit=50)), 50)
         self.assert_equal(len(self.repository.list(limit=50)), 50)
 
 
+    def test_max_data_size(self):
+        max_data = b'x' * MAX_DATA_SIZE
+        self.repository.put(b'00000000000000000000000000000000', max_data)
+        self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), max_data)
+        self.assert_raises(IntegrityError,
+                           lambda: self.repository.put(b'00000000000000000000000000000001', max_data + b'x'))
+
 
 
 class RepositoryCommitTestCase(RepositoryTestCaseBase):
 class RepositoryCommitTestCase(RepositoryTestCaseBase):
 
 

+ 8 - 0
docs/changes.rst

@@ -57,6 +57,14 @@ Security fixes:
 
 
 - fix security issue with remote repository access, #1428
 - fix security issue with remote repository access, #1428
 
 
+Bug fixes:
+
+- do not write objects to repository that are bigger than the allowed size,
+  borg will reject reading them, #1451.
+  IMPORTANT: if you created archives with many millions of files or
+             directories, please verify if you can open them successfully,
+             e.g. try a "borg list REPO::ARCHIVE".
+
 
 
 Version 1.0.7rc1 (2016-08-05)
 Version 1.0.7rc1 (2016-08-05)
 -----------------------------
 -----------------------------