Browse Source

platform.SaveFile: truncate_and_unlink temporary

SaveFile is typically used for small files where this is not
necessary. The sole exception is the files cache.
Marian Beermann 8 years ago
parent
commit
ed0a5c798f
3 changed files with 14 additions and 10 deletions
  1. 6 0
      src/borg/helpers.py
  2. 4 2
      src/borg/platform/base.py
  3. 4 8
      src/borg/repository.py

+ 6 - 0
src/borg/helpers.py

@@ -1995,6 +1995,12 @@ def secure_erase(path):
     os.unlink(path)
 
 
+def truncate_and_unlink(path):
+    with open(path, 'r+b') as fd:
+        fd.truncate()
+    os.unlink(path)
+
+
 def popen_with_error_handling(cmd_line: str, log_prefix='', **kwargs):
     """
     Handle typical errors raised by subprocess.Popen. Return None if an error occurred,

+ 4 - 2
src/borg/platform/base.py

@@ -1,6 +1,8 @@
 import errno
 import os
 
+from borg.helpers import truncate_and_unlink
+
 """
 platform base module
 ====================
@@ -157,7 +159,7 @@ class SaveFile:
     def __enter__(self):
         from .. import platform
         try:
-            os.unlink(self.tmppath)
+            truncate_and_unlink(self.tmppath)
         except FileNotFoundError:
             pass
         self.fd = platform.SyncFile(self.tmppath, self.binary)
@@ -167,7 +169,7 @@ class SaveFile:
         from .. import platform
         self.fd.close()
         if exc_type is not None:
-            os.unlink(self.tmppath)
+            truncate_and_unlink(self.tmppath)
             return
         os.replace(self.tmppath, self.path)
         platform.sync_dir(os.path.dirname(self.path))

+ 4 - 8
src/borg/repository.py

@@ -18,7 +18,7 @@ from .helpers import Location
 from .helpers import ProgressIndicatorPercent
 from .helpers import bin_to_hex
 from .helpers import hostname_is_unique
-from .helpers import secure_erase
+from .helpers import secure_erase, truncate_and_unlink
 from .locking import Lock, LockError, LockErrorT
 from .logger import create_logger
 from .lrucache import LRUCache
@@ -1159,9 +1159,7 @@ class LoggedIO:
         for segment, filename in self.segment_iterator(reverse=True):
             if segment > transaction_id:
                 # Truncate segment files before unlink(). This can help a full file system recover.
-                # We can use 'wb', since the segment must exist (just returned by the segment_iterator).
-                open(filename, 'wb').close()
-                os.unlink(filename)
+                truncate_and_unlink(filename)
             else:
                 break
 
@@ -1241,9 +1239,7 @@ class LoggedIO:
             # In this instance (cf. cleanup()) we need to use r+b (=O_RDWR|O_BINARY) and
             # issue an explicit truncate() to avoid creating a file
             # if *segment* did not exist in the first place.
-            with open(filename, 'r+b') as fd:
-                fd.truncate()
-            os.unlink(filename)
+            truncate_and_unlink(filename)
         except FileNotFoundError:
             pass
 
@@ -1297,7 +1293,7 @@ class LoggedIO:
         if segment in self.fds:
             del self.fds[segment]
         with open(filename, 'rb') as fd:
-            # XXX: Rather use mmap.
+            # XXX: Rather use mmap, this loads the entire segment (up to 500 MB by default) into memory.
             data = memoryview(fd.read())
         os.rename(filename, filename + '.beforerecover')
         logger.info('attempting to recover ' + filename)