From ed0a5c798f593f1addd0ef1f01e3c34e6797cd0a Mon Sep 17 00:00:00 2001 From: Marian Beermann Date: Tue, 6 Jun 2017 18:03:21 +0200 Subject: [PATCH] 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. --- src/borg/helpers.py | 6 ++++++ src/borg/platform/base.py | 6 ++++-- src/borg/repository.py | 12 ++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/borg/helpers.py b/src/borg/helpers.py index db66b822a..ee3ced59b 100644 --- a/src/borg/helpers.py +++ b/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, diff --git a/src/borg/platform/base.py b/src/borg/platform/base.py index 0d2fb51b8..be4b694e8 100644 --- a/src/borg/platform/base.py +++ b/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)) diff --git a/src/borg/repository.py b/src/borg/repository.py index 6ef75f8c0..2416abbee 100644 --- a/src/borg/repository.py +++ b/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)