mirror of https://github.com/borgbackup/borg.git
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.
This commit is contained in:
parent
95064cd241
commit
ed0a5c798f
|
@ -1995,6 +1995,12 @@ def secure_erase(path):
|
||||||
os.unlink(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):
|
def popen_with_error_handling(cmd_line: str, log_prefix='', **kwargs):
|
||||||
"""
|
"""
|
||||||
Handle typical errors raised by subprocess.Popen. Return None if an error occurred,
|
Handle typical errors raised by subprocess.Popen. Return None if an error occurred,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from borg.helpers import truncate_and_unlink
|
||||||
|
|
||||||
"""
|
"""
|
||||||
platform base module
|
platform base module
|
||||||
====================
|
====================
|
||||||
|
@ -157,7 +159,7 @@ class SaveFile:
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
from .. import platform
|
from .. import platform
|
||||||
try:
|
try:
|
||||||
os.unlink(self.tmppath)
|
truncate_and_unlink(self.tmppath)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
self.fd = platform.SyncFile(self.tmppath, self.binary)
|
self.fd = platform.SyncFile(self.tmppath, self.binary)
|
||||||
|
@ -167,7 +169,7 @@ class SaveFile:
|
||||||
from .. import platform
|
from .. import platform
|
||||||
self.fd.close()
|
self.fd.close()
|
||||||
if exc_type is not None:
|
if exc_type is not None:
|
||||||
os.unlink(self.tmppath)
|
truncate_and_unlink(self.tmppath)
|
||||||
return
|
return
|
||||||
os.replace(self.tmppath, self.path)
|
os.replace(self.tmppath, self.path)
|
||||||
platform.sync_dir(os.path.dirname(self.path))
|
platform.sync_dir(os.path.dirname(self.path))
|
||||||
|
|
|
@ -18,7 +18,7 @@ from .helpers import Location
|
||||||
from .helpers import ProgressIndicatorPercent
|
from .helpers import ProgressIndicatorPercent
|
||||||
from .helpers import bin_to_hex
|
from .helpers import bin_to_hex
|
||||||
from .helpers import hostname_is_unique
|
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 .locking import Lock, LockError, LockErrorT
|
||||||
from .logger import create_logger
|
from .logger import create_logger
|
||||||
from .lrucache import LRUCache
|
from .lrucache import LRUCache
|
||||||
|
@ -1159,9 +1159,7 @@ class LoggedIO:
|
||||||
for segment, filename in self.segment_iterator(reverse=True):
|
for segment, filename in self.segment_iterator(reverse=True):
|
||||||
if segment > transaction_id:
|
if segment > transaction_id:
|
||||||
# Truncate segment files before unlink(). This can help a full file system recover.
|
# 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).
|
truncate_and_unlink(filename)
|
||||||
open(filename, 'wb').close()
|
|
||||||
os.unlink(filename)
|
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -1241,9 +1239,7 @@ class LoggedIO:
|
||||||
# In this instance (cf. cleanup()) we need to use r+b (=O_RDWR|O_BINARY) and
|
# 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
|
# issue an explicit truncate() to avoid creating a file
|
||||||
# if *segment* did not exist in the first place.
|
# if *segment* did not exist in the first place.
|
||||||
with open(filename, 'r+b') as fd:
|
truncate_and_unlink(filename)
|
||||||
fd.truncate()
|
|
||||||
os.unlink(filename)
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1297,7 +1293,7 @@ class LoggedIO:
|
||||||
if segment in self.fds:
|
if segment in self.fds:
|
||||||
del self.fds[segment]
|
del self.fds[segment]
|
||||||
with open(filename, 'rb') as fd:
|
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())
|
data = memoryview(fd.read())
|
||||||
os.rename(filename, filename + '.beforerecover')
|
os.rename(filename, filename + '.beforerecover')
|
||||||
logger.info('attempting to recover ' + filename)
|
logger.info('attempting to recover ' + filename)
|
||||||
|
|
Loading…
Reference in New Issue